Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

54
Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

Transcript of Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

Page 1: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

Chapter 8Signals

Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

Page 2: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

8.1 Basic Signal Concepts

Page 3: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

3

Basic Signal Concepts• A signal is a software notification to a process of an event

• A signal is generated when the event that causes the signal occurs

• A signal is delivered when the process takes action based on the signal

• The lifetime of a signal is the interval between its generation and its delivery

• A signal that has been generated but not yet delivered is said to be pending– There may be considerable time between signal generation and signal delivery

– The process must be running on a computer at the time of signal delivery

• A program installs a signal handler by calling the sigaction() function with the name of a user-written function

• A process handles a signal if it executes a signal handler when the signal is delivered

• If the process is set to ignore a signal, that signal is thrown away when delivered and has no effect on the process

Page 4: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

4

Basic Signal Concepts(continued)

• The action taken when a signal is generated depends on the current signal handler for that signal and on the process signal mask

• The signal mask contains a list of currently blocked signals

• Blocked signals are not thrown away as ignored signals are

• If a signal is blocked, it is delivered when the process unblocks that signal

• A program blocks a signal by changing its process signal mask using sigprocmask()

• A program ignores a signal by setting the signal handler to SIG_IGN using sigaction()

Page 5: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

8.2 Generating Signals

Page 6: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

6

Signal Names

• Every signal has a symbolic name starting with SIG

• The signal names are defined in the signal.h file, which should be included by any C program that uses signals

• The table on the following slide lists some of the POSIX signals and their default actions

• Two signals, SIGUSR1 and SIGUSR2, are available for users and do not have a preassigned use

• Some signals such as SIGFPE and SIGSEGV are generated when certain errors occur; other signals are generated by specific function calls such as SIGALRM by the alarm() function

Page 7: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

7

Certain POSIX Signals

Abnormal terminationSegmentation fault11SIGSEGV

Implementation dependentInteractive termination; core dump3SIGQUIT

ContinueExecution continued if stopped25SIGCONT

17

16

15

23

9

2

8

14

6

NBR

Abnormal terminationUser-defined signal 2SIGUSR2

Abnormal terminationUser-defined signal 1SIGUSR1

Abnormal terminationTerminationSIGTERM

StopExecution stop (cannot be caught or ignored)

SIGSTOP

Abnormal terminationTermination (cannot be caught or ignored)

SIGKILL

Abnormal terminationInteractive attention signal (Ctrl-C)SIGINT

Implementation dependentArithmetic error (i.e., divide by zero)

SIGFPE

Abnormal terminationalarm clockSIGALRM

Implementation dependentProcess abort (sent by assert)SIGABRT

DEFAULT ACTIONDESCRIPTIONSIGNAL

Page 8: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

8

kill Command• A user can send signals to a process from a command shell using the kill command• The name of the command is a slight misnomer in that it can be used to send any of the

POSIX signals, not just the ones that cause abnormal process termination• The kill command sends a signal to the process or processes that are specified by each

pid operand

kill [-signal_name] pid ...kill [-signal_number] pid ...

• The first form of the kill command take a signal name as defined in signal.h without the SIG prefix (i.e., INT instead of SIGINT)

• The second form takes a signal number • If no signal name or number is specified, SIGTERM is sent by default• Example usage

kill 3425kill –9 896kill –KILL 3425kill –INT 9328kill –USR1 567

Page 9: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

9

kill() Function• In a program, a user can send a signal to a process using the kill() function

#include <signal.h>

int kill(pid_t pid, int signalNbr);

• The function takes a process ID and a signal number (i.e., a symbolic name) as parameters

• If successful, the function returns zero; otherwise, it returns –1 and sets errno• A user may only send a signal to a process that he or she owns

– For most signals, the kill() function determines permissions by comparing the user IDs of caller process and target process

• Example use of the kill() function

int status;pid_t childPid;. . .status = kill(childPid, SIGUSR1);if (status == -1) perror("Failed to send the SIGUSR1 signal to child");

Page 10: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

10

raise() Function

• A process can send a signal to itself with the raise() function

#include <signal.h>

int raise(int sig);

• The function takes just one parameter, a signal number• If successful, the function returns zero; otherwise, it returns a nonzero error

value and sets errno– The function sets errno to EINVAL if the signal number is invalid

• Example use of raise() function

int status;

status = raise(SIGUSR1);if (status != 0) perror("Failed to raise SIGUSR1");

