YouTip LogoYouTip

C Function Sigaction

# C Library Function - sigaction() In C programming, particularly in POSIX-compliant environments like Linux and macOS, managing asynchronous events (signals) is crucial for building robust applications. The `sigaction()` function, defined in the `` header, is used to examine, change, or set the action associated with a specific signal. Compared to the older, legacy `signal()` function, `sigaction()` is highly preferred because it provides significantly better control, predictable behavior across different Unix-like platforms, and enhanced portability. --- ## Syntax ```c #include int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oldact); ``` ### Parameters * **`int sig`**: The signal number to examine or modify. Common signals include: * `SIGINT`: Interactive attention signal (typically triggered by `Ctrl+C`). * `SIGTERM`: Termination request sent to the program. * `SIGQUIT`: Terminal quit signal (typically triggered by `Ctrl+\`). * *Note: Certain signals like `SIGKILL` and `SIGSTOP` cannot be caught, blocked, or ignored.* * **`const struct sigaction *act`**: A pointer to a `sigaction` structure that defines the new action for the signal. If this is `NULL`, the current signal handling action remains unchanged. * **`struct sigaction *oldact`**: A pointer to a `sigaction` structure where the system will store the previous action for the signal. If you do not need to retrieve the old action, you can pass `NULL`. ### Return Value * **`0`**: On success. * **`-1`**: On failure. The global variable `errno` is set to indicate the error (e.g., `EINVAL` if an invalid signal number is specified). --- ## The `struct sigaction` Structure The behavior of the signal is configured using the `struct sigaction` structure, which is defined as follows: ```c struct sigaction { void (*sa_handler)(int); // Pointer to a signal-catching function or SIG_IGN/SIG_DFL void (*sa_sigaction)(int, siginfo_t *, void *); // Pointer to an extended signal-catching function sigset_t sa_mask; // Set of signals to block during execution of the handler int sa_flags; // Special flags to modify signal behavior void (*sa_restorer)(void); // Deprecated/Internal use only (do not use) }; ``` ### Key Fields Explained 1. **`sa_handler`**: Points to your custom signal handler function which accepts a single integer argument (the signal number). You can also set this to: * `SIG_DFL`: Restore the default system action for the signal. * `SIG_IGN`: Ignore the signal. 2. **`sa_sigaction`**: An alternative, advanced signal handler. It provides detailed information about why the signal was generated (via the `siginfo_t` structure) and the execution context. This handler is only invoked if the `SA_SIGINFO` flag is set in `sa_flags`. 3. **`sa_mask`**: Defines a set of signals that should be blocked (masked) while the signal handler is executing. This prevents other signals from interrupting your handler. You should initialize this set using helper functions like `sigemptyset()` or `sigfillset()`. 4. **`sa_flags`**: A bitmask that modifies the behavior of the signal. Common options include: * **`SA_RESTART`**: Automatically restarts certain system calls (like `read()`, `write()`, or `accept()`) if they are interrupted by this signal. This prevents system calls from failing with `EINTR`. * **`SA_SIGINFO`**: Instructs the system to use the three-argument `sa_sigaction` handler instead of the single-argument `sa_handler`. * **`SA_NOCLDSTOP`**: If `sig` is `SIGCHLD`, this flag prevents the parent process from receiving a notification when child processes stop (e.g., via `SIGSTOP`) or resume. --- ## Code Example The following example demonstrates how to use `sigaction()` to gracefully catch the `SIGINT` (`Ctrl+C`) signal. ```c #include #include #include // Custom signal handler function void handle_sigint(int sig) { // Note: printf is technically not async-signal-safe, // but used here for simple demonstration purposes. printf("\nCaught signal %d (SIGINT). Exiting gracefully...\n", sig); } int main() { struct sigaction sa; // 1. Assign the custom handler function sa.sa_handler = handle_sigint; // 2. Clear the signal mask (do not block any other signals during execution) sigemptyset(&sa.sa_mask); // 3. Set flags (0 means no special flags) sa.sa_flags = 0; // 4. Register the signal handler using sigaction if (sigaction(SIGINT, &sa, NULL) == -1) { perror("Error: cannot register sigaction"); return 1; } // Main loop: runs continuously until interrupted printf("Program running. Press Ctrl+C to trigger the signal handler...\n"); while (1) { printf("Running...\n"); sleep(1); } return 0; } ``` ### Expected Output When you compile and run the program, it will print "Running..." every second: ```text Program running. Press Ctrl+C to trigger the signal handler... Running... Running... Running... ``` When you press **Ctrl+C** on your keyboard, the program intercepts the `SIGINT` signal, executes your custom handler, and then resumes or terminates depending on your handler's logic: ```text Running... ^C Caught signal 2 (SIGINT). Exiting gracefully... Running... ``` --- ## Important Considerations * **Async-Signal Safety**: Inside a signal handler, you should only call functions that are **async-signal-safe**. Functions like `printf()`, `malloc()`, and `exit()` are not safe to call inside a signal handler because they are not reentrant and can cause deadlocks. For production code, use low-level system calls like `write()` to output messages, or set a global volatile flag of type `volatile sig_atomic_t` and handle the logic in your main loop. * **Signal Blocking**: Use `sa_mask` to block critical signals while your handler is running to prevent race conditions or nested handler execution. * **Uncatchable Signals**: The signals `SIGKILL` (force kill) and `SIGSTOP` (pause process) cannot be caught, blocked, or ignored. Attempting to register a handler for them using `sigaction()` will result in an error.
← C Function SigpendingC Function Kill β†’