Table of contents
1. What is the signal.h header in C?2. Predefined types
3. Predefined constants
4. Signal handling functions in C
4.1. The signal function
4.2. The raise function
5. Additional remarks
The signal.h
header file provides the necessary tools to capture and handle various
signals that may occur during the execution of a C program. This page covers predefined types
and constants, as well as core functions that allow you to specify either a default or custom
handling procedure for different signals.
sig_atomic_t
: represents an atomic type for safely managing a variable that
might be accessed by both the main program and a signal handler. Operations on this type
are guaranteed to be indivisible (atomic).For more information, see the example of a typical use of sig_atomic_t.
Several constants, defined in signal.h
, serve to specify how to handle signals
or indicate various signal types. Below are some notable examples:
Constant | Description |
---|---|
SIG_DFL |
Requests the default handling for a particular signal. |
SIG_IGN |
Indicates that the signal should be ignored. |
SIG_ERR |
Return value from the signal function if an error occurs. |
SIGABRT |
Represents an abnormal termination (possibly triggered by calling abort ). |
SIGFPE |
Indicates an invalid arithmetic operation (division by zero, overflow, etc.). |
SIGILL |
Raised when the processor detects an invalid machine instruction. |
SIGINT |
Interactive signal, often sent by the user (for instance, Ctrl + C ). |
SIGSEGV |
Indicates illegal memory access (segmentation fault). |
SIGTERM |
Sent to request the program to terminate (a “graceful” shutdown). |
signal
function// Prototype
void (*signal(int signum, void (*handler)(int)))(int);
The signal
function associates the behavior handler
with the signal
numbered signum
. That behavior can be:
int
parameter.SIG_IGN
, meaning that the given signal is ignored.SIG_DFL
, applying the default action for that signal.
When the specified function (handler
) returns “normally” (i.e., without calling
exit
or using something like longjmp
), execution resumes from the
interrupted instruction.
Upon success, signal
returns the function previously associated with the given
signal (or SIG_DFL
if no custom handler was set before). If an error occurs,
signal
returns SIG_ERR
.
Example of using signal
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void handlerCtrlC(int sig) {
printf("Signal %d received (Ctrl + C).\n", sig);
// For instance, gracefully terminate the program:
exit(EXIT_SUCCESS);
}
int main(void) {
// Associate the SIGINT signal (Ctrl + C) with our handler
if (signal(SIGINT, handlerCtrlC) == SIG_ERR) {
perror("Error calling signal");
return EXIT_FAILURE;
}
printf("Waiting for Ctrl + C...\n");
while (1) {
// Infinite loop to illustrate signal handling
// Additional code can be placed here
}
return 0;
}
raise
function
// Prototype
int raise(int signum);
The raise
function sends the signal whose number is signum
to
the current process. This is effectively the same as the process sending itself that signal.
If a handler is defined for that signal, it will be executed. Otherwise, the default (or
ignored) action applies.
Example of using raise
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void handler(int sig) {
printf("Signal %d captured (SIGTERM).\n", sig);
// Perform specific actions or terminate
exit(EXIT_SUCCESS);
}
int main(void) {
// Associate SIGTERM with our handler
if (signal(SIGTERM, handler) == SIG_ERR) {
perror("Error calling signal");
return EXIT_FAILURE;
}
printf("Sending SIGTERM to ourselves...\n");
raise(SIGTERM);
printf("This line won't appear if the handler terminates the program.\n");
return 0;
}
The code below illustrates a typical use of sig_atomic_t
.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
/*
* We use a volatile sig_atomic_t so the child’s PID
* can be written inside a signal handler and safely
* read in main.
*/
volatile sig_atomic_t child_pid = 0;
/* SIGCHLD handler: reaps the child and records its PID */
void sigchld_handler(int sig)
{
int saved_errno = errno; /* Save errno in case waitpid alters it */
pid_t pid = waitpid(-1, NULL, 0);
if (pid > 0) {
child_pid = pid; /* Store the PID that was reaped */
}
errno = saved_errno; /* Restore errno */
}
/* SIGINT handler: Could clean up or set flags for graceful shutdown */
void sigint_handler(int sig)
{
printf("\nCaught SIGINT. Preparing to exit...\n");
/*
* Optionally set a flag here to notify main
* that it should exit or clean up.
*/
/* For this demo, we'll just exit immediately: */
exit(EXIT_SUCCESS);
}
int main(void)
{
struct sigaction sa_chld, sa_int;
/* Set up SIGCHLD handler */
sa_chld.sa_handler = sigchld_handler;
sa_chld.sa_flags = 0; /* or SA_RESTART, if desired */
sigemptyset(&sa_chld.sa_mask);
if (sigaction(SIGCHLD, &sa_chld, NULL) == -1) {
perror("sigaction SIGCHLD");
return EXIT_FAILURE;
}
/* Set up SIGINT handler */
sa_int.sa_handler = sigint_handler;
sa_int.sa_flags = 0;
sigemptyset(&sa_int.sa_mask);
if (sigaction(SIGINT, &sa_int, NULL) == -1) {
perror("sigaction SIGINT");
return EXIT_FAILURE;
}
/* Fork a child process */
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return EXIT_FAILURE;
}
else if (pid == 0) {
/* Child process: do some quick work and exit */
printf("Child: PID %d. Doing some work...\n", getpid());
sleep(2);
printf("Child: done. Exiting.\n");
_exit(0);
}
/* Parent process: main loop */
printf("Parent: PID %d. Waiting for child (PID %d) to exit...\n", getpid(), pid);
while (1) {
if (child_pid != 0) {
printf("Parent: detected that child %d terminated.\n", child_pid);
break;
}
/* Do other work or simply sleep/pause to wait for signals */
sleep(1);
}
printf("Parent: Exiting.\n");
return 0;
}
Explanation of the code:
volatile sig_atomic_t child_pid = 0;
volatile sig_atomic_t
because it’s written inside a signal handler (sigchld_handler) and read in the main loop.sigchld_handler()
waitpid(-1, NULL, 0)
to reap any dead child (here, we assume only one child for simplicity).sigint_handler()
sigaction()
, the program forks a child.SIGKILL
and SIGSTOP
) cannot be caught,
ignored, or reassigned. They forcibly stop or pause a process without the program’s
ability to handle them.
Author
Dr. Roger Ianjamasimanana