Add Book to My BookshelfPurchase This Book Online

Chapter 10 - Signals

UNIX Systems Programming for SVR4
David A. Curry
 Copyright © 1996 O'Reilly & Associates, Inc.

Advanced Signal Handling
The POSIX standard specifies a substantially more complex mechanism for processing reliable signals. In return for the added complexity, the programmer gains significant new functionality. The POSIX mechanism is based, in large part, on the signal handling functions introduced in 4.2BSD. However, although the concepts and functionality are similar, the functions and their arguments are completely new.
The signal-processing functions introduced up to this point, while not POSIX-compliant, are adequate for most programmers. Unless POSIX-compliance is a requirement, the functions described so far are more desirable, because they allow portability to older systems. However, more and more operating systems are being made POSIX-compliant because of the additional functionality offered by the POSIX interface, and you should be familiar with it.
Signal Sets
Many of the functions in the POSIX signal interface work with signal sets, rather than individual signals. A signal set is simply a bit mask, with one bit for each signal. If the bit is 1, the corresponding signal is in the set; if the bit is 0, the corresponding signal is not in the set. Signal sets are called masks in the 4.2BSD signal interface.
Signal sets are described by the data type sigset_t, defined in the include file signal.h. There are five functions defined for manipulating signal sets:
    #include <signal.h>
    int sigemptyset(sigset_t *set);
    int sigfillset(sigset_t *set);
    int sigaddset(sigset_t *set, int sig);
    int sigdelset(sigset_t *set, int sig);
    int sigismember(sigset_t *set, int sig);
The sigemptyset function initializes the set pointed to by set to exclude all signals defined by the system; that is, it initializes the set to the empty set.
The sigfillset function initializes the set pointed to by set to include all signals defined by the system; that is, it initializes the set to the value “all signals.”
The sigaddset function adds the individual signal identified by sig to the set pointed to by set. The sigdelset function does the opposite; it removes the individual signal identified by sig from the set pointed to by set.
The sigismember function returns 1 if the individual signal identified by sig is a member of the set pointed to by set, or 0 if it is not.
A signal set must be initialized by calling either sigemptyset or sigfillset before it can be used with any of the other functions. Upon successful completion all of the above functions (except sigismember) return 0; otherwise -1 is returned and errno is set to identify the error.
The sigaction Function
The principal workhorse of the POSIX signal mechanism is the sigaction function:
    #include <signal.h>
    int sigaction(int sig, const struct sigaction *act,
            struct sigaction *oact);
The purpose of sigaction is to examine or specify the action to be taken on delivery of a specific signal, identified by the sig parameter. If the act argument is not null, it points to a structure specifying the new action to be taken when delivering sig. If the oact argument is not null, it points to a structure where the action previously associated with sig is to be stored on return from the call to sigaction.
The struct sigaction structure is defined in signal.h and contains at least the following members:
    struct sigaction {
        void        (*sa_handler)(int);
        void        (*sa_sigaction)(int, siginfo_t *, void *);
        sigset_t    sa_mask;
        int         sa_flags;
    };