Page 11: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

11

alarm() Function• The alarm() function causes a SIGALRM signal to be sent to the calling process after a

specified number of real seconds has elapsed

#include <unistd.h>

unsigned int alarm(unsigned int seconds);

• The function returns the number of seconds remaining on the alarm before the call resets the value, or zero if no previous alarm was set

• The function never reports an error• Requests to alarm() are not stacked, so a call to alarm() before the previous timer

expires causes the alarm to be reset to the new value• If alarm() is called with a zero value argument, it cancels a previous alarm request• The default action for SIGALRM is to terminate the process• The following program runs for five seconds of wall-clock time and then terminates

#include <unistd.h>

int main(void){alarm(5);for ( ; ; );return 0;} // End main

Page 12: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

12

assert() Function• The assert() function is used to put diagnostic checks into a program by means of an

assertion expression that evaluates to true or false (i.e., a nonzero or a zero result)

#include <assert.h>

void assert(int expression);

• When the value of the expression passed to the assert() function is false, the function first prints a diagnostic message to stderr containing the assertion expression, the file name and the line number in the file where the assertion is located. It than calls the abort() function. An example output is shown below

assertion “nbrOfValues <= 7" failed: file “sample-program.c", line 52 10 [sig] a 3640 open_stackdumpfile: Dumping stack trace to a.exe.stackdump

• The abort() function sends the SIGABRT signal to the process (using raise() ) and never returns. At that point, the process either executes the default signal handler for SIGABRT or calls the signal handler registered by the process. In either case, the process terminates

• The default action for SIGABRT is to request a stack trace dump and then terminate • If NDEBUG is defined in a program before the #include for assert.h, then any assert

statements in the program are ignored by the compiler

Page 13: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

13

Use of assert() #include <stdio.h>#include <limits.h>

// #define NDEBUG#include <assert.h>

#define MIN_SCORES 2#define MAX_SCORES 5

// ******************************************int main(void){float average;int i;int score;float sum = 0;int nbrOfScores;int scoreTable[MAX_SCORES];

printf("Enter the number of scores (%d - %d: “, MIN_SCORES, MAX_SCORES);scanf("%d", &nbrOfScores);printf(“\n");

assert( (nbrOfScores >= MIN_SCORES) && (nbrOfScores <= MAX_SCORES) );

(More on next slide)

Page 14: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

14

Use of assert()(continued)

for (i = 0; i < nbrOfScores; i++) { printf("Enter score #%d (x >= 0): ", i + 1); scanf("%d", &score);

assert( (score >= 0) && (score < INT_MAX) );

assert( (i >= 0) && ( i < nbrOfScores) ); scoreTable[i] = score;

sum = sum + score; } // End for

printf("\nList of Scores: ");for (i = 0; i < nbrOfScores; i++) printf(" %d ", scoreTable[i]);

assert(sum >= 0); average = sum / nbrOfScores;

assert(average >= 0);printf("\n\nAverage score: %.2f\n", average);return 0;} // End main

Page 15: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

Sample Outputs ( with assert )

uxb3% a.out Enter the number of scores (2 - 5): 1assert-demo.c:25: failed assertion `(nbrOfScores >= MIN_SCORES) && (nbrOfScores <= MAX_SCORES)'Abortuxb3%

uxb3% a.outEnter the number of scores (2 - 5): 4

Enter score #1 (x >= 0): -1assert-demo.c:32: failed assertion `(score >= 0) && (score < INT_MAX)'Abortuxb3%

uxb3% a.out Enter the number of scores (2 - 5): 3

Enter score #1 (x >= 0): 89Enter score #2 (x >= 0): 72Enter score #3 (x >= 0): 84

List of Scores: 89 72 84

Average score: 81.67uxb3%

Page 16: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

Sample Output ( with no assert )

uxb2% a.outEnter the number of scores (2 - 5): 10

Enter score #1 (x >= 0): -5Enter score #2 (x >= 0): -10Enter score #3 (x >= 0): -15Enter score #4 (x >= 0): -20Enter score #5 (x >= 0): -25Enter score #6 (x >= 0): 5Enter score #7 (x >= 0): 10Enter score #8 (x >= 0): 15Enter score #9 (x >= 0): 20Enter score #10 (x >= 0): 25Enter score #11 (x >= 0): 34

