Applying C - Signals |
Written by Harry Fairhead | ||||||||||||||||||||||||||||
Monday, 09 March 2020 | ||||||||||||||||||||||||||||
Page 2 of 3
Controlling SignalsYou can control how a process responds to signals using either the signal function or the sigaction function. Of the two, sigaction is more portable as signal has been implemented in slightly different ways across different versions of Linux, but it is more complicated. The basic form of a sigaction call is: sigaction(signum, &action, &oldaction); signum is the signal number you want to modify. Both action and oldaction are sigaction structs with action specifying the new setting and oldaction being used to save the existing action state. You can use NULL for either action or old action. The simplest form of the sigaction struct is: struct sigaction { void (*sa_handler)(int); sigset_t sa_mask; int sa_flags; }; The first field is a pointer to the signal handler function which accepts a single int which is set to the signal number. It can also be set to SIG_DFL to set the default action or SIG_IGN to ignore the signal. The sa_mask field is the bitwise OR of a set of constants which indicate which signals are to be blocked while the handler is running. The sa_flags field controls how the signal is processed; you can mostly leave this cleared apart perhaps for SA_RESTART. For example, to handle SIG_TERM we first need a function to handle the signal: void signalHandler(int sig) { printf("SIGTERM\n"); } In principle we shouldn't be using printf within a signal handler for the simple reason that the main program has been interrupted and it could be in the middle of a printf call, and in principle even this printf call could be interrupted and so on. As our main program is going to just use a busy wait we can be reasonably sure that there isn't going to be a problem. The main program is: int main(int argc, char** argv) { struct sigaction psa={0}; psa.sa_handler = signalHandler; sigaction(SIGTERM, &psa, NULL); for (;;) {} return (EXIT_SUCCESS); } To make this work we need to include signal.h and in many cases add: #define _POSIX_C_SOURCE 200809L as sigaction is a POSIX standard. The complete program is: #define _POSIX_C_SOURCE 200809L #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> void signalHandler(int sig) { printf("SIGTERM\n"); } int main(int argc, char** argv) { struct sigaction psa={0}; psa.sa_handler = signalHandler; sigaction(SIGTERM, &psa, NULL); for (;;) { } return (EXIT_SUCCESS); } When you compile this you will have to run the program from the command line because there is no easy way to send the SIGTERM signal using an IDE such as NetBeans. You can discover the program’s PID using: ps ax and then you can send it the signal using: sudo kill -SIGTERM 32495 using whatever the PID is for the running program. Each time you send the signal you will see the message printed as the default action is being overridden by the handler. To terminate the program use: sudo kill -SIGKILL 32495 Notice that you don't have to use the other options specified in the struct but they are easy enough to use. Masking SignalsThe mask field is particularly useful because without it your handler is open to being interrupted by another signal. You can block any signal for the duration of the handler, using the appropriate mask, and this can be done using either sigaction or sigprocmask. The only complication is that the mask is a sigset_t and you shouldn't manipulate its bits directly because the implementation could change. Instead you should use the sigset manipulation functions.
As sigaddset and sigdelset change just one bit leaving all the others as they were, you have to initialize the sigset using either sigemptyset or sigfillset. For example, to block all signals while the signal handler is running: sigset_t mask; sigfillset(&mask); struct sigaction psa={0}; psa.sa_handler = signalHandler; psa.sa_mask=mask; sigaction(SIGTERM, &psa, NULL); To block just one signal you would change the initialization of the sigset to: sigset_t mask; sigemptyset(&mask); sigaddset(&mask,SIGTERM); This would block the SIGTERM signal until the handler completed. You can also use a sigset to block or unblock a signal at any time using the sigprocmask functions: sigprocmask(how, &set, &oldset); where how is one of SIG_BLOCK, SIG_UNBLOCK or SIG_SETMASK, The first two block or unblock the signals in set without changing other bits and the final one sets or unsets them all according to set. Notice this works with the current process. If you want to modify the signal mask of a thread use FlagsThe flag field can be used to set the behavior of the signal and handler. It currently accepts the bitwise OR of the following flag constants:
Noteworthy is In chapter but not in this extract
|
||||||||||||||||||||||||||||
Last Updated ( Monday, 09 March 2020 ) |