POSIX Signal Handling POSIX (Portable Operating System Interface) for Unix is the name of a family of related standards specified by the IEEE to define the application programming interface (API), along with shell and utilities interfaces for software compatible with variants of the Unix operating system, although the standard can be used by any operating system (Windows uses the POSIX standard). POSIX is used to provide code portability from one Unix system to another. A signal is a software generated interrupt that is sent to a process when an event happens. Signals can be synchronously generated by an error in an application, such as SIGFPE and SIGSEGV, but most signals are asynchronous. Signals can be posted to a process when the system detects a software event, such as a user entering an interrupt or stop or a kill request from another process. Signals can also be come directly from the OS kernel when a hardware event such as a bus error or an illegal instruction is encountered. The system defines a set of signals that can be posted to a process. Signal delivery is analogous to hardware interrupts in that a signal can be blocked from being delivered in the future. Most signals cause termination of the receiving process if no action is taken by the process in response to the signal. Some signals stop the receiving process and other signals can be ignored. Each signal has a default action which is one of the following: * The signal is discarded after being received * The process is terminated after the signal is received * A core file is written, then the process is terminated Each signal defined by the system falls into one of five classes: * Hardware conditions * Software conditions * Input/output notification * Process control * Resource control Signals provide a way to notify a process IMMEDIATELY that something has happened. Macros are defined in header file for common signals. Some examples: SIGHUP hangup, user has logged out SIGINT CTRL-C, terminate program SIGQUIT CTRL-\, terminate program with core dump SIGILL program has tried to execute an illegal instruction SIGFPE a floating point exception has occurred SIGKILL kill program -- this cannot be masked SIGBUS bus error or attempt to write read-only memory page SIGALRM realtime timer alarm SIGSEGV memory violation error SIGSYS invalid parameter to a system call SIGTERM "polite" request to a program to terminate SIGCLD notification that a child process has exited SIGSTOP suspend process SIGTSTP CTRL-Z, tell process to suspend itself SIGCONT resume suspended process The supported signals is platform specific--you must rely on the /usr/include signal.h header file (or signal_iso.h under solaris) for the definitive list. On sleipnir, see the file /usr/include/asm-x86_64/signal.h for a full list of supported signals. A signal is delivered immediately to a process from the operating system. The signal could have been initiated by another process or by the root user. However, non-privileged users cannot send signals (using "kill") to processes they don't own. All signals have an action which could be one of the following 1. SIG_IGN ignore this signal 2. SIG_DFL default action (which may be system dependent) 3. handler() a custom procedure written to take action only on receiving the signal One should be careful using the default action since in most cases the default action is to kill the program. Most signals (but NOT SIGKILL and SIGSTOP) can be masked, and a signal arriving when its mask bit is set will be held. Warning: there is no queue and ONLY ONE signal of each type can be held. If a signal has been held, when the mask bit is reset the signal will be delivered. This is different from ignoring the signal (and essentially throwing it away). Another warning: two signals arriving very closely spaced in time may be delivered to the process OUT OF ORDER. There are two ways to set up a signal handler, the quick way and the (newer) POSIX way, the latter providing more control. The quick way is to make the system call signal(signal_number, void (*func)(int)); where the macros above can be used; some examples: signal(SIGINT, SIG_IGN); /* ignore CTRL-C */ signal(SIGINT, SIG_DFL); /* restore default action of CTRL-C (kill the program) */ signal(SIGINT, my_hander); /* set custom handler for CTRL-C */ where, for example, the handler might be: void my_handler(int dummy) { fflush(stdout); fprintf(stderr, "** the user hit CTRL-C **"); signal(SIGINT, my_handler); /* re-setting the handler is NOT necessary on POSIX compliant systems but is good insurance on older platforms */ } The function which is to be the signal handler takes an integer parameter and returns nothing (since it was called asynchronously). The parameter is supposed to be the signal number but do NOT count on this with old implementations. For more control, the POSIX way is recommended. Suppose we want to set a realtime POSIX timer and have the handler called (via SIGALRM) when the timer counts down to zero: struct sigaction ALRMaction, OLDaction; ALRMaction.sa_handler = (void (*)())sigALRM_handler; /* set additional signals to be blocked when handler runs: */ sigemptyset(&ALRMaction.sa_mask); sigaddset(&ALRMaction.sa_mask, SIGQUIT); /* set flags to customize action */ ALRMaction.sa_flags = SA_SIGINFO; /* keep track of which timer (realtime) generated this signal */ sigaction(SIGALRM, &ALRMaction, &OLDaction); where, for example, the handler might be: int in_sigALRM; void sigALRM_handler(int signo, siginfo_t *info, void *context) { /* Note that *context is not currently defined by the POSIX standard. If several timers shared this signal we could tell which triggered it by looking at the *info fields. Also, this handler does not block CTRL-C */ in_sigALRM = 1; /* set global status flag */ fflush(stdout); fprintf(stderr, "** timer expired **"); in_sigALRM = 0; } The quick way is essentially the same as doing: sigemptyset(&ALRMaction.sa_mask); /* zero mask */ ALRMaction.sa_flags = 0; /* no additional signals to block (other than this one) */ sigaction(SIGALRM, &ALRMaction, NULL); /* don't save old action */ Signals introduce one major complexity into programming. There may be times where we have a CRITICAL section of code where we DON'T want to be interrupted by a signal until we are done. For example, we might want to update a global data structure (which might involve setting pointers and might take several operations). We can "protect" this section of code by setting a temporary signal mask with the system call sigprocmask() and using the system call sigsuspend() as follows: int SIG_lock = 0; sigset_t blockmask, zeromask, oldmask; /* signal masks */ sigemptyset(&blockmask); /* initialize signal masks */ sigemptyset(&zeromask); sigemptyset(&oldmask); sigaddset(&blockmask, SIGALRM); /* signal to block temporarily */ /* save current mask and start critical region.. */ sigprocmask(SIG_BLOCK, &blockmask, &oldmask); SIG_lock = 1; /* .. update global data structure .. */ /* wait for SIGALRM to arrive and reset SIG_lock to zero. The next two instructions are done as one atomic action since when sigsuspend() is called it waits for any signal (which may or may not be the correct one) and on arrival it temporarily restores the previous (blockmask) mask. then the while is checked to make sure, otherwise we return to sigsuspend() */ while (SIG_lock == 1) sigsuspend(&zeromask); /* .. end critical region and restore mask */ sigprocmask(SIG_SETMASK, &oldmask, NULL); **************************** * SIGNAL HANDLING IN UNIX * **************************** The ANSI C/POSIX signal handling interface uses the operations: signal, sigemptyset, sigfillset, sigaddset, sigdelset, sigismember The behavior of signal() varies across Unix versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead. ************* * SIGACTION * ************* Handling signals: sigaction() sigwait() sigwaitinfo() sigtimedwait() sigsuspend() sigpending() Delivering signals: kill() raise() sigqueue() Set operations: sigemptyset() sigfillset() sigaddset() sigdelset() sigismember() Other: sigprocmask() sigaltstack() siginterrupt() sigsetjmp() siglongjmp() signal() ----------------------------------------------------------------------------- From inside a signal handler, you must only use library functions and system calls that are async-safe and thus will not produce undefined behavior. The library functions and system calls that are async-safe are: _Exit(), _exit(), abort(), accept(), access(), aio_error(), aio_return(), aio_suspend(), alarm(), bind(), cfgetispeed(), cfgetospeed(), cfsetispeed(), cfsetospeed(), chdir(), chmod(), chown(), clock_gettime(), close(), connect(), creat(), dup(), dup2(), execle(), execve(), fchmod(), fchown(), fcntl(), fdatasync(), fork(), fpathconf(), fstat(), fsync(), ftruncate(), getegid(), geteuid(), getgid(), getgroups(), getpeername(), getpgrp(), getpid(), getppid(), getsockname(), getsockopt(), getuid(), kill(), link(), listen(), lseek(), lstat(), mkdir(), mkfifo(), open(), pathconf(), pause(), pipe(), poll(), posix_trace_event(), pselect(), raise(), read(), readlink(), recv(), recvfrom(), recvmsg(), rename(), rmdir(), select(), sem_post(), send(), sendmsg(), sendto(), setgid(), setpgid(), setsid(), setsockopt(), setuid(), shutdown(), sigaction(), sigaddset(), sigdelset(), sigemptyset(),sigfillset(), sigismember(), sleep(), signal(), sigpause(), sigpending(), sigprocmask(), sigqueue(), sigset(), sigsuspend(), sockatmark(), socket(), socketpair(), stat(), symlink(), sysconf(), tcdrain(), tcflow(), tcflush(), tcgetattr(), tcgetpgrp(), tcsendbreak(), tcsetattr(), tcsetpgrp(), time(), timer_getoverrun(), timer_gettime(), timer_settime(), times(), umask(), uname(), unlink(), utime(), wait(), waitpid(), and write(). Signals: SIGABRT Process abort signal. SIGALRM Alarm clock. SIGFPE Erroneous arithmetic operation. SIGHUP Hangup. SIGILL Illegal instruction. SIGINT Terminal interrupt signal. SIGKILL Kill (cannot be caught or ignored). SIGPIPE Write on a pipe with no one to read it. SIGQUIT Terminal quit signal. SIGSEGV Invalid memory reference. SIGTERM Termination signal. SIGUSR1 User-defined signal 1. SIGUSR2 User-defined signal 2. SIGCHLD Child process terminated or stopped. SIGCONT Continue executing, if stopped. SIGSTOP Stop executing (cannot be caught or ignored). SIGTSTP Terminal stop signal. SIGTTIN Background process attempting read. SIGTTOU Background process attempting write. SIGBUS Bus error. SIGPOLL Pollable event. SIGPROF Profiling timer expired. SIGSYS Bad system call. SIGTRAP Trace/breakpoint trap. SIGURG High bandwidth data is available at a socket. SIGVTALRM Virtual timer expired. SIGXCPU CPU time limit exceeded. SIGXFSZ File size limit exceeded.