Introduction to C++ | Signal Handling
Signal Handling in C++
Signal handling in C++ is a method of responding to asynchronous events or signals during program execution. A signal is an interrupt that informs a program that an event has occurred, such as a division by zero, an invalid memory reference, or even a user-defined event. Signals allow you to handle exceptional conditions and perform cleanup or error-handling routines before the program terminates.
What is a Signal?
A signal is a notification sent to a process by the operating system or another process to indicate an event. The event could be a hardware error, software exception, or a user-defined signal.
- Predefined signals: These are signals like SIGINT, SIGTERM, SIGFPE, SIGSEGV, etc.
- User-defined signals: These are signals that can be triggered by the program using
kill()
system calls.
How to Handle Signals?
Signal handling in C++ is typically done using the signal()
function from the signal.h
library. The signal()
function allows you to associate a signal with a custom handler function.
Syntax
signal() Function Syntax
#include <csignal>
void signal_handler(int signal_num) {
// Handle the signal
}
int main() {
signal(SIGINT, signal_handler); // Register signal handler for SIGINT
// Rest of the program
}
Example of Signal Handling
In this example, we will handle the SIGINT signal, which is sent when a user presses Ctrl+C
.
Code Example
#include <iostream>
#include <csignal>
using namespace std;
// Signal handler function
void signal_handler(int signal_num) {
cout << "Signal " << signal_num << " received. Program will exit now!" << endl;
exit(signal_num); // Exit the program after handling the signal
}
int main() {
// Register the signal handler for SIGINT (Ctrl+C)
signal(SIGINT, signal_handler);
cout << "Program is running... Press Ctrl+C to trigger the signal." << endl;
while (true) {
// Infinite loop to keep the program running until SIGINT is received
}
return 0;
}
Output
(Press Ctrl+C)
Signal 2 received. Program will exit now!
Predefined Signals
Here are some common predefined signals that can be handled in C++:
- SIGINT: Triggered when the user presses
Ctrl+C
. - SIGTERM: Used to request program termination.
- SIGSEGV: Triggered when a segmentation fault occurs (invalid memory access).
- SIGFPE: Triggered for floating-point exceptions (e.g., division by zero).
Example of Handling Different Signals
In this example, we will handle multiple signals like SIGSEGV (Segmentation fault) and SIGFPE (Floating-point exception).
Code Example: Handling Multiple Signals
#include <iostream>
#include <csignal>
using namespace std;
// Signal handler function
void signal_handler(int signal_num) {
if (signal_num == SIGSEGV) {
cout << "Segmentation fault occurred! Program will exit." << endl;
} else if (signal_num == SIGFPE) {
cout << "Floating-point exception occurred! Program will exit." << endl;
}
exit(signal_num); // Exit the program after handling the signal
}
int main() {
// Register signal handlers
signal(SIGSEGV, signal_handler);
signal(SIGFPE, signal_handler);
cout << "Program is running... Triggering signals now." << endl;
// Trigger a segmentation fault by dereferencing a null pointer
int* ptr = nullptr;
*ptr = 10;
return 0;
}
Output
Segmentation fault occurred! Program will exit.
Important Points About Signal Handling
- Signal Masking: Signals can be blocked or masked, preventing them from interrupting the program. You can use
sigprocmask()
to block signals. - Default Actions: Each signal has a default action (terminate, ignore, or core dump). The default action can be modified using signal handlers.
- Signal Safety: Avoid performing unsafe operations (like memory allocation or I/O) in signal handlers, as these functions may not be safe to call within a signal handler.
- Asynchronous Nature: Signals are asynchronous events, meaning they can occur at any point during the program’s execution.
Pro Tip:
💡 Pro Tip
Handling signals in C++ can be useful for cleanup tasks, error handling, or even catching user interrupts. However, ensure that your signal handlers are as simple as possible to avoid unpredictable behavior, and do not rely on non-reentrant functions inside signal handlers.