If the SA_SIGINFO flag in the sa_flags element of the structure is not set, the sa_handler element of the structure specifies the action to be associated with the signal specified in sig. It may take on any of the values SIG_DFL,SIG_IGN, or SIG_HOLD, or it may be the address of a signal handler function. In Solaris 2.x, if the SA_SIGINFO flag is set in sa_flags, then the sa_sigaction element of the structure specifies the signal handling function to be associated with sig. HP-UX 10.x and IRIX 5.x use the sa_handler field in this case, and do not define the sa_sigaction field.
The sa_mask element of the structure specifies a set of signals to be blocked while the signal handler is active; on entry to the signal handler this set of signals is added to the set of signals already being blocked when the signal is delivered. Additionally, the signal that caused the handler to be executed will be blocked, unless the SA_NODEFER flag has been set in sa_flags.
The sa_flags element of the structure specifies a set of flags that can be used to modify the delivery of the signal identified by sig. The value of sa_flags is formed by a logical or of the following values:
SA_ONSTACK
If set and the signal is caught, and the process has defined an alternate signal stack with sigaltstack, the signal is processed on the alternate stack. Otherwise, the signal is processed on the process' main stack.
SA_RESETHAND
If set and the signal is caught, the disposition of the signal is reset to SIG_DFL and the signal is not blocked on entry to the signal handler. This allows the old behavior of unreliable signals to be obtained.
SA_NODEFER
If set and the signal is caught, the signal will not be automatically blocked by the kernel while the signal handler is executing. This flag is not available in HP-UX 10.x.
SA_RESTART
If set and the signal is caught, a system call that is interrupted by the execution of this signal's handler will be restarted by the system when the signal handler returns. Otherwise, the system call will return with errno set to EINTR. This flag is not available in HP-UX 10.x.
SA_NOCLDWAIT
If set and sig is SIGCHLD, the system will not create zombie processes when children of the calling process exit. If the calling process later issues a call to wait, it blocks until all of the calling process' child processes terminate, and then returns -1 with errno set to ECHILD. This flag, in conjunction with SA_NOCLDSTOP, allows the System VSIGCLD behavior to be obtained. This flag is not available in HP-UX 10.x.
SA_NOCLDSTOP
If set and sig is SIGCHLD,SIGCHLD will not be sent to the calling process when its child processes stop or continue. In conjunction with SA_NOCLDWAIT, this flag allows the System VSIGCLD behavior to be obtained.
SA_WAITSIG
If set and sig is SIGWAITING, then the system will send SIGWAITING to the process when all of its lightweight processes are blocked. This flag is not available in HP-UX 10.x.
SA_SIGINFO
If not set and the signal identified by sig is caught, the function identified in sa_handler will be called, with sig as its only argument. If set and the signal is caught, pending signals of type sig will be reliably queued to the calling process, and the function identified in sa_sigaction will be called with three arguments. The first argument is the signal number, sig. The second argument, if non-null, points to a siginfo_t structure containing the reason why the signal was generated. The third argument points to a ucontext_t structure describing the receiving process' context when the signal was delivered. This flag is not available in HP-UX 10.x.
(The only one of these values defined by the POSIX standard is SA_NOCLDSTOP.)
On success, sigaction returns 0. On failure, it returns -1 and sets errno to indicate the error. If sigaction fails, no new signal handler will be installed.
The siginfo_t structure
If a process is catching a signal, it can ask the system to provide information about why it generated that signal. If the process is monitoring its child processes, it can ask the system to tell it why a child process changed state. In either case, this information is provided by means of a siginfo_t structure:
    typedef struct {
        int             si_signo;
        int             si_errno;
        int             si_code;
        union sigval    si_value;
        pid_t           si_pid;
        uid_t           si_uid;
        caddr_t         si_addr;
        int             si_status;
        long            si_band;
    } siginfo_t;
