Download presentation
Presentation is loading. Please wait.
Published bySuryadi Indradjaja Modified over 6 years ago
1
CS 3733 Operating Systems Topics: Signals (USP Chapter 8.1-8.6)
Instructor: Dr. Turgay Korkmaz Department Computer Science The University of Texas at San Antonio Office: NPB 3.330 Phone: (210) Fax: (210) web: These slides are prepared based on the materials provided by Drs. Robbins, Zhu, and Liu. Thanks to all.
2
Department of Computer Science @ UTSA
Outline Overview of signals Concepts and general terminology POSIX required signals Signal generations: kill vs. raise Block signals and process signal masks Signal handles: catch vs. ignore Signals Wait signals: pause, sigsuspend and sigwait Errors and async-signal safety Department of Computer UTSA
3
Motivation How would you stop this program?
while(n=5){ // vs. n==5 printf(“n is %d\n”, n--); ... } Motivation How would you stop this program? Did you ever pres Ctrl-C when running a program? Did you ever use > kill -9 PID If yes, can you explain what is happening? We send a SIGNAL to a process… A signal is a software notification to a process of an event. Accordingly, the process takes an action (e.g., quit). Is this a SYNCHRONOUS or an ASYNCHRONOUS event?
4
Signal Concept and Terminology
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 delivery. A signal that has been generated but not yet delivered is pending. A process catches a signal if it executes a signal handler when the signal is delivered. Alternatively, a process can ignore a signal when it is delivered, that is to take no action. When will a signal be delivered to a process? Immediately? NO Signal: Software notification to a process of an event Generate a signal: when associated event occurs Deliver a signal: when the process takes actions Signal lifetime: time from generation to delivery Signals vs. processes Signals can be blocked by a process: not all signals Signal mask of a process: a list of signals to be blocked A generated signal is pending before it is delivered A signal is ignored if delivered but no action is taken Signal handler - function
5
Signal Concept and Terminology (cont’d)
The function sigaction is used to specify what is to happen to a signal when it is delivered. Each signal has a default action which is usually to terminate the process. The function sigprocmask is used to modify the signal mask. The signal mask determines the action to be taken when the signal is generated. It contains a list of signals to be blocked. A blocked signal is not delivered to a process until it is unblocked. S i gn a l Ma s k A c t o n 1 2 3 4 A process executes a signal handler When signal is delivered catch the signal Function to set up signal handlers for a process Function to modify signal mask for a process A blocked signal is not delivered until it is unblocked by resetting a process’ signal mask Some signals may NOT be blocked Each signal has a default action Normally terminate the process
6
POSIX Required Signals: Table 8.1
7
Generate Signals: Command Line
The kill command Send a signal to a process from console The usage of kill kill –l : list the signals the system understands kill [–signal] pid: send a signal to a process (pid) The default is SIGTERM Example: to unconditionally kill a process kill -9 pid kill –KILL pid
8
Generate Signals: Running Program
The kill system call #include <sys/types.h> #include <signal.h> int kill(pid_t pid, int sig); Example: send SIGUSR1 to process 3423 if (kill(3423, SIGUSR1) == -1) perror("Failed to send SIGUSR1 signal"); Normally, do NOT specify pid as a number Use getpid() or getppid()
9
Generate Signals: Running Program (cont.)
Example: a child process kills its parent if (kill(getppid(), SIGTERM) == -1) perror ("Failed to kill parent"); The raise system call: send a signal to itself if (raise(SIGUSR1) != 0) perror("Failed to raise SIGUSR1"); What do you think the following program do? int main(void) { alarm(10); for ( ; ; ) ; } After 10 seconds, it gets SIGALRM, which (by default) kills the process…
10
Signal Mask and Signal Sets
How do you deal with a set of signals? How can we prevent a signal from being delivered?
11
Signal Mask and Signal Sets
Each process has a signal mask Defines the set of signals to be BLOCKED! The set of signals: type sigset_t (originally was int, one bit per signal) Routines to handle signal sets (compare to select which handles FDs) #include <signal.h> int sigemptyset( sigset_t *set); int sigfillset( sigset_t *set); int sigaddset( sigset_t *set, int signo); int sigdelset( sigset_t *set, int signo); int sigismember(const sigset_t *set, int signo); initializes the set to contain no signals puts all signals in the set adds one signal to the set removes one signal from the set tests to see if a signal is in the set if((sigemptyset(&twosigs) == -1) || (sigaddset(&twosigs, SIGINT) == -1) || (sigaddset(&twosigs, SIGQUIT) == -1)) perror("Failed to set up signal mask");
12
Sys. Call to Modify Signal Mask: sigprocmask
The system call: sigprocmask int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset); how: an integer specifying how the signal mask is to be modified: SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK set: a pointer to a signal set to be used in the modification. If set is NULL, no modification is made. oset: If oset is not NULL, the sigprocmask returns in *oset the signal set before the modification. Example: initialize a signal set and set mask sigset_t newsigset; if ((sigemptyset(&newsigset) == -1) || (sigaddset(&newsigset, SIGINT) == -1)) perror("Failed to initialize signal set"); else if (sigprocmask(SIG_BLOCK, &newsigset,NULL) == -1) perror("Failed to block SIGINT”); Some signals (e.g., SIGSTOP and SIGKILL) cannot be blocked.
13
Example: Block/Unblock SIGINT
Alternating block/unblock SIGINT Ctrl+C: will not return immediately if in block section! Ctrl+C: will return immediately if in unblock section!
14
Example: block a signal while creating two pipes, then restore old one
#include <errno.h> #include <signal.h> #include <unistd.h> #include <sys/stat.h> #define R_MODE (S_IRUSR | S_IRGRP | S_IROTH) #define W_MODE (S_IWUSR | S_IWGRP | S_IWOTH) #define RW_MODE (R_MODE | W_MODE) Example: block a signal while creating two pipes, then restore old one int makepair(char *pipe1, char *pipe2) { sigset_t blockmask; sigset_t oldmask; int returncode = 0; if(sigfillset(&blockmask) == -1) return -1; if(sigprocmask(SIG_SETMASK, &blockmask, &oldmask) == -1)return -1; if(((mkfifo(pipe1, RW_MODE) == -1) && (errno != EEXIST)) || ((mkfifo(pipe2, RW_MODE) == -1) && (errno != EEXIST))) { returncode = errno; unlink(pipe1); unlink(pipe2); } if((sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1) && !returncode) if(returncode) { errno = returncode; return -1; return 0;
15
Example: Block Signals & fork: child inherit mask
mask contains ALL signals! No interruption during child’s execution!
16
Catching and Ignoring Signals: sigaction
Let caller to examine or specify the action associated with a specific signal.
17
Set up signal handler: sigaction
int sigaction(int signo, struct sigaction *act, struct sigaction *oact); signo: specifies the signal number for the action. act: a pointer to a struct sigaction structure that specifies the action to be taken. oact: a pointer to a struct sigaction structure that receives the previous action associated with the signal. If act is NULL, do not change the action associated with the signal. Either act or oact may be NULL.
18
struct sigaction structure
void (*sa_handler)(int); /* SIG_DFL, SIG_IGN or pointer to function (no return value) */ sigset_t sa_mask; /* additional signals to be blocked */ int sa_flags; /* special flags and options */ void (*sa_sigaction) (int, siginfo_t *, void *); /* realtime handler */ }; SIG_DFL: restore the default action for the signal. SIG_IGN: handle the signal by ignoring it (throwing it away). Pointer to a user defined function (signal 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, sa_handler specifies the action to be taken. If the SA_SIGINFO flag of the sa_flags field is set, sa_sigaction field specifies a signal-catching function. 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. SIG_IGN ignore the signal after delivery!
19
Example: new handler for SIGINT Set up a signal handler that catches the SIGINT signal generated by Ctrl-C. How can we reset SIGINT to the default handler or ignore it? act.sa_handler=SIG_DFL; act.sa_handler=SIG_IGN; // and use the same procedure 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 How about ignoring SIGINT if the default action is in effect for this signal? (next slide)
20
Example: 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"); }
21
Exercise: A program that terminates gracefully on ctrl-C
Write a program that terminates gracefully on ctrl-C while(1) { // doSomething() } Print a msg that Program ends gracefully…
22
Wait for Signals Signals provide a method for waiting for an event without busy waiting. int pause(void); int sigsuspend(const sigset_t *sigmask); int sigwait(sigset_t *restrict sigmask, int *restrict signo);
23
Wait for Signal: pause()
The usage of pause() #include <unistd.h> int pause(void); Always return -1; if interrupted, set errno as EINTR However, no information about which signal causes it return! Can we use pause() to wait for a particular signal? How to know which signal? The signal handler of the wanted signal sets a flag After return from pause(), check the flag
24
Wait for Signal: pause() – cont.
void catch_signal(int signo){ sigreceived = 1; } //handler } … static volatile sig_atomic_t sigreceived = 0; while(sigreceived == 0) pause(); What if the desired signal occurs between condition check and pause()? -- wait for another occurrence again or other signal! We need to block the signal when checking the condition!
25
Wait for Signal: pause() – cont. What about this solution?
static volatile sig_atomic_t sigreceived = 0; int signum; sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, signum); //desired signal sigprocmask(SIG_BLOCK, &sigset, NULL); while(sigreceived == 0) pause(); It executes pause while the signal is blocked, so the program never receives the signal and pause never returns! We need to atomically unblock the signal and suspend the process after checking the condition!
26
The sigsuspend() Function Suspends the process until a signal is received We will use it to unblock the signal and pause the process in an atomic manner #include <signal.h> int sigsuspend(const sigset_t *sigmask); Sets the signal mask to the one pointed to by sigmask and suspends the process until a signal is caught (in atomic manner). It returns when the signal handler of the caught signal returns. This function always returns -1. Also the signal mask is restored to what it was before this function was called.
27
A wrong way to wait for a signal: wait for SIGUSR1
sigfillset(&sigmost); sigdelset(&sigmost, SIGUSR1); while(sigreceived == 0) sigsuspend(&sigmost); The sigmost signal set contains all signals except the one to wait for. When the process suspends, only the signal SIGUSR1 is unblocked and so it seems that only this signal can cause sigsuspend to return. But, it has the same problem as pause. If the signal is delivered before the sigsuspend call, the process still suspends itself and deadlocks if another SIGUSR1 signal is not generated.
28
How to use sigsuspend() wait for a signal
Have an handler to set a flag for the signal (e.g. signo) Block the signal with signo Create a sigmask without signo while(flag == 0) sigsuspend(sigmask); Signal signo is blocked until sigsuspend is called Sigsuspend unblocks the signal signo and suspends When the signal is caught, the handler will be executed. Then sigsuspend returns and restores the previous mask (i.e., block the signal with signo again)
29
A correct way to wait for a signal: wait for SIGUSR1
static volatile sig_atomic_t sigreceived = 0; void catch_signal(int signo){ sigreceived = 1; } sigset_t blockmask, sigmask; sigemptyset(&blockmask); sigemptyset(&sigmask); sigaddset(&blockmask, SIGUSR1); sigprocmask(SIG_BLOCK, &blockmask, NULL); while(sigreceived == 0) sigsuspend(&sigmask); From web: Function: int sigsuspend (const sigset_t *set) This function replaces the process's signal mask with set and then suspends the process until a signal is delivered whose action is either to terminate the process or invoke a signal handling function. In other words, the program is effectively suspended until one of the signals that is not a member of set arrives. If the process is woken up by delivery of a signal that invokes a handler function, and the handler function returns, then sigsuspend also returns. The mask remains set only as long as sigsuspend is waiting. The function sigsuspend always restores the previous signal mask when it returns. The return value and error conditions are the same as for pause. With sigsuspend, you can replace the pause or sleep loop in the previous section with something completely reliable: sigset_t mask, oldmask; ... /* Set up the mask of signals to temporarily block. */ sigemptyset (&mask); sigaddset (&mask, SIGUSR1); ... /* Wait for a signal to arrive. */ sigprocmask (SIG_BLOCK, &mask, &oldmask); while (!usr_interrupt) sigsuspend (&oldmask); sigprocmask (SIG_UNBLOCK, &mask, NULL);This last piece of code is a little tricky. The key point to remember here is that when sigsuspend returns, it resets the process's signal mask to the original value, the value from before the call to sigsuspend—in this case, the SIGUSR1 signal is once again blocked. The second call to sigprocmask is necessary to explicitly unblock this signal. One other point: you may be wondering why the while loop is necessary at all, since the program is apparently only waiting for one SIGUSR1 signal. The answer is that the mask passed to sigsuspend permits the process to be woken up by the delivery of other kinds of signals, as well—for example, job control signals. If the process is woken up by a signal that doesn't set usr_interrupt, it just suspends itself again until the “right” kind of signal eventually arrives. This technique takes a few more lines of preparation, but that is needed just once for each kind of wait criterion you want to use. The code that actually waits is just four lines. Sets the signal mask to the one in sigmask (i.e., unblocks SIGUSR1 and suspends the process until a signal happens. When it returns the signal mask is restored to what it was before it was called.
30
Robust version A correct way to wait for a signal while allowing other signals to be handled: 3. sigset_t maskblocked, maskold, maskunblocked; 4. int signum = SIGUSR1; 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); Instead of blocking all signals and then unblocking only signum, this example (8.25) does not change the other signals in the signal mask. 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
31
The sigwait() Function: An alternative way to wait for signals
int sigwait(const sigset_t *restrict sigmask, int *restrict signo); Block all signals that we want to wait for Put the signals you want to wait for in a sigset_t call sigwait, which blocks the process until at least one of these signals is pending. It removes one of the pending signals and gives you the corresponding signal number in the second parameter. Do what you want: no signal handler needed. It returns 0 on success and -1 on error with errno set How is this different than sigsuspend? The sigwait 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.
32
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.
33
Count Signals First, block the signal Wait only for the signal!
34
Issues in Handling Signals… Errors and async-signal safety
Three issues in handling signals: Certain blocking system calls (e.g., read) return -1 and set errno to EINTR if a signal is caught while the function is blocked. Error handling in signal handlers Async-signal safety
35
Signals and blocking system calls
Certain blocking system calls will return -1 with errno set to EINTR if a signal is caught while the function is blocked. Check the man page to see if a system call can set errno to EINTR. If this happens, you should usually restart the function since a real error has not occurred. The restart library handles this for many of the most important system calls. Look at the functions in the restart library.
36
Handling errors in signal handlers
If you use a function that can modify errno in a signal handler, you must make sure that it does not interfere with error handling in the main part of the program. There is only one errno and it is used by both the main program and by the signal handler. Solution: in the signal handler, save and restore errno. void myhandler(int signo) { int esaved; esaved = errno; write(STDOUT_FILENO, "Got a signal\n", 13); errno = esaved; } Error handling in signal handlers Only one errno! If a function in signal handler modify errno, should not interfere with main handler
37
Async-signal safety. Only certain system calls and library functions may be used safely in a signal handler. The strtok function is an example of a function that might have a problem. In fact, the POSIX standard only guarantees that a small number of functions are async-signal safe, that is safe to use in a signal handler and the main program. Other functions may be async-signal safe in some implementations. Almost none of the functions in the standard C library are on the list. Async-signal safety Only certain system calls and library functions can be safely used in signal handlers OK: write(); read() etc. NOT: strtok(); almost all standard functions in C library
38
Functions that POSIX guarantees to be async-signal safe.
_Exit execve lseek sendto stat _exit fchown lstat setgid symlink accept fcntl mkdir setpgid sysconf access fdatasync mkfifo setsid tcdrain aio_error fork open setsockopt tcflow aio_return fpathconf pathconf setuid tcflush aio_suspend fstat pause shutdown tcgetattr alarm fsync pipe sigaction tcgetpgrp bind ftruncate poll sigaddset tcsendbreak cfgetispeed getegid posix_trace_event sigdelset tcsetattr cfgetospeed geteuid pselect sigemptyset tcsetpgrp cfsetispeed getgid raise sigfillset time cfsetospeed getgroups read sigismember timer_getoverrun chdir getpeername readlink signal timer_gettime chmod getpgrp recv sigpause timer_settime chown getpid recvfrom sigpending times clock_gettime getppid recvmsg sigprocmask umask close getsockname rename sigqueue uname connect getsockopt rmdir sigset unlink creat getuid select sigsuspend utime dup kill sem_post sleep wait dup2 link send socket waitpid execle listen sendmsg socketpair write
39
How do you handle signals in a threaded environment?
Signals and Threads How do you handle signals in a threaded environment?
40
Handling signals in a multithreaded environment
If a signal is sent to a threaded program, any of the threads can handle the signal. Each thread inherits the process signal mask, but each thread has its own signal mask that can be modified with pthread_sigmask. sigprocmask should not be used in a threaded environment, but it can be used before the threads are created. The simplest way to handle signals in a multithreaded environment is to have a thread dedicated to signal handling.
41
Handling signals in a multithreaded environment - cont
Issues involving signal safety can be handled by using sigwait: The main process blocks all signals before creating any threads. No signal handlers are set up. A thread is created to handle the signals. That thread sets up a sigset_t containing the signals of interest. It loops, calling sigwait and handles the pending signals.
42
EXTRAS….
43
Real Time Signals (USP 9.4)
sigaction void(*sa_sigaction) (int, siginfo_t *, void *); /* realtime handler */ When SA_SIGINFO flag is set sa_sigaction specifies the action to be taken A new type of signal handler: take 3 parameters Signals for this type of signal handlers are queued Use sigqueue instead of kill to send one of these signals int sigqueue(pid_t pid, int signo, const union sigval value);
44
Set Up a Real Time Signal Handler
45
An Example: Send a queue signal
Similar presentations
© 2024 SlidePlayer.com. Inc.
All rights reserved.