8.4 Catching and Ignoring Signals-sigaction

Team-FLY

8.4 Catching and Ignoring Signals ” sigaction

The sigaction function allows the caller to examine or specify the action associated with a specific signal. The sig parameter of sigaction specifies the signal number for the action. The act parameter is a pointer to a struct sigaction structure that specifies the action to be taken. The oact parameter is a pointer to a struct sigaction structure that receives the previous action associated with the signal. If act is NULL , the call to sigaction does not change the action associated with the signal. If oact is NULL , the call to sigaction does not return the previous action associated with the signal.

  SYNOPSIS  #include <signal.h>   int sigaction(int sig, const struct sigaction *restrict act,                 struct sigaction *restrict oact);  POSIX:CX  

If successful, sigaction returns 0. If unsuccessful , sigaction returns “1 and sets errno . The following table lists the mandatory errors for sigaction .

errno

cause

EINVAL

sig is an invalid signal number, or attempt to catch a signal that cannot be caught, or attempt to ignore a signal that cannot be ignored

ENOTSUP

SA_SIGINFO bit of the sa_flags is set and the implementation does not support POSIX:RTS or POSIX:XSI

The struct sigaction structure must have at least the following members .

 struct sigaction {    void (*sa_handler)(int); /* SIG_DFL, SIG_IGN or pointer to function */    sigset_t sa_mask;        /* additional signals to be blocked                                   during execution of handler */    int sa_flags;            /* special flags and options */    void(*sa_sigaction) (int, siginfo_t *, void *); /* realtime handler */ }; 

The storage for sa_handler and sa_sigaction may overlap, and an application should use only one of these members to specify the action. If the SA_SIGINFO flag of the sa_flags field is cleared, the sa_handler specifies the action to be taken for the specified signal. If the SA_SIGINFO flag of the sa_flags field is set and the implementation supports either the POSIX:RTS or the POSIX:XSI Extension, the sa_sigaction field specifies a signal-catching function.

Example 8.14

The following code segment sets the signal handler for SIGINT to mysighand .

 struct sigaction newact; newact.sa_handler = mysighand;  /* set the new handler */ newact.sa_flags = 0;            /* no special options */ if ((sigemptyset(&newact.sa_mask) == -1)      (sigaction(SIGINT, &newact, NULL) == -1))     perror("Failed to install SIGINT signal handler"); 

In the POSIX base standard, a signal handler is an ordinary function that returns void and has one integer parameter. When the operating system delivers the signal, it sets this parameter to the number of the signal that was delivered. Most signal handlers ignore this value, but it is possible to have a single signal handler for many signals. The usefulness of signal handlers is limited by the inability to pass values to them. This capability has been added to the POSIX:RTS and POSIX:XSI Extensions, which can use the alternative sa_sigaction field of the struct sigaction structure to specify a handler. This section describes using the sa_handler field of sigaction to set up the handler; Section 9.4 describes using the sa_sigaction field for the handler.

Two special values of the sa_handler member of struct sigaction are SIG_DFL> and SIG_IGN . The SIG_DFL value specifies that sigaction should restore the default action for the signal. The SIG_IGN value specifies that the process should handle the signal by ignoring it (throwing it away).

Example 8.15

The following code segment causes the process to ignore SIGINT if the default action is in effect for this signal.

 struct sigaction act; if (sigaction(SIGINT, NULL, &act) == -1)  /* Find current SIGINT handler */    perror("Failed to get old handler for SIGINT"); else if (act.sa_handler == SIG_DFL) {    /* if SIGINT handler is default */    act.sa_handler = SIG_IGN;         /* set new SIGINT handler to ignore */    if (sigaction(SIGINT, &act, NULL) == -1)       perror("Failed to ignore SIGINT"); } 
Example 8.16

The following code segment sets up a signal handler that catches the SIGINT signal generated by Ctrl-C.

 void catchctrlc(int signo) {    char handmsg[] = "I found Ctrl-C\n";    int msglen = sizeof(handmsg);    write(STDERR_FILENO, handmsg, msglen); } ... struct sigaction act; act.sa_handler = catchctrlc; act.sa_flags = 0; if ((sigemptyset(&act.sa_mask) == -1)      (sigaction(SIGINT, &act, NULL) == -1))    perror("Failed to set SIGINT to handle Ctrl-C"); 
Exercise 8.17

Why didn't Example 8.16 use fprintf or strlen in the signal handler?

Answer:

POSIX guarantees that write is async-signal safe, meaning that it can be called safely from inside a signal handler. There are no similar guarantees for fprintf or strlen , but they may be async-signal safe in some implementations . Table 8.2 on page 285 lists the functions that POSIX guarantees are async-signal safe.

Example 8.18

The following code segment sets the action of SIGINT to the default.

 struct sigaction newact; newact.sa_handler = SIG_DFL;    /* new handler set to default */ newact.sa_flags = 0;            /* no special options */ if ((sigemptyset(&newact.sa_mask) == -1)      (sigaction(SIGINT, &newact, NULL) == -1))    perror("Failed to set SIGINT to the default action"); 
Example 8.19 testignored.c

The following function takes a signal number parameter and returns 1 if that signal is ignored and 0 otherwise .

 #include <signal.h> #include <stdio.h> int testignored(int signo) {    struct sigaction act;    if ((sigaction(signo, NULL, &act) == -1)  (act.sa_handler != SIG_IGN))       return 0;    return 1; } 