The si_signo element of the structure contains the system-generated signal number; when used with waitid, si_signo is always SIGCHLD.
If si_errno is non-zero, it contains an error number associated with the signal, as defined in the include file errno.h.
The si_code element of the structure contains a code identifying the cause of the signal. If the value of si_code is SI_NOINFO, then only the si_signo element of the structure is meaningful, and the value of all other elements of the structure is undefined.
If the value of si_code is less than or equal to 0, then the signal was generated by a user process (using one of the functions kill, _lwp_kill, sigsend, abort, or raise). If this is the case, then the si_pid element of the structure will contain the process ID of the process that sent the signal, and the si_uid element will contain the user ID of the process that sent the signal. When si_code is less than or equal to 0, it will contain one of the following values:
SI_USER
The signal was sent by one of the functions kill, sigsend, raise, or abort.
SI_LWP
The signal was sent by _lwp_kill, a function used with lightweight processes. This code is available only in Solaris 2.x.
SI_QUEUE
The signal was sent by the sigqueue function, used in real-time programming.
SI_TIMER
The signal was generated by the expiration of a timer set with the timer_settime function, used in real-time programming. This code is available only in Solaris 2.x.
SI_ASYNCIO
The signal was generated by the completion of an asynchronous input/output request. This code is available only in Solaris 2.x.
SI_MESGQ
The signal was generated by the arrival of a message on an empty message queue (used in real-time programming). This code is available only in Solaris 2.x.
In the latter four cases, the si_value element of the structure will contain the application-specified value that was passed to the signal-catching function when the signal was delivered.
If si_code contains a value greater than 0, it indicates the signal-specific reason that the system generated the signal, as shown in Table 10-1.
Table 10-1:  Values of si_code
si_signo
si_code
Reason
SIGILL
ILL_ILLOPC
illegal opcode
ILL_ILLOPN
illegal operand
ILL_ILLADDR
illegal addressing mode
ILL_ILLTRP
illegal trap
ILL_PRVOPC
privileged opcode
ILL_PRVREG
privileged register
ILL_COPROC
co-processor error
ILL_BADSTK
internal stack error
SIGFPE
FPE_INTDIV
integer division by 0
FPE_INTOVF
integer overflow
FPE_FLTDIV
floating point divide by 0
FPE_FLTOVF
floating-point overflow
FPE_FLTUND
floating-point underflow
FPE_FLTRES
floating-point inexact result
FPE_FLTINV
invalid floating-point operation
FPE_FLTSUB
subscript out of range
SIGSEGV
SEGV_MAPERR
address not mapped to object
SEGV_ACCERR
invalid permissions for mapped object
SIGBUS
BUS_ADRALN
invalid address alignment
BUS_ADRERR
non-existent physical address
BUS_OBJERR
object-specific hardware error
SIGTRAP
TRAP_BRKPT
process breakpoint
TRAP_TRACE
process trace trap
SIGCHLD
CLD_EXITED
child has exited
CLD_KILLED
child was killed
CLD_DUMPED
child terminated abnormally
CLD_TRAPPED
traced child has trapped
CLD_STOPPED
child has stopped
CLD_CONTINUED
stopped child has continued
SIGPOLL
POLL_IN
data input available
POLL_OUT
output buffers available
POLL_MSG
input message available
POLL_ERR
I/O error
POLL_PRI
high priority input available
POLL_HUP
device disconnected
In addition, other information may be provided for certain signals.
If the signal is SIGILL or SIGFPE, the si_addr element of the structure contains the address of the faulting instruction. If the signal is SIGSEGV or SIGBUS, si_addr contains the address of the faulting memory reference. (For some implementations the exact value of si_addr may not be available; in that case, si_addr is guaranteed to be on the same page as the faulting instruction or memory reference.)
If the signal is SIGCHLD, then the si_pid element of the structure will contain the process ID of the described child, and si_status will contain either the child's exit status (if si_code is CLD_EXITED) or the signal that caused the child to change state.
If the signal is SIGPOLL, the si_band element of the structure will contain the band event if si_code is equal to POLL_IN, POLL_OUT, or POLL_MSG.
Other Functions
Although the sigaction function is the most significant part of the POSIX signal mechanism, there are also a number of other functions defined. Some of these functions are simply souped-up versions of things we've already covered, while others are entirely new.
Sending signals
Although the kill function can still be used for sending signals to processes, SVR4 also defines two new functions that give the programmer somewhat more control over the set of processes the signal is delivered to:
    #include <sys/types.h>
    #include <sys/signal.h>
    #include <sys/procset.h>
    int sigsend(idtype_t idtype, id_t id, int sig);
    int sigsendset(procset_t *psp, int sig);
