600 likes | 1.03k Views
Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003. Chapter 8 Signals. 8.1 Basic Signal Concepts. 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
E N D
Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003. Chapter 8Signals
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
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()
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
SIGNAL NBR DESCRIPTION DEFAULT ACTION SIGABRT 6 Process abort (sent by assert) Implementation dependent SIGALRM 14 alarm clock Abnormal termination SIGCONT 25 Execution continued if stopped Continue SIGFPE 8 Arithmetic error (i.e., divide by zero) Implementation dependent SIGINT 2 Interactive attention signal (Ctrl-C) Abnormal termination SIGKILL 9 Termination (cannot be caught or ignored) Abnormal termination SIGQUIT 3 Interactive termination; core dump Implementation dependent SIGSEGV 11 Segmentation fault Abnormal termination SIGSTOP 23 Execution stop (cannot be caught or ignored) Stop SIGTERM 15 Termination Abnormal termination SIGUSR1 16 User-defined signal 1 Abnormal termination SIGUSR2 17 User-defined signal 2 Abnormal termination Certain POSIX Signals
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 operandkill [-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 usagekill 3425kill –9 896kill –KILL 3425kill –INT 9328kill –USR1 567
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() functionint status;pid_t childPid;. . .status = kill(childPid, SIGUSR1);if (status == -1) perror("Failed to send the SIGUSR1 signal to child");
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() functionint status;status = raise(SIGUSR1);if (status != 0) perror("Failed to raise SIGUSR1");
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
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 belowassertion “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
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)
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
Sample Outputs ( with assert ) uxb3% a.out Enter the number of scores (2 - 5): 3 Enter score #1 (x >= 0): 89 Enter score #2 (x >= 0): 72 Enter score #3 (x >= 0): 84 List of Scores: 89 72 84 Average score: 81.67 uxb3% uxb3% a.out Enter the number of scores (2 - 5): 1 assert-demo.c:25: failed assertion `(nbrOfScores >= MIN_SCORES) && (nbrOfScores <= MAX_SCORES)' Abort uxb3% uxb3% a.out Enter the number of scores (2 - 5): 4 Enter score #1 (x >= 0): -1 assert-demo.c:32: failed assertion `(score >= 0) && (score < INT_MAX)' Abort uxb3%
Sample Output ( with no assert ) uxb2% a.out Enter the number of scores (2 - 5): 10 Enter score #1 (x >= 0): -5 Enter score #2 (x >= 0): -10 Enter score #3 (x >= 0): -15 Enter score #4 (x >= 0): -20 Enter score #5 (x >= 0): -25 Enter score #6 (x >= 0): 5 Enter score #7 (x >= 0): 10 Enter score #8 (x >= 0): 15 Enter score #9 (x >= 0): 20 Enter score #10 (x >= 0): 25 Enter 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.27 uxb2% Notice the amount of incorrect input and output that is not flagged nor pointed out as wrong
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)
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
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
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
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)
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 if status = sigaddset(&intMask, SIGINT); if (status == -1) { perror("Failed to add SIGINT to the signal set"); return 1; } // End if (More on next slide)
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
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)
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 if else 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)
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
Example #2: Sample Output uxb2% 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 6 drwx------ 12 jjt107 faculty 1024 Jun 21 10:35 Files drwx------ 2 jjt107 faculty 1024 Jul 24 08:43 Mail drwx--x--x 19 jjt107 faculty 1024 Jul 3 12:32 http Parent has detected termination of child process uxb2%
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
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
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 actionsstatus = 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
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)
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 signalstruct 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
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 handler void 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 as a signal handler for the SIGINT signal generated by 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 within a signal handler. (More on next slide)
Example SIGINT Signal Handler(continued) Sample Output // ***************************************** 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 if status = 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.out Ctrl-C was pressed Ctrl-C was pressed Ctrl-C was pressed Ctrl-C was pressed Ctrl-C was pressed uxb3%
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
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
Example of pause() and SIGINT Signal Handler Sample Output 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 if status = 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.out Program paused . . . Ctrl-C was pressed uxb3%
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 1static 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
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
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
Example use of sigsuspend()(continued) Command Shell A #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.out PID 2624 uxb3% a.out PID 3548 uxb3% Command Shell B uxb3% kill –INT 2624 uxb3% uxb3% kill –USR1 2624 uxb3% kill –9 3548 uxb3%
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
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)
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
Example use of sigwait(): Sample Output Command Shell A Command Shell B uxb3% a.out This process has ID 15469 Unblocked signal #16 Number of signals so far: 1 Unblocked signal #17 Number of signals so far: 2 Unblocked signal #2 Number of signals so far: 3 Unblocked signal #14 Number of signals so far: 4 Unblocked signal #16 Number of signals so far: 5 Unblocked signal #2 Number of signals so far: 6 Killed uxb3% uxb3% kill -USR1 15469 uxb3% kill -USR2 15469 uxb3% kill -INT 15469 uxb3% kill -ALRM 15469 uxb3% kill -USR1 15469 uxb3% kill -INT 15469 uxb3% kill -KILL 15469 uxb3%
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