Program 8.5 estimates the average value of sin(x) on the interval from 0 to 1 by computing the average of the sine of randomly picked values. The main program loop chooses a random value, x, between 0 and 1, adds sin(x) to a running sum, increments the count of the values, and prints the current count and average. The program illustrates the use of a signal handler to gracefully terminate a program. When the user enters Ctrl-C at standard input, the signal handler sets doneflag to signify that the program should terminate. On each iteration of the computation loop, the program tests doneflag to see whether it should drop out of the loop and print a final message.

Program 8.5 signalterminate.c

A program that terminates gracefully when it receives a Ctrl-C .

 #include <math.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> static volatile sig_atomic_t doneflag = 0; /* ARGSUSED */ static void setdoneflag(int signo) {     doneflag = 1; } int main (void) {     struct sigaction act;     int count = 0;     double sum = 0;     double x;     act.sa_handler = setdoneflag;            /* set up signal handler */     act.sa_flags = 0;     if ((sigemptyset(&act.sa_mask) == -1)            (sigaction(SIGINT, &act, NULL) == -1)) {         perror("Failed to set SIGINT handler");         return 1;     }     while (!doneflag) {         x = (rand() + 0.5)/(RAND_MAX + 1.0);         sum += sin(x);         count++;         printf("Count is %d and average is %f\n", count, sum/count);     }     printf("Program terminating ...\n");     if (count == 0)         printf("No values calculated yet\n");     else         printf("Count is %d and average is %f\n", count, sum/count);     return 0; } 

Code that accesses doneflag is a critical section because the signal handler can modify this variable while the main program examines it. (See Chapter 14 for a discussion of critical sections and atomic operations.) We handle the problem here by declaring doneflag to be sig_atomic_t , an integral type that is small enough to be accessed atomically. The volatile qualifier on doneflag informs the compiler that the variable may be changed asynchronously to program execution. Otherwise, the compiler might assume that doneflag is not modified in the while loop and generate code that only tests the condition on the first iteration of the loop.

Exercise 8.20

Why is it okay to use perror and printf in Program 8.5 even though these functions are not "signal safe"?

Answer:

Signal safety is a problem when both the signal handler and the main program use these functions. In this case, only the main program uses these functions.

When both a signal handler and the main program need to access data that is larger than sig_atomic_t , care must be taken so that the data is not modified in one part of the program while being read in another. Program 8.6 also calculates the average value of sin(x) over the interval from 0 to 1, but it does not print the result on each iteration. Instead, the main program loop generates a string containing the results every 10,000th iteration. A signal handler for SIGUSR1 outputs the string when the user sends SIGUSR1 to the process.

Program 8.6 averagesin.c

A program to estimate the average values of sin(x) over the interval from 0 to 1 .

 #include <errno.h> #include <limits.h> #include <math.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define BUFSIZE 100 static char buf[BUFSIZE]; static int buflen = 0; /* ARGSUSED */ static void handler(int signo) {          /* handler outputs result string */     int savederrno;     savederrno = errno;     write(STDOUT_FILENO, buf, buflen);     errno = savederrno; } static void results(int count, double sum) {       /* set up result string */     double average;     double calculated;     double err;     double errpercent;     sigset_t oset;     sigset_t sigset;     if ((sigemptyset(&sigset) == -1)            (sigaddset(&sigset, SIGUSR1) == -1)            (sigprocmask(SIG_BLOCK, &sigset, &oset) == -1) )         perror("Failed to block signal in results");     if (count == 0)         snprintf(buf, BUFSIZE, "No values calculated yet\n");     else {         calculated = 1.0 - cos(1.0);         average = sum/count;         err = average - calculated;         errpercent = 100.0*err/calculated;         snprintf(buf, BUFSIZE,                  "Count = %d, sum = %f, average = %f, error = %f or %f%%\n",                  count, sum, average, err, errpercent);     }     buflen = strlen(buf);     if (sigprocmask(SIG_SETMASK, &oset, NULL) == -1)         perror("Failed to unblock signal in results"); } int main(void) {     int count = 0;     double sum = 0;     double x;     struct sigaction act;     act.sa_handler = handler;     act.sa_flags = 0;     if ((sigemptyset(&act.sa_mask) == -1)            (sigaction(SIGUSR1, &act, NULL) == -1) ) {         perror("Failed to set SIGUSR1 signal handler");         return 1;     }     fprintf(stderr, "Process %ld starting calculation\n", (long)getpid());     for ( ; ; ) {         if ((count % 10000) == 0)             results(count, sum);         x = (rand() + 0.5)/(RAND_MAX + 1.0);         sum += sin(x);         count++;         if (count == INT_MAX)             break;     }     results(count, sum);     handler(0);        /* call handler directly to write out the results */     return 0; } 

The signal handler uses write instead of printf , since printf may not be safe to use in a signal handler. The handler avoids strlen for the same reason. The string and its length are global variables accessible to both the main program and the signal handler. Modifying the string in the main program and writing the string to standard output in the signal handler are critical sections for this program. The main program protects its critical section by having results block the signal while modifying the string and its length. Notice also that handler saves and restores errno , since write may change it.

Legacy programs sometimes use signal instead of sigaction to specify signal handlers. Although signal is part of ISO C, it is unreliable even when used in a program with a single thread. Always use sigaction to set up your handlers .

Team-FLY


Unix Systems Programming
UNIX Systems Programming: Communication, Concurrency and Threads
ISBN: 0130424110
EAN: 2147483647
Year: 2003
Pages: 274

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net