The sigsend function sends the signal specified by sig to the process or set of processes identified by idtype and id. If sig is 0, error checking is performed but no signal is actually sent. The legal values for idtype and their meanings are:
P_PID
The signal will be sent to the process with process ID set to id.
P_PGID
The signal will be sent to any process with process group ID set to id (see Chapter 11, Processes).
P_SID
The signal will be sent to any process with session ID set to id (see Chapter 11, Processes).
P_UID
The signal will be sent to any process with effective user ID set to id.
P_GID
The signal will be sent to any process with effective group ID set to id.
P_CID
The signal will be sent to any process with scheduler class ID set to id. This value is not available in HP-UX 10.x.
P_ALL
The signal will be sent to all processes; id is ignored.
If id is P_MYPID, the value of id is taken to be the calling process' process ID.
The sigsendset function provides an interesting way to send a signal to a set of processes. The signal is specified by sig, and the set of processes is specified by psp. The psp argument is a pointer to a structure of type procset_t:
    typedef struct {
        idop_t      p_op;
        idtype_t    p_lidtype;
        id_t        p_lid;
        idtype_t    p_ridtype;
        id_t        p_rid;
    } procset_t;
The p_lidtype and p_lid elements specify one set of processes (the “left” set), and the p_ridtype and p_rid elements specify another set (the “right” set). The idtypes and ids are specified in the same manner as for sigsend, described above.
The p_op element of the structure identifies an operation to be performed on the two sets of processes; the results of this operation are then used as the set of processes to which sig is delivered. Legal values for p_op are:
POP_DIFF
Set difference. Processes in the left set that are not in the right set.
POP_AND
Set intersection. Processes in both the left and right sets.
POP_OR
Set union. Processes in either the left set, right set, or both.
POP_XOR
Set exclusive-or. Processes in either the left or right set, but not both.
On success, sigsend and sigsendset return 0. On failure, they return -1 and errno will contain the reason for failure.
With both sigsend and sigsendset, the process with process ID set to 0 is always excluded, and the process with process ID 1 is excluded for all values of idtype except P_PID.
Also in both cases, the real or effective user ID of the calling process must match the real or effective user ID of the receiving process, unless the effective user ID of the sending process is that of the superuser, or sig is SIGCONT and the sending process has the same session ID as the receiving process.
Waiting for signals to occur
The POSIX standard provides two new functions for stopping a process until a signal occurs, sigsuspend and sigwait. The pause and sigpause functions, described earlier, may also be used for this purpose (however, sigpause should not be used with the POSIX signal functions, since it is part of a different signal mechanism).
    #include <signal.h>
    int sigsuspend(const sigset_t *set);
    int sigwait(sigset_t *set);
The sigsuspend function replaces the process' signal mask with the set of signals pointed to by set, and then suspends the process until delivery of a signal whose action is either to execute a signal-catching function or to terminate the process. On return, the process' signal mask is restored to the set that existed before the call to sigsuspend.
The sigwait function selects a signal from the set pointed to by set that is pending for the process. If no signals in set are pending, then sigwait blocks until a signal in set becomes pending. The selected signal is cleared from the set of signals pending for the process, and the number of the signal is returned. The selection of a signal in set is independent of the process' signal mask. This means that a process can synchronously wait for signals that are being blocked by the signal mask.
Both sigsuspend and sigwait return -1 and set errno if an error occurs.
Printing signal information
The psginal function, described earlier, can still be used with the POSIX signal functions to print information about signals. SVR4 also provides a second function, for use with the siginfo_t structure:
    #include <siginfo.h>
    void psiginfo(siginfo_t *pinfo, char *s);
