and sigwait

Team-FLY

8.5 Waiting for Signals ” pause , sigsuspend and sigwait

Signals provide a method for waiting for an event without busy waiting . Busy waiting means continually using CPU cycles to test for the occurrence of an event. Typically, a program does this testing by checking the value of a variable in a loop. A more efficient approach is to suspend the process until the waited-for event occurs; that way, other processes can use the CPU productively. The POSIX pause , sigsuspend and sigwait functions provide three mechanisms for suspending a process until a signal occurs.

8.5.1 The pause function

The pause function suspends the calling thread until the delivery of a signal whose action is either to execute a user -defined handler or to terminate the process. If the action is to terminate, pause does not return. If a signal is caught by the process, pause returns after the signal handler returns.

  SYNOPSIS  #include <unistd.h>   int pause(void);  POSIX  

The pause function always returns “1. If interrupted by a signal, pause sets errno to EINTR .

To wait for a particular signal by using pause , you must determine which signal caused pause to return. This information is not directly available, so the signal handler must set a flag for the program to check after pause returns.

Exercise 8.21

The following code segment uses pause to cause a process to wait for a particular signal by having the signal handler set the sigreceived variable to 1. What happens if a signal is delivered between the test of sigreceived and pause ?

 static volatile sig_atomic_t sigreceived = 0; while(sigreceived == 0)     pause(); 

Answer:

The previously delivered signal does not affect pause . The pause function does not return until some other signal or another occurrence of the same signal is delivered to the process. A workable solution must test the value of sigreceived while the signal is blocked.

Exercise 8.22

What is wrong with the following attempt to prevent a signal from being delivered between the test of sigreceived and the execution of pause in Exercise 8.21?

 static volatile sig_atomic_t sigreceived = 0; int signum; sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, signum); sigprocmask(SIG_BLOCK, &sigset, NULL); while(sigreceived == 0)    pause(); 

Answer:

Unfortunately, the code segment executes pause while the signal is blocked. As a result, the program never receives the signal and pause never returns. If the program unblocks the signal before executing pause , it might receive the signal between the unblocking and the execution of pause . This event is actually more likely than it seems. If a signal is generated while the process has the signal blocked, the process receives the signal right after unblocking it.

8.5.2 The sigsuspend function

The delivery of a signal before pause was one of the major problems with the original UNIX signals, and there was no simple, reliable way to get around the problem. The program must do two operations "at once" ”unblock the signal and start pause . Another way of saying this is that the two operations together should be atomic (i.e., the program cannot be logically interrupted between execution of the two operations). The sigsuspend function provides a method of achieving this.

The sigsuspend function sets the signal mask to the one pointed to by sigmask and suspends the process until a signal is caught by the process. The sigsuspend function returns when the signal handler of the caught signal returns. The sigmask parameter can be used to unblock the signal the program is looking for. When sigsuspend returns, the signal mask is reset to the value it had before the sigsuspend function was called.

  SYNOPSIS  #include <signal.h>   int sigsuspend(const sigset_t *sigmask);  POSIX:CX  

The sigsuspend function always returns “1 and sets errno . If interrupted by a signal, sigsuspend sets errno to EINTR .

Exercise 8.23

What is wrong with the following code that uses sigsuspend to wait for a signal?

 sigfillset(&sigmost); sigdelset(&sigmost, signum); sigsuspend(&sigmost); 

Answer:

The sigmost signal set contains all signals except the one to wait for. When the process suspends, only the signal signum is unblocked and so it seems that only this signal can cause sigsuspend to return. However, the code segment has the same problem that the solution using pause had. If the signal is delivered before the start of the code segment, the process still suspends itself and deadlocks if another signum signal is not generated.

Example 8.24

