Section 12.7. Learning About a Signal


12.7. Learning About a Signal

The signals we have discussed so far carry no data with them; the arrival of the signal is the only information the application gets. In some cases it would be nice to know what caused the signal to be sent (such as the illegal memory address that caused a SIGSEGV) or to be able to include data with application-generated signals. The Real Time Signal extensions address both of these needs.

12.7.1. Getting a Signal's Context

The information about how and why a signal was generated is called the signal's context.[21] Applications that wish to view a signal's context use a different signal handler than the normal one. It includes two more parameters, a pointer to a siginfo_t, which provides the signal's context and a pointer to void *, which can be used by some low-level system libraries.[22] Here is what the full handler prototype looks like:

[21] Before the POSIX standards, applications could access a struct sigcontext for the same type of information now provided by siginfo_t, and the term context has stuck from this older implementation.

[22] This third parameter is actually a pointer to a struct ucontext, which allows processes to do full context switching in user space. Doing this is beyond the scope of this book, but it is well documented in the Single Unix Specification.

 void handler(int signum, siginfo_t * siginfo, void * context); 

Applications need to tell the kernel to pass full context information by setting the SA_SIGINFO flag in the sa_mask member of the struct sigaction used to register the signal handler. The sa_handler member is also not used, as it is a pointer to a function with a different prototype. Instead, a new member, sa_sigaction, is set to point to the signal handler with the proper prototype.

To help reduce memory usage, sa_handler and sa_sigaction are allowed to use the same region of memory, so only one should be used at a time. To make this transparent, the C library defines struct sigaction like this:

 #include <signal.h> struct sigaction {         union {             __sighandler_t sa_handler;             __sigaction_t sa_sigaction;         } __sigaction_handler;         sigset_t sa_mask;         unsigned long sa_flags; }; #define sa_handler __sigaction_handler.sa_handler #define sa_sigaction __sigaction_handler.sa_sigaction 

Using this combination of a union and macros allows the two members to overlap in memory without the data structure getting overly complicated from the application's view.

The siginfo_t structure contains information about where and why a signal was generated. Two members are available for all signals: si_signo and si_code. Which other members are available depends on the signal being delivered, and those members overlap in memory in a way similar to sa_handler and sa_sigaction in struct sigaction. The si_signo member contains the signal number that is being delivered and is the same as the first parameter passed to the signal handler, while si_code specifies why the signal was generated and changes depending on the signal number. For most signals, it is one of the following:


A user-space application used kill() to send the signal.[23]


Auser-space application used sigqueue() to send the signal, which is discussed on page 237.


The signal was sent by a user-space application using the tkill() system call. While the Linux kernel uses SI_TKILL, its value is not specified in current versions of the C library. If you need to check for SI_TKILL, use the following code segment to define its value:

 #ifndef SI_TKILL #define SI_TKILL -6 #endif 


SI_TKILL is not specified by any standard (but it is allowed by them), so it needs to be used with care in portable programs.


The signal was generated by the kernel.[24]

[23] The sigsend() function, which Linux includes for compatibility with some other Unix systems, also causes SI_USER.

[24] There are more values for si_code than we talk about here, related to features like asynchronous I/O, message queues, and real-time timers, which are outside the scope of this book.

When SIGILL, SIGFPE, SIGSEGV, SIGBUS, and SIGCHLD are sent by the kernel, si_code takes on the values shown in Table 12.3 instead of SI_KERNEL.[25]

[25] It also takes on special values for SIGTRAP, which is used by debuggers, and SIGPOLL, which is used by an unreliable asynchronous I/O mechanism. Neither of these topics is covered in this book, so the details on these signals have been omitted from Table 12.3.

Table 12.3. Values of si_code for Special Signals






Illegal opcode



Illegal operand



Illegal addressing mode



Illegal trap



Privileged opcode



Privileged register



Internal stack error



Coprocessor error



Integer divide by zero



Integer overflow