Like psignal, psiginfo prints the string pointed to by s, followed by a colon, followed by a string describing the signal (pinfo->si_signo). It then prints a description of the reason the signal was delivered, as indicated by the siginfo_t structure pointed to by pinfo.
The psiginfo function is not available in HP-UX 10.x.
Example 10-7 shows another version of our signal program that demonstrates the use of psiginfo.
Example 10-7:  signal4
#include <signal.h>
#include <stdio.h>
void handler(int, siginfo_t *, void *);
int
main(void)
{
    struct sigaction sact;
    /*
     * Set up the sigaction structure.  We want to get the
     * extra information about the signal, so set SA_SIGINFO.
     */
    sact.sa_sigaction = handler;
    sact.sa_flags = SA_SIGINFO;
    sigemptyset(&sact.sa_mask);
    /*
     * Send SIGUSR1 and SIGUSR2 to the handler function.
     */
    if (sigaction(SIGUSR1, &sact, (struct sigaction *) NULL) < 0) {
        fprintf(stderr, "cannot set handler for SIGUSR1\n");
        exit(1);
    }
    if (sigaction(SIGUSR2, &sact, (struct sigaction *) NULL) < 0) {
        fprintf(stderr, "cannot set handler for SIGUSR2\n");
        exit(1);
    }
    /*
     * Now wait for signals to arrive.
     */
    for (;;)
        pause();
}
/*
* handler - handle a signal.
*/
void
handler(int sig, siginfo_t *sinf, void *ucon)
{
    /*
     * Print out what we received.
     */
    psiginfo(sinf, "Received signal");
}
    % signal4 &
    [1] 12345
    % kill -USR1 12345
    Received signal: Signal User 1 (from process 678)
    % kill -USR2 12345
    Received signal: Signal User 2 (from process 678)
    % kill -USR1 12345
    Received signal: Signal User 1 (from process 678)
    % kill -USR2 12345
    Received signal: Signal User 2 (from process 678)
    % kill 12345
    [1] + Terminated     signal4
Manipulating the signal mask
The POSIX standard also specifies the way in which a process may examine and change its signal mask. This method is similar to, but less cumbersome than, the sighold/sigrelse method offered by SVR3.
    #include <signal.h>
    int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
The sigprocmask function is used both to examine and change the signal mask. If set is non-null, then the signal set it points to modifies the signal mask according to the value of how:
SIG_BLOCK
The signal set pointed to by set is added to the current signal mask.
SIG_UNBLOCK
The signal set pointed to by set is removed from the current signal mask.
SIG_SETMASK
The signal set pointed to by set replaces the current signal mask.
If oset is non-null, the previous value of the signal mask is stored in the area it points to. If set is null, the value of how is ignored and the signal mask is not changed; this enables the process to inquire about its current signal mask.
If there are any pending unblocked signals after the call to sigprocmask, at least one of those signals will be delivered to the process before sigprocmask returns.
On success, sigprocmask returns 0. On failure, it returns -1 and errno will contain the reason for failure.
Examining the list of pending signals
POSIX provides the sigpending function to obtain the list of signals a process has pending:
    #include <signal.h>
    int sigpending(sigset_t *set);
The function returns the list of signals that have been sent to the process but are being blocked from delivery by the signal mask, and stores them in the area pointed to by set. On success, sigpending returns 0; if it fails, it returns -1 and stores the reason for failure in errno.
The setjmp and longjmp functions, revisited
Recall that, when we discussed the setjmp and longjmp functions, we mentioned that they had one particularly annoying problem. Because the longjmp function is usually called from within a signal handler, and transfers control out of the signal handler without the handler ever returning, the signal that originally caused the handler to be invoked remains blocked in the process' signal mask.
To get around this problem, POSIX defines two new functions:
    #include <setjmp.h>
    int sigsetjmp(sigjmp_buf env, int savemask);
    void siglongjmp(sigjmp_buf env, int val);
These two functions are identical to setjmp and longjmp, except that they use a sigjmp_buf data type instead of a jmp_buf data type, and sigsetjmp takes an additional argument. If the value of savemask is non-zero, then sigsetjmp saves the process' signal mask and scheduling parameters, and they will be restored when siglongjmp is called.
The POSIX signal mechanism is substantially more powerful than either the Version 7 or SVR3 mechanisms, particularly for complex applications in which signals must be blocked or detailed information about why a signal was delivered is needed. However, as mentioned before, it's somewhat more than the average programmer usually needs.

Previous SectionNext Section
Books24x7.com, Inc © 2000 –  Feedback