The following code segment shows a correct way to wait for a single signal. Assume that a signal handler has been set up for the signum signal and that the signal handler sets sigreceived to 1.

 1  static volatile sig_atomic_t sigreceived = 0;  2  3  sigset_t maskall, maskmost, maskold;  4  int signum = SIGUSR1;  5  6  sigfillset(&maskall);  7  sigfillset(&maskmost);  8  sigdelset(&maskmost, signum);  9  sigprocmask(SIG_SETMASK, &maskall, &maskold); 10  if (sigreceived == 0) 11     sigsuspend(&maskmost); 12  sigprocmask(SIG_SETMASK, &maskold, NULL); 

The code omits error checking for clarity.

Example 8.24 uses three signal sets to control the blocking and unblocking of signals at the appropriate time. Lines 6 through 8 set maskall to contain all signals and maskmost to contain all signals but signum . Line 9 blocks all signals. Line 10 tests sigreceived , and line 11 suspends the process if the signal has not yet been received. Note that no signals can be caught between the testing and the suspending, since the signal is blocked at this point. The process signal mask has the value maskmost while the process is suspended , so only signum is not blocked. When sigsuspend returns, the signal must have been received.

Example 8.25

The following code segment shows a modification of Example 8.24 that allows other signals to be handled while the process is waiting for signum .

 1  static volatile sig_atomic_t sigreceived = 0;  2  3  sigset_t maskblocked, maskold, maskunblocked;  4  int signum = SIGUSR1;  5  6  sigprocmask(SIG_SETMASK, NULL, &maskblocked);  7  sigprocmask(SIG_SETMASK, NULL, &maskunblocked);  8  sigaddset(&maskblocked, signum);  9  sigdelset(&maskunblocked, signum); 10  sigprocmask(SIG_BLOCK, &maskblocked, &maskold); 11  while(sigreceived == 0) 12     sigsuspend(&maskunblocked); 13  sigprocmask(SIG_SETMASK, &maskold, NULL); 

The code omits error checking for clarity.

Instead of blocking all signals and then unblocking only signum , Example 8.25 does not change the other signals in the signal mask. As before, the sigreceived variable declared in line 1 is declared outside any block and has static storage class. The code assumes that sigreceived is modified only in the signal handler for signum and that signal handler sets the value to 1. Thus, only the delivery of signum can make this variable nonzero. The rest of the code starting with line 3 is assumed to be inside some function.

The three signal sets declared in line 3 are initialized to contain the currently blocked signals in lines 6, 7 and 10. Line 8 adds the signal signum to the set maskblocked if it was not already blocked, and line 9 removes signum from maskunblocked if it was not already unblocked. The consequence of these two lines is that maskblocked contains exactly those signals that were blocked at the start of the code segment, except that signum is guaranteed to be in this set. Similarly, maskunblocked contains exactly those signals that were blocked at the start of the code segment, except that signum is guaranteed not to be in this set.

Line 10 guarantees that the signum signal is blocked while the value of sigreceived is being tested . No other signals are affected. The code ensures that sigreceived does not change between its testing in line 11 and the suspending of the process in line 12. Using maskunblocked in line 12 guarantees that the signal will not be blocked while the process is suspended, allowing a generated signal to be delivered and to cause sigsuspend to return. When sigsuspend does return, the while in line 11 executes again and tests sigreceived to see if the correct signal came in. Signals other than signum may have been unblocked before entry to the code segment and delivery of these signals causes sigsuspend to return. The code tests sigreceived each time and suspends the process again until the right signal is delivered. When the while condition is false, the signal has been received and line 13 executes, restoring the signal mask to its original state.

Example 8.26

The following code segment shows a shorter, but equivalent, version of the code in Example 8.25.

 1  static volatile sig_atomic_t sigreceived = 0;  2  3  sigset_t masknew, maskold;  4  int signum = SIGUSR1;  5  6  sigprocmask(SIG_SETMASK, NULL, &masknew);  7  sigaddset(&masknew, signum);  8  sigprocmask(SIG_SETMASK, &masknew, &maskold);  9  sigdelset(&masknew, signum); 10  while(sigreceived == 0) 11     sigsuspend(&masknew); 12  sigprocmask(SIG_SETMASK, &maskold, NULL); 

This code omits error checking for clarity.

Lines 6 and 7 set masknew to contain the original signal mask plus signum . Line 8 modifies the signal mask to block signum . Line 9 modifies masknew again so that now it does not contain signum . This operation does not change the process signal mask or the signals that are currently blocked. The signal signum is still blocked when line 10 tests sigreceived , but it is unblocked when line 11 suspends the process because of the change made to masknew on line 9.

The code segment in Example 8.26 assumes that sigreceived is initially 0 and that the handler for signum sets sigreceived to 1. It is important that the signal be blocked when the while is testing sigreceived . Otherwise, the signal can be delivered between the test of sigreceived and the call to sigsuspend . In this case, the process blocks until another signal causes the sigsuspend to return.

Exercise 8.27

Suppose the sigsuspend in Example 8.26 returns because of a different signal. Is the signum signal blocked when the while tests sigreceived again?

Answer:

Yes, when sigsuspend returns, the signal mask has been restored to the state it had before the call to sigsuspend . The call to sigprocmask before the while guarantees that this signal is blocked.

Program 8.7 simplesuspend.c

An object that allows a program to safely block on a specific signal .

 #include <errno.h> #include <signal.h> #include <unistd.h> static int isinitialized = 0; static struct sigaction oact; static int signum = 0; static volatile sig_atomic_t sigreceived = 0; /* ARGSUSED */ static void catcher (int signo) {     sigreceived = 1; } int initsuspend (int signo) {        /* set up the handler for the pause */     struct sigaction act;     if (isinitialized)         return 0;     act.sa_handler = catcher;     act.sa_flags = 0;     if ((sigfillset(&act.sa_mask) == -1)            (sigaction(signo, &act, &oact) == -1))         return -1;     signum = signo;     isinitialized = 1;     return 0; } int restore(void) {     if (!isinitialized)         return 0;     if (sigaction(signum, &oact, NULL) == -1)         return -1;     isinitialized = 0;     return 0; } int simplesuspend(void) {     sigset_t maskblocked, maskold, maskunblocked;     if (!isinitialized) {         errno = EINVAL;         return -1;     }     if ((sigprocmask(SIG_SETMASK, NULL, &maskblocked) == -1)            (sigaddset(&maskblocked, signum) == -1)            (sigprocmask(SIG_SETMASK, NULL, &maskunblocked) == -1)            (sigdelset(&maskunblocked, signum) == -1)            (sigprocmask(SIG_SETMASK, &maskblocked, &maskold) == -1))         return -1;     while(sigreceived == 0)         sigsuspend(&maskunblocked);     sigreceived = 0;     return sigprocmask(SIG_SETMASK, &maskold, NULL); } 

Program 8.7 shows an object implementation of functions to block on a specified signal. Before calling simplesuspend , the program calls initsuspend to set up the handler for the signal to pause on. The program calls restore to reset signal handling to the prior state.

Program 8.8 uses the functions of Program 8.7 to wait for SIGUSR1 .

Program 8.8 simplesuspendtest.c

A program that waits for SIGUSR1 .

 #include <signal.h> #include <stdio.h> #include <unistd.h> int initsuspend(int signo); int restore(void); int simplesuspend(void); int main(void) {     fprintf(stderr, "This is process %ld\n", (long)getpid());     for ( ; ; ) {         if (initsuspend(SIGUSR1)) {             perror("Failed to setup handler for SIGUSR1");             return 1;         }         fprintf(stderr, "Waiting for signal\n");         if (simplesuspend()) {             perror("Failed to suspend for signal");             return 1;         }         fprintf(stderr, "Got signal\n");         if (restore()) {             perror("Failed to restore original handler");             return 1;         }     }     return 1; } 

Program 8.9, which is based on the strategy of Example 8.25, uses two signals to control the setting or clearing of a flag. To use the service, a program calls initnotify with the two signals that are to be used for control. The signo1 signal handler sets the notifyflag ; the signo2 signal handler clears the notifyflag . After the initialization, the program can call waitnotifyon to suspend until the notification is turned on by the delivery of a signo1 signal.

Program 8.9 notifyonoff.c

An object that provides two-signal control for turning on or off a service .

 #include <errno.h> #include <signal.h> #include <stdio.h> static volatile sig_atomic_t notifyflag = 1; static int signal1 = 0; static int signal2 = 0; /* ARGSUSED */ static void turnon(int s) {     notifyflag = 1; } /* ARGSUSED */ static void turnoff(int s) {     notifyflag = 0; } /* ---------------------------Public functions --------------------------*/ int initnotify(int signo1, int signo2) {        /* set up for the notify */     struct sigaction newact;     signal1 = signo1;     signal2 = signo2;     newact.sa_handler = turnon;                 /* set up signal handlers */     newact.sa_flags = 0;     if ((sigemptyset(&newact.sa_mask) == -1)            (sigaddset(&newact.sa_mask, signo1) == -1)            (sigaddset(&newact.sa_mask, signo2) == -1)            (sigaction(signo1, &newact, NULL) == -1))         return -1;     newact.sa_handler = turnoff;     if (sigaction(signo2, &newact, NULL) == -1)         return -1;     return 0; } int waitnotifyon(void) {          /* Suspend until notifyflag is nonzero */     sigset_t maskblocked, maskold, maskunblocked;     if ((sigprocmask(SIG_SETMASK, NULL, &maskblocked) == -1)            (sigprocmask(SIG_SETMASK, NULL, &maskunblocked) == -1)            (sigaddset(&maskblocked, signal1) == -1)            (sigaddset(&maskblocked, signal2) == -1)            (sigdelset(&maskunblocked, signal1) == -1)            (sigdelset(&maskunblocked, signal2) == -1)            (sigprocmask(SIG_BLOCK, &maskblocked, &maskold) == -1))         return -1;     while (notifyflag == 0)         sigsuspend(&maskunblocked);     if (sigprocmask(SIG_SETMASK, &maskold, NULL) == -1)         return -1;     return 0; } 

Section 5.6 presented a simplebiff program to notify a user when mail is present. Program 8.10 shows a more sophisticated version that uses stat to determine when the size of the mail file increases . The program outputs the bell character to inform the user that new mail has arrived. This program uses the service of Program 8.9 to turn mail notification on or off without killing the process. The user sends a SIGUSR1 signal to turn on mail notification and a SIGUSR2 signal to turn off mail notification.

Program 8.10 biff.c

A biff program that uses the notifyonoff service .

 #include <errno.h> #include <limits.h> #include <pwd.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> #include "notifyonoff.h" #define MAILDIR "/var/mail/" static int checkmail(char *filename) {               /* is there new mail ? */     struct stat buf;     int error = 0;     static long newsize = 0;     static long oldsize = 0;     error = stat(filename, &buf);                   /* check the file status */     if ((error == -1) && (errno != ENOENT))         return -1;                       /* real error indicated by -1 return */     if (!error)         newsize = (long)buf.st_size;     else         newsize = 0;     if (newsize > oldsize)         error = 1;                           /* return 1 to indicate new mail */     else         error = 0;                        /* return 0 to indicate no new mail */     oldsize = newsize;     return error; } int main(int argc, char *argv[]) {     int check;     char mailfile[PATH_MAX];     struct passwd *pw;     int sleeptime;     if (argc != 2) {         fprintf(stderr, "Usage: %s sleeptime\n", argv[0]);         return 1;     }     sleeptime = atoi(argv[1]);     if ((pw = getpwuid(getuid())) == NULL) {         perror("Failed to determine login name");         return 1;     }     if (initnotify(SIGUSR1, SIGUSR2) == -1) {         perror("Failed to set up turning on/off notification");         return 1;     }     snprintf(mailfile, PATH_MAX,"%s%s",MAILDIR,pw->pw_name);     for( ; ; ) {         waitnotifyon();         sleep(sleeptime);         if ((check = checkmail(mailfile)) == -1) {             perror("Failed to check mail file");             break;         }         if (check)             fprintf(stderr, " 
 #include <errno.h> #include <limits.h> #include <pwd.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> #include "notifyonoff.h" #define MAILDIR "/var/mail/" static int checkmail(char *filename) { /* is there new mail ? */ struct stat buf; int error = 0; static long newsize = 0; static long oldsize = 0; error = stat(filename, &buf); /* check the file status */ if ((error == -1) && (errno != ENOENT)) return -1; /* real error indicated by -1 return */ if (!error) newsize = (long)buf.st_size; else newsize = 0; if (newsize > oldsize) error = 1; /* return 1 to indicate new mail */ else error = 0; /* return 0 to indicate no new mail */ oldsize = newsize; return error; } int main(int argc, char *argv[]) { int check; char mailfile[PATH_MAX]; struct passwd *pw; int sleeptime; if (argc != 2) { fprintf(stderr, "Usage: %s sleeptime\n", argv[0]); return 1; } sleeptime = atoi(argv[1]); if ((pw = getpwuid(getuid())) == NULL) { perror("Failed to determine login name "); return 1; } if (initnotify(SIGUSR1, SIGUSR2) == -1) { perror("Failed to set up turning on/off notification"); return 1; } snprintf (mailfile, PATH_MAX,"%s%s",MAILDIR,pw->pw_name); for( ; ; ) { waitnotifyon(); sleep(sleeptime); if ((check = checkmail(mailfile)) == -1) { perror("Failed to check mail file"); break; } if (check) fprintf(stderr, "\007"); } return 1; } 
7"); } return 1; }

8.5.3 The sigwait function

The sigwait function blocks until any of the signals specified by *sigmask is pending and then removes that signal from the set of pending signals and unblocks. When sigwait returns, the number of the signal that was removed from the pending signals is stored in the location pointed to by signo .

  SYNOPSIS  #include <signal.h>   int sigwait(const sigset_t *restrict sigmask,               int *restrict signo);  POSIX:CX  

If successful, sigwait returns 0. If unsuccessful , sigwait returns “1 and sets errno . No mandatory errors are defined for sigwait .

Note the differences between sigwait and sigsuspend . Both functions have a first parameter that is a pointer to a signal set ( sigset_t * ). For sigsuspend , this set holds the new signal mask and so the signals that are not in the set are the ones that can cause sigsuspend to return. For sigwait , this parameter holds the set of signals to be waited for, so the signals in the set are the ones that can cause the sigwait to return. Unlike sigsuspend , sigwait does not change the process signal mask. The signals in sigmask should be blocked before sigwait is called.

Program 8.11 uses sigwait to count the number of times the SIGUSR1 signal is delivered to the process. Notice that no signal handler is necessary, since the signal is always blocked.

Program 8.11 countsignals.c

A program that counts the number of SIGUSR1 signals sent to it .

 #include <signal.h> #include <stdio.h> #include <unistd.h> int main(void) {     int signalcount = 0;     int signo;     int signum = SIGUSR1;     sigset_t sigset;     if ((sigemptyset(&sigset) == -1)            (sigaddset(&sigset, signum) == -1)            (sigprocmask(SIG_BLOCK, &sigset, NULL) == -1))         perror("Failed to block signals before sigwait");     fprintf(stderr, "This process has ID %ld\n", (long)getpid());     for ( ; ; ) {         if (sigwait(&sigset, &signo) == -1) {             perror("Failed to wait using sigwait");             return 1;         }         signalcount++;         fprintf(stderr, "Number of signals so far: %d\n", signalcount);     } } 
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