Floating point divide by zero



Floating point overflow



Floating point underflow



Floating point inexact result



Floating point invalid operation



Floating point subscript out of range



Address not mapped in an object



Invalid permissions for address



Invalid address alignment



Nonexistent physical address



Object specific hardware error



Child has exited



Child was killed without a core file



Child was killed and a core file was created



Child has hit a breakpoint



Child has stopped

To help clarify the various values si_code can take, here is an example that generates SIGCHLD in four different ways: kill(), sigqueue(), raise() (which uses the tkill() system call), and by creating a child that immediately terminates.

  1: /* sicode.c */  2:  3: #include <sys/signal.h>  4: #include <stdlib.h>  5: #include <stdio.h>  6: #include <unistd.h>  7:  8: #ifndef SI_TKILL  9: #define SI_TKILL -6 10: #endif 11: 12: void handler(int signo, siginfo_t * info, void * f) { 13:     static int count = 0; 14: 15:     printf("caught signal sent by "); 16:     switch (info->si_code) { 17:     case SI_USER: 18:         printf("kill()\n"); break; 19:     case SI_QUEUE: 20:         printf("sigqueue()\n"); break; 21:     case SI_TKILL: 22:         printf("tkill() or raise()\n"); break; 23:     case CLD_EXITED: 24:         printf("kernel telling us child exited\n"); exit(0); 25:     } 26: 27:     if (++count == 4) exit(1); 28: } 29: 30: int main() { 31:     struct sigaction act; 32:     union sigval val; 33:     pid_t pid = getpid(); 34: 35:     val.sival_int = 1234; 36: 37:     act.sa_sigaction = handler; 38:     sigemptyset(&act.sa_mask); 39:     act.sa_flags = SA_SIGINFO; 40:     sigaction(SIGCHLD, &act, NULL); 41: 42:     kill(pid, SIGCHLD); 43:     sigqueue(pid, SIGCHLD, val); 44:     raise(SIGCHLD); 45: 46:     /* To get a SIGCHLD from the kernel we create a child and 47:        have it exit immediately. The signal handler exits after 48:        receiving the signal from the kernel, so we just sleep for 49:        a while and let the program terminate that way. */ 50: 51:     if (!fork()) exit(0); 52:     sleep(60); 53: 54:     return 0; 55: } 

If si_code is SI_USER, SI_QUEUE, or SI_TKILL, two additional members of siginfo_t are available, si_pid and si_uid, which provide the process ID that sent the signal and the real user ID of that process.

When a SIGCHLD is sent by the kernel, the si_pid, si_status, si_utime, and si_stime members are available. The first, si_pid, gives the pid of the process whose status has changed.[26] Information on the new status is available in both si_code (as specified by Table 12.3) and si_status, which is identical to the status integer returned by the wait() family of functions. The final two members, si_utime and si_stime, specify the amount of the time the child application has spent in user space and kernel space, respectively (this is similar to measures wait3() and wait4() return in struct rusage). They are measured in clock ticks, which is an integer. The number of clock ticks per second is defined by the _SC_CLK_TCK macro, defined in <sysconf.h>.

[26] Recall that SIGCHLD is sent not only when a child has exited, but also when a child has stopped or resumed.

SIGSEGV, SIGBUS, SIGILL, and SIGFPE all provide si_addr, which specifies the address that caused the fault described by si_code.

Here is a simple example of examining a signal's context. It installs a signal handler for SIGSEGV that prints out the context for that signal and then terminates the process. A segmentation violation is generated by trying to dereference NULL.

  1: /* catch-segv.c */  2:  3: #include <sys/signal.h>  4: #include <stdlib.h>  5: #include <stdio.h>  6:  7: void handler(int signo, siginfo_t * info, void * f) {  8:     printf("caught ");  9:     if (info->si_signo == SIGSEGV) 10:         printf("segv accessing %p", info->si_addr); 11:     if (info->si_code == SEGV_MAPERR) 12:         printf(" SEGV_MAPERR"); 13:     printf("\n"); 14: 15:     exit(1); 16: } 17: 18: int main() { 19:     struct sigaction act; 20: 21:     act.sa_sigaction = handler; 22:     sigemptyset(&act.sa_mask); 23:     act.sa_flags = SA_SIGINFO; 24:     sigaction(SIGSEGV, &act, NULL); 25: 26:     *((int *) NULL) = 1; 27: 28:     return 0; 29: } 

