YouTip LogoYouTip

Cpp Signal Handling

# C++ Signal Handling In C++, signals are interrupts delivered by the operating system to a running process. These interrupts can terminate a program prematurely or trigger specific behaviors. Signals can be generated by the system (such as a memory access violation), by user actions (such as pressing `Ctrl+C` in the terminal), or programmatically by the application itself. While some signals cannot be intercepted or ignored, many can be caught and handled. This allows your program to perform cleanup operations, save state, or terminate gracefully. The macros and functions required for signal handling are defined in the `` header file. --- ## Standard C++ Signals The following table lists the standard signals defined in `` that can be caught and handled within a C++ program: | Signal | Description | | :--- | :--- | | **`SIGABRT`** | Abnormal termination of the program, such as when calling the `abort` function. | | **`SIGFPE`** | Erroneous arithmetic operation, such as division by zero or an operation causing an overflow. | | **`SIGILL`** | Detection of an illegal instruction. | | **`SIGINT`** | Interactive attention signal (usually generated by the user pressing `Ctrl+C`). | | **`SIGSEGV`** | Invalid memory access (segmentation fault). | | **`SIGTERM`** | Termination request sent to the program. | --- ## The `signal()` Function The C++ signal-handling library provides the `signal()` function to trap and handle unexpected events or interrupts. ### Syntax The formal prototype of the `signal()` function can look intimidating: ```cpp void (*signal(int sig, void (*func)(int)))(int); ``` To make it easier to understand, you can think of it as: ```cpp signal(registered_signal, signal_handler); ``` ### Parameters and Return Value * **`registered_signal`**: The integer identifier of the signal you want to catch (e.g., `SIGINT`, `SIGTERM`). * **`signal_handler`**: A pointer to the function that will handle the signal. This function must accept a single `int` argument (the signal number) and return `void`. You can also pass special macros: * `SIG_DFL`: Reverts the signal handling to the default operating system behavior. * `SIG_IGN`: Ignores the signal completely. * **Return Value**: The function returns a pointer to the previously defined signal handler. If no handler was previously set, it returns `SIG_DFL` or `SIG_IGN`. --- ## Code Examples ### 1. Catching an External Signal (`SIGINT`) The following example demonstrates how to register a signal handler for `SIGINT` (triggered by pressing `Ctrl+C` in your terminal). ```cpp #include #include #include // On POSIX systems (Linux/macOS), provides sleep() // On Windows, you can use and Sleep() #ifdef _WIN32 #include #define sleep(x) Sleep((x) * 1000) #else #include #endif using namespace std; // Define the signal handler function void signalHandler(int signum) { cout << "\nInterrupt signal (" << signum << ") received." << endl; // Perform cleanup operations here (e.g., closing files, releasing resources) cout << "Cleaning up and exiting..." << endl; // Terminate the program exit(signum); } int main() { // Register the SIGINT signal and associate it with signalHandler signal(SIGINT, signalHandler); cout << "Program running. Press Ctrl+C to interrupt..." << endl; while (true) { cout << "Going to sleep..." << endl; sleep(1); } return 0; } ``` #### Expected Output: If you run the program and press `Ctrl+C` after a few seconds, the output will look like this: ```text Program running. Press Ctrl+C to interrupt... Going to sleep... Going to sleep... Going to sleep... ^C Interrupt signal (2) received. Cleaning up and exiting... ``` --- ### 2. Raising Signals Programmatically with `raise()` You can manually trigger a signal from within your program using the `raise()` function. ### Syntax ```cpp int raise(int sig); ``` Here, `sig` is the signal number you want to send to the current process. ### Example The following program uses `raise()` to trigger a `SIGINT` signal automatically after looping three times: ```cpp #include #include #include #ifdef _WIN32 #include #define sleep(x) Sleep((x) * 1000) #else #include #endif using namespace std; // Define the signal handler function void signalHandler(int signum) { cout << "\nInterrupt signal (" << signum << ") received." << endl; // Perform cleanup and exit exit(signum); } int main() { int i = 0; // Register the SIGINT signal and associate it with signalHandler signal(SIGINT, signalHandler); while (++i) { cout << "Going to sleep..." << endl; // Programmatically trigger SIGINT when i reaches 3 if (i == 3) { cout << "Raising SIGINT programmatically..." << endl; raise(SIGINT); } sleep(1); } return 0; } ``` #### Expected Output: ```text Going to sleep... Going to sleep... Going to sleep... Raising SIGINT programmatically... Interrupt signal (2) received. ``` --- ## Important Considerations & Best Practices When working with signal handlers in production C++ applications, keep the following safety rules in mind: 1. **Asynchronous Signal Safety**: Signal handlers run asynchronously and can interrupt your program at any point. Inside a signal handler, you should **only** call lock-free, async-signal-safe functions. 2. **Avoid I/O and Allocation**: Functions like `std::cout`, `printf()`, `malloc()`, or the `new` operator are **not** async-signal-safe. Calling them inside a signal handler can lead to deadlocks or undefined behavior if the program was interrupted while executing those same functions. (Note: The examples above use `std::cout` for demonstration purposes only). 3. **Use `volatile std::sig_atomic_t`**: If you need to share a flag between your main program loop and the signal handler, declare it as `volatile std::sig_atomic_t`. This guarantees that reads and writes to the variable are atomic and cannot be interrupted. ```cpp #include volatile std::sig_atomic_t keep_running = 1; void handle_sig(int) { keep_running = 0; // Safe } ``` 4. **Platform Differences**: Signal handling behavior can vary significantly between POSIX systems (Linux, macOS) and Windows. For robust, production-grade Unix applications, consider using the POSIX-specific `sigaction()` function instead of `signal()`, as it offers more reliable and configurable signal-handling behavior.
← Lua Garbage CollectionCpp Dynamic Memory β†’