List of Scores: -5 -10 -15 -20 -25 5 10 15 1117650944 34 10 0 0 0 -4195632

Average score: 5.27uxb2%

Notice the amount of incorrect input and output that is not flagged nor pointed out as wrong

Page 17: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

8.3 Signal Masks and Signal Sets

Page 18: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

18

The Signal Set

• A process can temporarily prevent a signal from being delivered by blocking it• Blocked signals do not affect the behavior of the process until they are

delivered• The process signal mask identifies the set of signals that are currently blocked• A program specifies operations (such as blocking or unblocking) on groups of

signals by using signal sets of type sigset_t • Blocking a signal is different from ignoring a signal• When a process blocks a signal, the operating system does not deliver the

signal until the process unblocks the signal– A process blocks a signal by modifying its signal mask with sigprocmask()

• When a process ignores a signal, the signal is delivered and the process handles it by throwing it away

– A process sets a signal to be ignored by calling sigaction() with a handler of SIG_IGN (described later in these slides)

Page 19: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

19

Signal Set Functions• Signal sets are manipulated by the five functions listed below

#include <signal.h>

int sigaddset(sigset_t *set, int signalNbr);int sigdelset(sigset_t *set, int signalNbr);int sigemptyset(sigset_t *set);int sigfillset(sigset_t *set);int sigismember(const sigset_t *set, int signalNbr);

• The first parameter for each function is a pointer to a variable of type sigset_t• The sigaddset() function adds signalNbr to the signal set• The sigdelset() function removes signalNbr from the signal set• The sigemptyset() function initializes a sigset_t variable to contain no signals• The sigfillset() function initializes a sigset_t variable to contain all signals

– A program initializes a signal set by calling either sigemptyset() or sigfillset() before using it

• Each of these functions return zero if the operation is successful; otherwise, the function returns –1 and sets errno

• The sigismember() function reports whether signalNbr is in a sigset_t variable– It returns 1 if sigNbr is in the signal set and zero if it is not

Page 20: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

20

Example use of Signal Set Functions#include <stdio.h>#include <signal.h>

// ******************************* int main(void){int status;sigset_t mySet;

status = sigemptyset(&mySet);if (status == -1) perror("Failed to empty signal set");status = sigaddset(&mySet, SIGINT);if (status == -1) perror("Failed to add SIGINT signal to signal set");status = sigaddset(&mySet, SIGQUIT);if (status == -1) perror("Failed to add SIGQUIT signal to signal set");

if (sigismember(&mySet, SIGINT)) fprintf(stderr, "The set contains the SIGINT signal\n");else fprintf(stderr, "The set does not contain the SIGINT signal\n");

return 0;} // End main

Page 21: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

21

sigprocmask() Function• A process can examine or modify its process signal mask with the sigprocmask()

function

#include <signal.h>

int sigprocmask(int how, const sigset_t *set, sigset_t *oldSet);

– The how parameter is an integer specifying the manner in which the signal mask is to be modified

– The set parameter is a pointer to a signal set to be used in the modification• If set is NULL, no modification is made

– If oldSet is not NULL, the function sets oldSet to a pointer pointing to the signal set used before the modification

• If successful, the function returns zero; otherwise, it returns –1 and sets errno• The sigprocmask() function should only be used by a process with a single thread

– When multiple threads exist, the pthread_sigmask() function should be used

Page 22: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

22

Using the sigprocmask() Function• The how parameter can take on one of the following three values

– SIG_BLOCK Add a collection of signals to those currently blocked– SIG_UNBLOCK Delete a collection of signals from those currently blocked– SIG_SETMASK Set the collection of signals being blocked to the specified set

• Some signals, such as SIGSTOP and SIGKILL cannot be blocked– If an attempt is made to block these signals, the system ignores the request without

reporting an error

• The program on the next slide enters an infinite loop in which it displays a message, blocks the SIGINT signal, does some calculations, unblocks the signal, and does some more calculations

– If a user enters Ctrl-C while SIGINT is blocked, the programs finishes the calculations and prints the message "Calculation ran safe from interruption" before terminating

– If a user enters Ctrl-C while SIGINT is unblocked, the program terminates immediately (probably in the midst of the unblocked calculation loop)

Page 23: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

23

Example #1: use of sigprocmask()#include <math.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h> int main(void) {int i;sigset_t intMask;int repeatFactor = 10000;double y = 0.0;int status; status = sigemptyset(&intMask);if (status == -1) { perror("Failed to initialize the signal set"); return 1; } // End ifstatus = sigaddset(&intMask, SIGINT);if (status == -1) { perror("Failed to add SIGINT to the signal set"); return 1; } // End if (More on next slide)

Page 24: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

24

Example #1: use of sigprocmask() (continued)

for ( ; ; ) // Infinite loop { status = sigprocmask(SIG_BLOCK, &intMask, NULL); if (status == -1) break; fprintf(stderr, "Blocked the SIGINT signal\n"); for (i = 0; i < repeatFactor; i++) y = y + sin((double)i); fprintf(stderr, "[SIGINT Blocked] Calculation ran safe from interruption\n");

status = sigprocmask(SIG_UNBLOCK, &intMask, NULL); if (status == -1) break; fprintf(stderr, "Unblocked the SIGINT signal\n"); for (i = 0; i < repeatFactor; i++) y = y + sin((double)i); fprintf(stderr, "[SIGINT Unblocked] Calculation ran, but vulnerable to interruption\n"); } // End for

perror("Failed to block or unblock signal mask");return 1;} // End main

Page 25: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

25

Example #2: use of sigprocmask()#include <errno.h>#include <stdio.h>#include <signal.h>#include <unistd.h>#include <sys/types.h>#include <sys/wait.h>

int main(void) {pid_t pid;sigset_t mask, oldMask;int status;

status = sigfillset(&mask);if (status == -1) { perror("Failed to fill the signal set"); return 1; } // End if

(More on next slide)

Page 26: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

26

Example #2: use of sigprocmask()(continued)

status = sigprocmask(SIG_SETMASK, &mask, &oldMask);if (status == -1) { perror("Failed to block signals using the signal mask"); return 1; } // End ifelse fprintf(stderr, "\nParent set mask to block all signals before creating child\n\n");

pid = fork();if (pid == -1) { perror("Failed to create child process"); return 1; } // End if

if (pid == 0) // Child process { fprintf(stderr, "Child process is running with inherited signal mask from parent\n\n"); execl("/bin/ls", "ls", "-l", NULL); perror("exec function failed in child process"); return 1; } // End if (More on next slide)

Page 27: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

27

Example #2: use of sigprocmask()(continued)

else // Parent process { fprintf(stderr, "Parent created child process\n\n"); status = sigprocmask(SIG_SETMASK, &oldMask, NULL); if (status == -1) { perror("Parent failed to restore signal mask"); return 1; } // End if else fprintf(stderr, "Parent reset signal mask to original values after creating child\n\n");

fprintf(stderr, "Parent waiting for child process to terminate...\n\n"); status = wait(NULL); if (status == -1) { perror("Parent failed to wait for child"); return 1; } // End if fprintf(stderr, "\nParent has detected termination of child process\n"); } // End else

return 0;} // End main

Page 28: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

28

Example #2: Sample Outputuxb2% a.out

Parent set mask to block all signals before creating child

Parent created child process

Parent reset signal mask to original values after creating child

Parent waiting for child process to terminate...

Child process is running with inherited signal mask from parent

total 6drwx------ 12 jjt107 faculty 1024 Jun 21 10:35 Filesdrwx------ 2 jjt107 faculty 1024 Jul 24 08:43 Maildrwx--x--x 19 jjt107 faculty 1024 Jul 3 12:32 http

Parent has detected termination of child processuxb2%

Page 29: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

8.4 Catching and Ignoring Signals

Page 30: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

30

sigaction() Function• The sigaction() function allows the calling program to set and examine the

action associated with a specific signal

#include <signal.h>

int sigaction(int signalNbr, const struct sigaction *action, struct sigaction *oldAction);

– The signalNbr parameter specifies the signal number for the action– The action parameter is a pointer to a struct sigaction structure that specifies

the action to be taken– If action is NULL, the call to the function does not change the action associated with

the signal– The oldAction parameter is a pointer to a struct sigaction structure that is

assigned the previous action associated with the signal

• If successful, the function returns zero; otherwise, it returns –1 and sets errno

Page 31: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

31

struct sigaction• The struct sigaction structure has the following contents:

struct sigaction{void (*sa_handler)(int);sigset_t sa_mask;int sa_flags;void (*sa_sigaction)(int, siginfo_t *, void *);};

– The sa_handler member is assigned SIG_DFL, SIG_IGN or a pointer to a signal handler function

– The sa_mask member is assigned additional signals to be blocked during execution of the handler

– The sa_flags member is assigned special flags and options– The sa_sigaction member is assigned a pointer to a real-time signal handler

• The storage for sa_handler and sa_sigaction may overlap; consequently, a program 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 field specifies the action to be taken for the specified signal

– If the SA_SIGINFO flag of the sa_flags field is set, the sa_sigaction field specifies a signal-catching function

Page 32: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

32

struct sigaction(continued)

• This code segment sets the signal handler for SIGINT to mySignalHandler()

struct sigaction newAction;int status;

newAction.sa_handler = mySignalHandler; // Set the signal handlernewAction.sa_flags = 0; // No special actions

status = sigemptyset(&newAction.sa_mask);if (status == -1) perror("Failed to initialize signal set");else { status = sigaction(SIGINT, &newAction, NULL); if (status == -1) perror("Failed to install signal handler for SIGINT"); } // End else

Page 33: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

33

Signal Handler

• A signal handler is an ordinary function that returns void and has one integer parameter

• When the operating system delivers the signal, it sets the parameter to the number of the signal that was delivered

– Most signal handlers ignore this value, but it is possible to use the same signal handler for many signals

• The usefulness of signal handlers is limited by the inability to pass values to them (this has been corrected for the sa_sigaction handler)

• Two special values of the sa_handler member of the struct sigaction structure are SIG_DFL and SIG_IGN– SIG_DFL specifies that the sigaction() function should restore the default

action for the signal– SIG_IGN specifies that the process should handle the signal by ignoring it

(throwing it away)

Page 34: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

34

Example use of SIG_IGN

• The following code segment causes the process to change its new action to ignore the SIGINT signal if the default action is currently in effect for this signal

struct sigaction action;int status;

status = sigaction(SIGINT, NULL, &action);if (status == -1) perror("Failed to get old handler information for SIGINT");else if (action.sa_handler == SIG_DFL) { action.sa_handler = SIG_IGN; status = sigaction(SIGINT, &action, NULL); if (status == -1) perror( "Failed to set SIGINT signal handler to ignore signal"); } // End else

Page 35: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

35

Example SIGINT Signal Handler#include <signal.h>#include <stdio.h>#include <unistd.h>#define MAX_PRESSES 5

static int pressCount = 0;

void catchCtrlC(int signalNbr);

int main(void){// See next slide}

// This function is the signal handlervoid catchCtrlC(int signalNbr){char message[] = "Ctrl-C was pressed\n";

write(STDERR_FILENO, message, strlen(message) );pressCount++; // Global variable} // End catchCtrlC

The catchCtrlC() function is defined here asa signal handler for the SIGINT signal generatedby Ctrl-C.

The write() function is used instead offprintf() because POSIX guarantees thatit is async-signal safe, meaning that thefunction can be called safely from withina signal handler.

(More on next slide)

Page 36: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

36

Example SIGINT Signal Handler(continued)

// *****************************************int main(void) {struct sigaction action;int status;

action.sa_handler = catchCtrlC;action.sa_flags = 0;status = sigemptyset(&action.sa_mask);if (status == -1) { perror("Failed to initialize signal set"); exit(1); } // End ifstatus = sigaction(SIGINT, &action, NULL);if (status == -1) { perror("Failed to set signal handler for SIGINT"); exit(1); } // End if

while (pressCount < MAX_PRESSES); // Loop has no statements return 0;} // End main

uxb3% a.outCtrl-C was pressedCtrl-C was pressedCtrl-C was pressedCtrl-C was pressedCtrl-C was pressed

uxb3%

Sample Output

Page 37: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

8.5 Waiting for Signals

Page 38: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

38

Waiting for Signals

• 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 through the use of 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 pause(), sigsuspend(), and sigwait() functions provide three mechanisms for suspending a process until a signal occurs

Page 39: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

39

pause() Function• The pause() function suspends the calling thread until the delivery of a

signal whose action is either to execute a user-defined signal handler or to terminate the process

#include <unistd.h>

int pause(void);

• 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• 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(), a program must determine which signal caused pause() to return

– This information is not directly available, so the signal handler must set a global flag for the program to check after pause() returns

Page 40: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

40

Example of pause() and SIGINT Signal Handler

int main(void) {struct sigaction action;int status;

action.sa_handler = catchCtrlC;action.sa_flags = 0;status = sigemptyset(&action.sa_mask);if (status == -1) { perror("Failed to initialize signal set"); exit(1); } // End ifstatus = sigaction(SIGINT, &action, NULL);if (status == -1) { perror("Failed to set signal handler for SIGINT"); exit(1); } // End if

fprintf(stderr, "Program paused . . .\n");pause();return 0;} // End main

uxb3% a.outProgram paused . . .Ctrl-C was presseduxb3%

Sample Output

Page 41: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

41

Sequence Problems with the pause() Function

• 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

static sig_atomic_t sigreceived = 0;

. . .while (sigreceived == 0) pause();

• What happens if a signal occurs after the test of sigreceived but before pause()?

– The pause() function would not return at that time, but would wait until some other signal or the occurrence of the same signal was delivered again to the process

• The delivery of a signal before the pause() function was called in a program was one of the major problems with the original UNIX signals

– There was no simple, reliable way to get around the problem

• The program must do two operations at once: unblock the signal and call the pause() function

– In other words, the two operations need to be atomic (i.e., logically viewed as one instruction)

• The sigsuspend() function provides a method of achieving this

Page 42: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

42

sigsuspend() Function• The sigsuspend() function replaces the process signal mask with the set

of signals pointed to by the signalMask argument and suspends the process until delivery of a signal whose action is either to execute a signal handler function or to terminate the process.

#include <signal.h>

int sigsuspend(const sigset_t *signalMask);

• The sigsuspend() function returns when the signal handler of the caught signal returns

• The signalMask parameter can be used to unblock the signal that the program is looking for

• When sigsuspend() returns, the signal mask is reset to the value it had before the sigsuspend() function was called

• The sigsuspend() function always returns –1 and sets errno

Page 43: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

43

Example use of sigsuspend()

• The program on the next slide shows a way for a process to wait for a single signal

• Assume that a signal handler has been set up for the sigNbr signal and that the signal handler sets sigReceived to 1

• Error checking has been omitted from the code for clarity purposes• The maskAll structure contains all signals• The maskMost structure contains all signals except sigNbr• No signals can be caught between the testing of sigReceived and the

suspension of the process, since the signal is blocked at this point• The process signal mask had the value contained in maskMost while the

process is suspended, so only sigNbr is not blocked• When sigsuspend() returns, the signal must have been received

Page 44: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

44

Example use of sigsuspend()(continued)

#include <stdio.h>#include <signal.h>

static sig_atomic_t sigReceived = 0;

int main(void){int status;sigset_t maskAll;sigset_t maskMost;sigset_t maskOld;int sigNbr = SIGUSR1;

fprintf(stderr, "PID: %d\n", getpid()); sigfillset(&maskAll);sigfillset(&maskMost);sigdelset(&maskMost, sigNbr);sigprocmask(SIG_SETMASK, &maskAll, &maskOld);if (sigReceived == 0) sigsuspend(&maskMost);sigprocmask(SIG_SETMASK, &maskOld, NULL);return 0;} // End main

uxb3% a.outPID 2624

uxb3% a.outPID 3548

uxb3%

Command Shell A

uxb3% kill –INT 2624

uxb3%uxb3% kill –USR1 2624

uxb3% kill –9 3548

uxb3%

Command Shell B

Page 45: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

45

sigwait() Function• The sigwait() function blocks until any of the signals in the structure pointed to

by signalMask becomes pending– It then removes that signal from the set of pending signals and unblocks itself

#include <signal.h>

int sigwait(const sigset_t *signalMask, int *signalNbr);

• When sigwait() returns, the number of the signal that was removed from the pending signals is stored in the location pointed to by signalNbr

• If successful, sigwait() returns zero; otherwise, it returns –1 and sets errno• Note the difference between sigwait() and sigsuspend()

– Both functions have a first parameter that is a pointer to a signal set– For sigsuspend(), this signal 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 signal set 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(), the sigwait() function does not change the process signal

mask– The signals in signalMask should be blocked before sigwait() is called

Page 46: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

46

Example use of sigwait()

#include <signal.h>#include <stdio.h>

int main(void){int signalCount = 0;int signalNbr;sigset_t signalSet;int status;

status = sigfillset(&signalSet);if (status == -1) perror("Failed to fill the signal set");

status = sigprocmask(SIG_BLOCK, &signalSet, NULL);if (status == -1) perror("Failed to block signals");

fprintf(stderr, "This process has ID %ld\n", (long)getpid());

(More on next slide)

Page 47: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

47

Example use of sigwait()(continued)

for ( ; ; ) // Infinite loop { signalNbr = sigwait(&signalSet); // Default version if (signalNbr == -1) { perror("Failed to wait using sigwait"); return 1; } // End if else fprintf(stderr, "Unblocked signal #%d\n", signalNbr); /* status = sigwait(&signalSet, &signalNbr); // POSIX version if (status == -1) { perror("Failed to wait using sigwait"); return 1; } // End if */ signalCount++; fprintf(stderr, "Number of signals so far: %d\n", signalCount); } // End for} // End main

Page 48: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

48

Example use of sigwait(): Sample Output

uxb3% a.outThis process has ID 15469Unblocked signal #16Number of signals so far: 1Unblocked signal #17Number of signals so far: 2Unblocked signal #2Number of signals so far: 3Unblocked signal #14Number of signals so far: 4Unblocked signal #16Number of signals so far: 5Unblocked signal #2Number of signals so far: 6Killeduxb3%

uxb3% kill -USR1 15469uxb3% kill -USR2 15469uxb3% kill -INT 15469uxb3% kill -ALRM 15469uxb3% kill -USR1 15469uxb3% kill -INT 15469 uxb3% kill -KILL 15469uxb3%

Command Shell A Command Shell B

Page 49: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

8.6 Handling Signals: Errors and Async-signal Safety

Page 50: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

50

Signals and Function Calls

• Three difficulties can occur when signals interact with function calls– The first problem concerns whether POSIX functions that are interrupted by signals

should be restarted– The second problem occurs when signal handlers call non-reentrant functions– The third problem involves the handling of errors that use errno

• What happens when a process catches a signal while it is executing a library function?

– The answer depends on the type of call

• Terminal I/O can block the process for an undetermined length of time– There is no limit on how long it takes to get a key value from a keyboard or to read

from a pipe– Function calls that perform such operations are sometimes characterized as slow

• Other operations, such as disk I/O, can block for short periods of time; still others, such as getpid(), do not block at all

– Neither of these operations is considered to be slow

Page 51: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

51

Slow POSIX Calls

• The slow POSIX calls are the ones that are interrupted by signals

• They return when a signal is caught and the signal handler returns

• The interrupted function returns –1 with errno set to EINTR– If a function sets errno and one of the possible values is EINTR, then the function

can be interrupted

• The program must handle this error explicitly and restart the system call if desired

• It was originally thought that the operating system needs to interrupt slow calls to allow the user the option of canceling a blocked call

• This traditional treatment of handling blocked functions has been found to add unneeded complexity to many programs, and it is not used in newly-created POSIX functions

Page 52: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

52

Async-signal Safe• A function is async-signal safe if it can be safely called from within a signal

handler• Many POSIX library functions are not async-signal safe because they use

static data structures, call malloc() or free(), or use global data structures in a non-reentrant way

• Consequently, a single process might not correctly execute concurrent calls to these functions

• Normally this is not a problem in a program with no signal handlers, but signals add concurrency to a program

• Since signals occur asynchronously, a process may catch a signal while it is executing a library function

– Therefore, programmers must be careful when calling library functions from inside signal handlers

• The next slide lists many of the POSIX functions that are safe to call from within a signal handler

– Notice that functions such as scanf(), fscanf(), printf(), and fprintf() from the C standard I/O library are not on the list

Page 53: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

53

Some Async-signal Safe Functions

sleep

sigsuspend

sigprocmask

sigismember

sigfillset

sigdelset

sigaddset

sigaction

rmdir

read

write

waitpid

wait

unlink

time

stat

raisegetpid

pipefstat

pausefork

openexecve

mkfifoexecle

mkdirclose

lstatchown

linkchmod

killchdir

getppidalarm

Page 54: Chapter 8 Signals Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.

54

Useful Rules for Signal Handling

• When in doubt that a function is async-signal safe or not, explicitly restart library function calls within a program

• Check each library function used in a signal handler to make sure that it is on the list of async-signal safe functions

• Carefully analyze the potential interactions between a signal handler that changes an external variable and other program code that accesses the variable

– Block signals to prevent unwanted interactions

• As a general rule, signal handlers should save and restore errno if they call functions that might change the value of errno