12.7.2. Sending Data with a Signal

The siginfo_t mechanism also allows signals sent from programs to attach a single data element to the signal (this element may be a pointer, allowing an arbitrary amount of data to be passed indirectly). To send data, a union sigval is used.

 #include <signal.h> union sigval {         int sival_int;         void * sival_ptr; }; 

Either sival_int or sival_ptr may be set to an arbitrary value that is included in the siginfo_t delivered with the signal. To generate a signal with a union sigval, sigqueue() must be used.

 #include <signal.h> void * sigqueue(pid_t pid, int signum, const union sigval value); 

Unlike kill(), pid must be a valid process ID number (no negative values are allowed). The signum specifies the signal number to send. Like kill(), sigqueue() allows signum to be zero to check whether the calling process is allowed to send the target pid a signal without actually sending one. The final parameter, value, provides the datum that is delivered along with the signal.

To receive the union sigval, the process catching the signal must use SA_SIGINFO when registering its signal handler with sigaction(). When the si_code member of siginfo_t is SI_QUEUE, siginfo_t provides a si_value member that is the same as the value passed to sigqueue.

Here is an example of sending data elements with a signal. It queues three SIGRTMIN signals with different data elements. It demonstrates that the signals were delivered in the same order they were sent, which is what we would expect for queued real-time signals.[27] A more involved example uses signals to monitor changes in directories and is presented on page 319.

[27] For more examples of signal handling, look at the sample programs for file leases (page 287), tty handling (page 355), and interval timers (page 493).

  1: /* sigval.c */  2:  3: #include <sys/signal.h>  4: #include <stdlib.h>  5: #include <stdio.h>  6: #include <string.h>  7: #include <unistd.h>  8:  9: /* Catch a signal and record that it was handled. */ 10: void handler(int signo, siginfo_t * si, void * context) { 11:     printf("%d\n", si->si_value.sival_int); 12: } 13: 14: int main() { 15:     sigset_t mask; 16:     sigset_t oldMask; 17:     struct sigaction act; 18:     int me = getpid(); 19:     union sigval val; 20: 21:     /* Send signals to handler() and keep all signals blocked 22:        that handler() has been configured to catch to avoid 23:        races in manipulating the global variables. */ 24:     act.sa_sigaction = handler; 25:     act.sa_mask = mask; 26:     act.sa_flags = SA_SIGINFO; 27: 28:     sigaction(SIGRTMIN, &act, NULL); 29: 30:     /* Block SIGRTMIN so we can see the queueing and ordering */ 31:     sigemptyset(&mask); 32:     sigaddset(&mask, SIGRTMIN); 33: 34:     sigprocmask(SIG_BLOCK, &mask, &oldMask); 35: 36:     /* Generate signals */ 37:     val.sival_int = 1; 38:     sigqueue(me, SIGRTMIN, val); 39:     val.sival_int++; 40:     sigqueue(me, SIGRTMIN, val); 41:     val.sival_int++; 42:     sigqueue(me, SIGRTMIN, val); 43: 44:     /* Enable delivery of the signals. */ 45:     sigprocmask(SIG_SETMASK, &oldMask, NULL); 46: 47:     return 0; 48: } 


    Linux Application Development
    Linux Application Development (paperback) (2nd Edition)
    ISBN: 0321563220
    EAN: 2147483647
    Year: 2003
    Pages: 168 © 2008-2017.
    If you may any questions please contact us: