Signals are notifications sent by the operating system to a process when specific events occur, such as user interrupts, invalid operations, or termination requests. C++ provides signal handling through the <csignal> header, allowing programs to define custom handlers for these events.
- Detects runtime events generated by the operating system.
- Allows custom handling instead of the default behavior.
Example: The following program runs continuously. Pressing Ctrl + C generates the SIGINT signal, which terminates the program.
#include <iostream>
using namespace std;
int main() {
while(1){
cout << "GFG!\n";
}
return 0;
}
Explanation
- The program continuously prints "GFG".
- Pressing Ctrl + C sends the SIGINT signal.
- Since no signal handler is registered, the program terminates.
Syntax
Every signal has a default behavior, but C++ allows programs to replace it with a custom signal handler using the signal() function.
signal(signal_type, signal_handler);
Where:
- signal_type specifies the signal to handle.
- signal_handler is the function executed when the signal occurs.
Common Signals in C++
The following are some commonly used signals available in C++ and POSIX systems.
| Signal | Description | Default Behavior |
|---|---|---|
| SIGABRT | Abnormal termination using abort() | Terminates the program |
| SIGFPE | Arithmetic error (divide by zero, overflow) | Terminates the program |
| SIGILL | Illegal machine instruction | Terminates the program |
| SIGINT | User interrupt (Ctrl + C) | Terminates the program |
| SIGSEGV | Invalid memory access | Terminates the program |
| SIGTERM | Program termination request | Terminates the program |
| SIGKILL | Forcefully kills a process | Cannot be caught or ignored |
| SIGQUIT | Quit signal | Terminates and creates a core dump |
| SIGCHLD | Child process status changed | Ignored by default |
| SIGSTOP | Stops a process | Cannot be caught or ignored |
| SIGSYS | Invalid system call | Terminates the program |
| SIGUSR1 | User-defined signal | Can be handled by the program |
Note: Some signals, such as SIGKILL and SIGSTOP, cannot be caught, blocked, or ignored.
Example: Handling SIGINT
#include <csignal>
#include <iostream>
using namespace std;
// Signal handler function
void signalHandler(int sig) {
cout << "Interrupt handle " << sig << endl;
// Optionally exit the program after handling
exit(sig);
}
int main() {
// Handle signal
signal(SIGINT, signalHandler);
// Loop that waits for the signal
while (true) {
cout << "Geeks\n";
}
return 0;
}
Output
Geeks
Geeks
Geeks
....
Ctrl+C (Enter by user)
Interrupt handle 2
Explanation
- signal() registers signalHandler() for the SIGINT signal.
- When Ctrl + C is pressed, the operating system generates SIGINT.
- Instead of terminating immediately, the registered handler executes first.
Raising Signals Programmatically
Signals can also be generated manually from within a program.
1. Using raise()
The raise() function sends a signal to the current process.
Syntax
raise(signal_type);
It returns:
- 0 on success.
- A non-zero value if the signal could not be raised.
#include <csignal>
#include <iostream>
using namespace std;
void signalHandler(int sig) {
cout << "Signal received: " << sig << endl;
}
int main() {
signal(SIGINT, signalHandler);
raise(SIGINT);
return 0;
}
Output
Signal received: 2
Explanation
- raise(SIGINT) generates the interrupt signal for the current process.
- Since a handler is registered, it executes immediately.
2. Using kill()
The kill() function sends a signal to another process or to the current process using its process ID.
Note: kill() is available only on POSIX-compliant operating systems such as Linux and macOS.
Syntax
kill(process_id, signal_type);
Where:
- process_id specifies the target process.
- signal_type specifies the signal to send.
#include <csignal>
#include <iostream>
#include <unistd.h>
using namespace std;
void signalHandler(int sig) {
cout << "Signal received: " << sig << endl;
}
int main() {
signal(SIGINT, signalHandler);
pid_t pid = getpid();
kill(pid, SIGINT);
return 0;
}
Output
Signal received: 2
Explanation
- getpid() obtains the current process ID.
- kill(pid, SIGINT) sends SIGINT to that process.
- The registered signal handler executes.
Applications of Signal Handling
Signal handling is commonly used in the following situations:
- Gracefully terminating programs before exit.
- Cleaning up allocated resources.
- Responding to keyboard interrupts.
- Implementing user-defined notifications between processes.
Limitations of Signal Handling
Although useful, signal handling has several limitations:
- Some signals (SIGKILL, SIGSTOP) cannot be handled.
- Only async-signal-safe operations should be performed inside signal handlers.
- Signal behavior may differ across operating systems.
- Complex logic inside handlers can lead to unpredictable behavior.
Best Practices
Follow these practices while working with signals:
- Keep signal handlers short and simple.
- Avoid performing memory allocation or I/O-intensive operations inside handlers.
- Use signals mainly for notifications rather than normal program flow.
- Release resources safely before terminating the program.