UNIX Signals

bulletWhat is a signal?
bulletA signal is a software interrupt.
bulletA signal is asynchronous event that the application must deal with.
bulletAsynchronous means we do not know when it will occur.
bulletThe purpose of signals is to provide a way processes can synchronize with one another.
bulletWe will describe POSIX reliable signals, which standardizes the signal handling in UNIX.
bulletNow we write a program and terminate it with Ctrl-C key, or with a kill command.
bulletSignal concepts
bulletEvery signal has a name and a number.
bulletAll the names start with SIG.
bulletAll the numbers can be found in <signal.h>, or with 'man 7 signal', or check out the table on page 266.
bulletNo signal has the number 0. This is reserved for a null signal.
bulletSignal can be generated in many circumstances.
bulletTyping a special key
bulletHardware exception
bulletkill system call
bulletkill command
bulletOther conditions that a process should be aware of.
bulletFor example, one of the process's children terminates.
bulletWhat a process can do when a signal arrives. There is no way a process can predict when it will receive a signal, since signal is asynchronous.
bulletIgnore the signal.
bulletTwo signals are never ignored -- SIGKILL and SIGSTOP.
bulletCatch the signal.
bulletWrite a signal handling routine and do whatever you want there.
bulletLet the default action apply.
bulletUNIX kernel has a set of default action for different signals.
bulletTerminate with coredump
bulletTerminate
bulletIgnore
bulletPage 266 has a complete list of the default actions.
bulletNow we test the signal processing using kill command.
bulletA list of important signals
bulletSIGABRT
bulletSIGALRM
bulletSIGBUS
bulletSIGCHLD
bulletSIGFPE
bulletSIGINT
bulletSIGKILL
bulletSIGQUIT
bulletSIGSEGV
bulletSIGSTOP
bulletSIGTERM
bulletSIGUSR1
bulletSIGUSR2
bulletSignal API
bulletsignal
bulletAlthough implied by its name, this routine does not send out signal (kill does). It simply install (or more formally, register) a handler routine for a particular signal.
bulletThe signature is complicated. We will use the following "simplification".
bullettypedef void Sigfunc(int);
bulletA Sigfunc is a function that takes a integer as the only argument, and does not return anything.
bulletSigfunc *signal(int, Sigfunc *);
bulletThe function 'signal' takes two arguments.
bulletThe first argument is an integer.
bulletThe second argument is a pointer to a type Sigfunc function we defined earlier.
bulletThe return value is another pointer to a type Sigfunc function.
bulletNow put all these together. The function "signal" install a handler (the second argument) for a particular signal (the second argument), and returns the previous handler as the return value.
bulletThe <signal.h> header file provides three constants for handler.
bulletSIG_DFL
bulletApply the default action.
bulletSIG_IGN
bulletIgnore the signal.
bulletSIG_ERR
bulletError flag for "signal" routine.
bullet Now we test the textbook example sigusr.c.
bulletNote that pause function will suspend a process until a signal arrives.
bulletThe program installs a handler for SIGUSR1 and SIGUSR2, then waits for signals.
bulletThe routine errdump terminates the process with a coredump.
bulletWhen a process 'exec's another, the installed user-defined signal handlers are all reset to default action.
bulletWithout the old program text, how do you expect the user-defined handlers remain working?
bulletWhen a process forks another, the installed user-defined signal handlers remain working.
bulletTerminology and Semantics
bulletGenerated
bulletA signal is generated when the event that causes the signal occurs.
bulletDelivered
bulletA signal is delivered to a process when the action for that signal is taken.
bulletPending
bulletA signal is pending from it is generated until it is delivered.
bulletBlocking
bulletA process can block the delivery of a signal by mean of masks. When a signal is blocked, it remains pending.
bulletA pending signal can be delivered either the process ignores the signal, or unblock the signal.
bulletIf more than one signal is pending, there is no guarantee that which one will be processed first.
bulletIf more than one signal of the same type is pending, it will be treated as a single one or multiple ones?
bulletPOSIX allows both.
bulletkill and raise API
bulletkill
bulletSend a signal to a process.
bulletThe sender can specify the process id to which the signal will go.
bulletpid > 0
bulletSend to the process with given id.
bulletpid = 0
bulletSend to all processes with the same process group id as the sender.
bulletpid < 0
bulletSend to all processes with the given process group id.
bulletPermission to send signal
bulletThe super user can send signals to anyone.
bulletOther users can send to processes with the same effective or real user ids that matches the sender's effective user id.
bullet0 is the null signal, and often is used to determine whether a process is still alive.
bulletraise
bulletSend a signal to oneself.
bulletalarm and pause API
bulletalarm
bulletDeliver a SIGABRT to the given process after a specified number of seconds.
bulletOnly one alarm clock exists. If alarm is called before it is expired, the new setting replaces the old one and the number of seconds till the previous alarm is returned.
bulletpause
bulletStop the process until a signal is received, and the routines returns when the signal handler for the caught signal returns.
bulletCheck out the sleep implementation using alarm (sleep1.c).
bulletThe sleep routine will make the calling process idle for a given number of seconds.
bulletIf the sleeping process is awakened by a signal, the number of unslept seconds should be returned.
bulletThere are some problems with this program.
bulletIf the caller use alarm for other purpose, the value is lost.
bulletThe caller may have installed his own signal handler for SIGALRM, so we need to restore that.
bulletA race condition between the call to alarm and pause. If the SIGALRM happens between them then no one will wake up the process when it is in pause.
bulletWe need signal mask for these problems. See below.
bulletSignal Sets and signal masks
bulletBefore we talk about signal mask we must be able to group the signals that we want to process as a set.
bulletSignal set processing API
bulletsigemptyset
bulletMake a empty set.
bulletsigfillset
bulletMake a signal set that has all the signals.
bulletsigaddset
bulletAdd a signal into a set.
bulletsigdelset
bulletDelete a signal from a set.
bulletsigmember
bulletDetermine whether a signal is in a set or not.
bulletAll these API are implemented with bit string.
bulletSignal mask API
bulletsigprocmask
bulletThis function modifies the signal mask of a porcess.
bulletThere are three operation modes (indicated by the "how" argument).
bulletSIG_BLOCK
bulletBlock the signals in the given signal set.
bulletSIG_UNBLOCK
bulletUnblock the signals in the given signal set.
bulletSIG_SETMASK
bulletSet the mask to a given value.
bulletThis function also outputs the original mask through a pointer.
bulletRefer to the pr_mask routine for an example.
bulletsigpending
bulletThis function returns the set of signals that are blocked (though a pointer).
bulletNow check out the example on page 295 (critical.c).
bulletThe program first installs a signal handler for SIGQUIT.
bulletThen the program constructs signal set with SIGQUIT in it, and uses sigprocmask to block SIGQUIT.
bulletThe program then sleeps for 5 seconds. During this period, no signal is delivered (they are blocked).
bulletThen the program gets the mask and print a message if there is SIGQUIT being blocked.
bulletFinally the mask is restored and as soon as this happens, the signal is delivered
bulletNote that the signal handler is set to the default action within sig_quit.
bulletsigaction
bulletThis function is similar to "signal", but it can access the signal handler without replacing it.
bulletThe arguments are the following.
bulletAn integer to indicate the signal number
bulletAn input sigaction structure to indicate the information that we want to impose.
bulletAn output sigaction structure for the information that we retrieved.
bulletThe interface consists of a special structure called sigaction, which has the following:
bulletThe address of the handler
bulletA signal mask
bulletThis mask will be automatically added when the signal handler is invoked, and removed when the handler finishes.
bulletThe signal that invokes the handler will be automatically added into the mask. As a result this mechanism guarantees that the signal handler will not be interrupted again by the same signal.
bulletNow check out the implementation of "signal" using "sigaction" (page 298).
bulletsigsuspend
bulletThis function sets the mask to a given value and wait for signal interrupt. These two operation are done atomically.
bulletThe purpose of this function (see the example on page 303) is to provide a way to unblock a signal and wait for it in a single step.  This could be used to implement a critical section.
bulletThe signal mask is set to its original value before the sigsuspend call if the handler returns.
bulletTest the textbook example (suspend1.c).
bulletFirst the program constructs a mask containing SIGINT, which is set to be the new mask.
bulletThen the program unblocks and waits for signal.
bulletThe output from pr_mask indicates that in the critical section, SIGINT is blocked.
bulletIn the SIGINT handler, the SIGINT remains blocked, since at the beginning of the handler, the mask is restored.
bulletFinally we restore the old mask.
bulletUse sigsuspend to synchronize processes (page 307).
bulletThe synchronization involves three functions.
bulletTELL_WAIT
bulletTwo signals are used in the synchronization process -- SIGUSR1 and SIGUSR2.
bulletThis function initializes the signal handlers. Note that both signals use the same signal handler.
bulletThe initialization blocks both SIGUSR1 and SIGUSR2, and store the original mask in the variable old_mask.
bulletTELL_PARENT and TELL_CHILD
bulletThese functions send SIGUSR2 and SIGUSR1 to the parent and child respectively.
bulletWAIT_CHILD and WAIT_PARENT
bulletThese functions wait for the signals from child and parent respectively.
bulletThese functions use a spin loop to wait until the global variable is set to non-zero by the interrupt handler.
bulletIf the sender executes first the signal will be blocked until the receiver is ready.
bulletIf the receiver executes first then it will wait until the sender unblocks (with the zero mask). After the handler returns the global variable sigflag must be reset back to 0.  
bulletA correct implementation of sleep (page 318)
bulletNow we are ready to implement sleep.
bulletThe sigaction installs and examines the handler address.
bulletThe sigprocmask manipulates the mask so that we can block the SIGALRM when we want to.
bulletThe sigsuspend unblocks and waits for SIGALRM.
bulletthe race condition in the previous implementation is resolved.
bulletThe semantic of sleep is as follows:
bulletIf the amount of time has elapsed, the function returns 0.
bulletThe alarm function returns 0, just as we want it to.
bulletOtherwise the function returns the number of unslept seconds.
bulletThe alarm function returns the number of seconds till it is supposed to wake up.
bulletAbort function
bulletThe function is similar to alarm, but it sends a SIGABRT to itself.
bulletThis function cannot return even if a user defines a handler that will (required by ANSI C).
bulletThe purpose of abort is to cleanup when the process exits.
bulletNow examine the implementation on page 311.
bulletFirst the program check the current handler for SIGABRT, and replace SIG_IGN with SIG_DFL if the user tries to ignore the SIGABRT.
bulletThen the program checks for the mask, and unblock SIGABRT if the mask does block it.
bulletSend a SIGABRT to itself.
bulletCheck if the handler returns. If it does, replace the handler with SIGABRT and this makes sure the following SIGABRT will kill the process.
bulletSystem function
bulletPOSIX requires that the system function ignores SIGINT and SIGQUIT, and blocks SIGCHLD.
bulletFirst see the example on page 312.
bulletIn this example, SIGINT and SIGCHLD are handled by the main program, which launches an ed process by system.
bulletWhen the ed child process terminates, it sends a SIGCHLD to the main program.
bulletThis is not acceptable since the main program may be waiting for other children to return -- it cannot tell whether the child is from the system call or from its fork.
bulletWhen we type INT character in ed all the foreground processes receive this signal, including the main program.
bulletThis should not happen since the main program executes ed with system, so it does not want to be bother by the signal.
bulletThe solution is to ignore SIGINT and SIGQUIT from the caller of system.
bulletNow check out the implementation on page 314.
bulletSIGCHLD is blocked.
bulletSIGINT and SIGQUIT are ignored.
bulletAfter forking the child, the handlers for  SIGINT and SIGQUIT, and the mask are restored. Then the child executes the command using sh.
bulletAfter the child (now running sh) returns, everything is restored.
bulletWe change the disposition of these signals to avoid a race condition. If we fork first and then change the disposition, the child running sh may send signals before the parent is prepared.
bulletInterrupted system calls
bulletA signal could be "slow". For example, it could be waiting for I/O to complete (read or write), or waiting for its child process to terminate (wait and waitpid).
bulletWhen a system call is interrupted by a signal, the error number errno is set to EINTR and the system call returns.
bulletTo handle the possible interruption by signals, the programmer needs to code like the case on page 276, which is very tedious and error-prone.
bulletIf the system can restart itself when interrupted by a signal, we will not to do it in our program. This was introduced by BSD 4.2.
bulletThe automatically restarted system calls include ioctl, read, write, writev, wait, and waitpid.
bulletFigure 10.2 indicates the ability to restart system calls by various signal functions.
bulletNow we examine the program on page 298. Here we see that if SA_RESTART is defined, we always restart our system calls.
bulletThe function alarm can be used to limit the time a user can input. See the code on page 289.
bulletIn this code we first set up an alarm of 10 seconds.
bulletIf the user did not do anything, the alarm comes and the read is interrupted.
bulletIf the user does something before the alarm comes, the alarm(0) will turn off the alarm.
bulletNow it is clear that we do not wish to restart a system call when the signal is SIGALARM.
bulletNow go back to page 298, and we see exactly what we just described.
bulletReentrant functions
bulletA reentrant function is one that could call itself in the middle of its own execution.
bulletWhat is NOT a reentrant code.
bulletCode that uses static data structure.
bulletCode that calls malloc
bulletCode that refers to standard I/O library
bulletIn fact all these reasons are the same -- they all use a single global data structure.
bulletWe should not call functions that are not reentrant in a signal handler.
bulletThe reason? We could be interrupted again and go into another handler instance, which will call the non-reentrant functions and cause errors.
bulletWe also should save the errno in a signal handler, since the interrupted process might want to know its value.
bulletNow check the program on page 280.
bulletThe main program execute a infinite loop. During each loop it checks for inconsistent user name.
bulletThe inconsistency will happen when the SIGALARM interrupts the main program between the getpwnam and strcmp, since the handler calls getpwnam with a different user name.