10.2. Signal Concepts
It is considered bad form for the kernel to include header files meant for user-level applications, so if the applications and the kernel both need the same definitions, the information is placed in a kernel header file that is then included by the user-level header file. Linux 2.4.22 defines the signals in <bits/signum.h> No signal has a signal number of 0. We'll see in Section 10.9 that the kill function uses the signal number of 0 for a special case. POSIX.1 calls this value the null signal. The terminal-generated signals occur when users press certain terminal keys. Hardware exceptions generate signals: divide by 0, invalid memory reference, and the like. The kill(2) function allows a process to send any signal to another process or process group.we have to be the owner of the process that we're sending the signal to, or we have to be the superuser. The kill(1) command allows us to send signals to other processes. Software conditions can generate signals when something happens about which the process should be notified. We can tell the kernel to do one of three things when a signal occurs. We call this the disposition of the signal, or the action associated with a signal. Ignore the signal. Two signals can never be ignored: SIGKILL and SIGSTOP. Catch the signal. Note that the two signals SIGKILL and SIGSTOP can't be caught. Let the default action apply.
We now describe each of these signals in more detail.
SIGABRT | This signal is generated by calling the abort function (Section 10.17). The process terminates abnormally. |
SIGALRM | This signal is generated when a timer set with the alarm function expires (see Section 10.10 for more details). This signal is also generated when an interval timer set by the setitimer(2) function expires. |
SIGBUS | This indicates an implementation-defined hardware fault. Implementations usually generate this signal on certain types of memory faults, as we describe in Section 14.9. |
SIGCHLD | Whenever a process terminates or stops, the SIGCHLD signal is sent to the parent. By default, this signal is ignored, so the parent must catch this signal if it wants to be notified whenever a child's status changes. The normal action in the signal-catching function is to call one of the wait functions to fetch the child's process ID and termination status. The same as SIGCLD |
SIGCONT | This job-control signal is sent to a stopped process when it is continued. The default action is to continue a stopped process, but to ignore the signal if the process wasn't stopped. A full-screen editor, for example, might catch this signal and use the signal handler to make a note to redraw the terminal screen. See Section 10.20 for additional details. |
SIGEMT | This indicates an implementation-defined hardware fault. The name EMT comes from the PDP-11 "emulator trap" instruction. Not all platforms support this signal. On Linux, for example, SIGEMT is supported only for selected architectures, such as SPARC, MIPS, and PA-RISC. |
This signals an arithmetic exception, such as divide by 0, floating-point overflow, and so on. | |
SIGHUP | This signal is sent to the controlling process (session leader) associated with a controlling terminal if a disconnect is detected by the terminal interface. This signal is generated for this condition only if the terminal's CLOCAL flag is not set. (The CLOCAL flag for a terminal is set if the attached terminal is local. The flag tells the terminal driver to ignore all modem status lines. We describe how to set this flag in Chapter 18.) Note that the session leader that receives this signal may be in the background; see Figure 9.7 for an example. This differs from the normal terminal-generated signals (interrupt, quit, and suspend), which are always delivered to the foreground process group. This signal is also generated if the session leader terminates. In this case, the signal is sent to each process in the foreground process group. This signal is commonly used to notify daemon processes (Chapter 13) to reread their configuration files. The reason SIGHUP is chosen for this is that a daemon should not have a controlling terminal and would normally never receive this signal. |
SIGILL | This signal indicates that the process has executed an illegal hardware instruction. 4.3BSD generated this signal from the abort function. SIGABRT is now used for this. |
SIGINT | This signal is generated by the terminal driver when we type the interrupt key (often DELETE or Control-C). This signal is sent to all processes in the foreground process group (refer to Figure 9.8). This signal is often used to terminate a runaway program, especially when it's generating a lot of unwanted output on the screen. |
SIGIO | This signal indicates an asynchronous I/O event. We discuss it in Section 14.6.2. In Figure 10.1, we labeled the default action for SIGIO as either "terminate" or "ignore." Unfortunately, the default depends on the system. Under System V, SIGIO is identical to SIGPOLL, so its default action is to terminate the process. Under BSD, the default is to ignore the signal. Linux 2.4.22 and Solaris 9 define SIGIO to be the same value as SIGPOLL, so the default behavior is to terminate the process. |
SIGIOT | This indicates an implementation-defined hardware fault. On FreeBSD 5.2.1, Linux 2.4.22, Mac OS X 10.3, and Solaris 9, SIGIOT is defined to be the same value as SIGABRT. |
SIGKILL | This signal is one of the two that can't be caught or ignored. It provides the system administrator with a sure way to kill any process. |
SIGPIPE | If we write to a pipeline but the reader has terminated, SIGPIPE is generated. We describe pipes in Section 15.2. This signal is also generated when a process writes to a socket of type SOCK_STREAM that is no longer connected. We describe sockets in Chapter 16. |
SIGPOLL | This signal can be generated when a specific event occurs on a pollable device. We describe this signal with the poll function in Section 14.5.2. SIGPOLL originated with SVR3, and loosely corresponds to the BSD SIGIO and SIGURG signals. On Linux and Solaris, SIGPOLL is defined to have the same value as SIGIO. |
SIGPROF | This signal is generated when a profiling interval timer set by the setitimer(2) function expires. |
SIGPWR | This signal is system dependent. Its main use is on a system that has an uninterruptible power supply (UPS). If power fails, the UPS takes over and the software can usually be notified. Nothing needs to be done at this point, as the system continues running on battery power. But if the battery gets low (if the power is off for an extended period), the software is usually notified again; at this point, it behooves the system to shut everything down within about 1530 seconds. This is when SIGPWR should be sent. Most systems have the process that is notified of the low-battery condition send the SIGPWR signal to the init process, and init handles the shutdown. Linux 2.4.22 and Solaris 9 have entries in the inittab file for this purpose: powerfail and powerwait (or powerokwait). In Figure 10.1, we labeled the default action for SIGPWR as either "terminate" or "ignore." Unfortunately, the default depends on the system. The default on Linux is to terminate the process. On Solaris, the signal is ignored by default. |
This signal is generated by the terminal driver when we type the terminal quit key (often Control-backslash). This signal is sent to all processes in the foreground process group (refer to Figure 9.8). This signal not only terminates the foreground process group (as does SIGINT), but also generates a core file. | |
SIGSEGV | This signal indicates that the process has made an invalid memory reference. The name SEGV stands for "segmentation violation." |
SIGSTKFLT | This signal is defined only by Linux. This signal showed up in the earliest versions of Linux, intended to be used for stack faults taken by the math coprocessor. This signal is not generated by the kernel, but remains for backward compatibility. |
SIGSTOP | This job-control signal stops a process. It is like the interactive stop signal (SIGTSTP), but SIGSTOP cannot be caught or ignored. |
SIGSYS | This signals an invalid system call. Somehow, the process executed a machine instruction that the kernel thought was a system call, but the parameter with the instruction that indicates the type of system call was invalid. This might happen if you build a program that uses a new system call and you then try to run the same binary on an older version of the operating system where the system call doesn't exist. |
SIGTERM | This is the termination signal sent by the kill(1) command by default. |
SIGTRAP | This indicates an implementation-defined hardware fault. The signal name comes from the PDP-11 TRAP instruction. Implementations often use this signal to transfer control to a debugger when a breakpoint instruction is executed. |
SIGTSTP | This interactive stop signal is generated by the terminal driver when we type the terminal suspend key (often Control-Z). This signal is sent to all processes in the foreground process group (refer to Figure 9.8). Unfortunately, the term stop has different meanings. When discussing job control and signals, we talk about stopping and continuing jobs. The terminal driver, however, has historically used the term stop to refer to stopping and starting the terminal output using the Control-S and Control-Q characters. Therefore, the terminal driver calls the character that generates the interactive stop signal the suspend character, not the stop character. |
SIGTTIN | This signal is generated by the terminal driver when a process in a background process group tries to read from its controlling terminal. (Refer to the discussion of this topic in Section 9.8.) As special cases, if either (a) the reading process is ignoring or blocking this signal or (b) the process group of the reading process is orphaned, then the signal is not generated; instead, the read operation returns an error with errno set to EIO. |
SIGTTOU | This signal is generated by the terminal driver when a process in a background process group tries to write to its controlling terminal. (Refer to the discussion of this topic in Section 9.8.) Unlike the SIGTTIN signal just described, a process has a choice of allowing background writes to the controlling terminal. We describe how to change this option in Chapter 18. If background writes are not allowed, then like the SIGTTIN signal, there are two special cases: if either (a) the writing process is ignoring or blocking this signal or (b) the process group of the writing process is orphaned, then the signal is not generated; instead, the write operation returns an error with errno set to EIO. Regardless of whether background writes are allowed, certain terminal operations (other than writing) can also generate the SIGTTOU signal: tcsetattr, tcsendbreak, tcdrain, tcflush, tcflow, and tcsetpgrp. We describe these terminal operations in Chapter 18. |
SIGURG | This signal notifies the process that an urgent condition has occurred. This signal is optionally generated when out-of-band data is received on a network connection. |
SIGUSR1 | This is a user-defined signal, for use in application programs. |
SIGUSR2 | This is another user-defined signal, similar to SIGUSR1, for use in application programs. |
SIGVTALRM | This signal is generated when a virtual interval timer set by the setitimer(2) function expires. |
SIGWAITING | This signal is used internally by the Solaris threads library, and is not available for general use. |
SIGWINCH | The kernel maintains the size of the window associated with each terminal and pseudo terminal. A process can get and set the window size with the ioctl function, which we describe in Section 18.12. If a process changes the window size from its previous value using the ioctl set-window-size command, the kernel generates the SIGWINCH signal for the foreground process group. |
SIGXCPU | The Single UNIX Specification supports the concept of resource limits as an XSI extension; refer to Section 7.11. If the process exceeds its soft CPU time limit, the SIGXCPU signal is generated. In Figure 10.1, we labeled the default action for SIGXCPU as either "terminate with a core file" or "ignore." Unfortunately, the default depends on the operating system. Linux 2.4.22 and Solaris 9 support a default action of terminate with a core file, whereas FreeBSD 5.2.1 and Mac OS X 10.3 support a default action of ignore. The Single UNIX Specification requires that the default action be to terminate the process abnormally. Whether a core file is generated is left up to the implementation. |
SIGXFSZ | This signal is generated if the process exceeds its soft file size limit; refer to Section 7.11. Just as with SIGXCPU, the default action taken with SIGXFSZ depends on the operating system. On Linux 2.4.22 and Solaris 9, the default is to terminate the process and create a core file. On FreeBSD 5.2.1 and Mac OS X 10.3, the default is to be ignored. The Single UNIX Specification requires that the default action be to terminate the process abnormally. Whether a core file is generated is left up to the implementation. |
SIGXRES | This signal is defined only by Solaris. This signal is optionally used to notify processes that have exceeded a preconfigured resource value. The Solaris resource control mechanism is a general facility for controlling the use of shared resources among independent application sets. |
10.3. signal Function
#include <signal.h> typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); use signaction instead. The signum argument is just the name of the signal. The value of handler is (a) the constant SIG_IGN, (b) the constant SIG_DFL, or (c) the address of a function to be called when the signal occurs. 一个sighandler_t 是有一个int类型参数返回值为void的回调函数。 The return value from signal is the pointer to the previous signal handler. 信号被catch一次后,会回到默认的处理方法。 Another problem with these earlier systems is that the process was unable to turn a signal off when it didn't want the signal to occur. 调用了exec后,所有信号回复到SIG_DFL void sig_int(int), sig_quit(int); if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, sig_int); if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) signal(SIGQUIT, sig_quit);
Doing this, the process catches the signal only if the signal is not currently being ignored.
Process Creation
When a process calls fork, the child inherits the parent's signal dispositions. Here, since the child starts off with a copy of the parent's memory image, the address of a signal-catching function has meaning in the child.
10.4. Unreliable Signals
Another problem with these earlier systems is that the process was unable to turn a signal off when it didn't want the signal to occur. 信号被catch一次后,会回到默认的处理方法。
10.5. Interrupted System Calls(?)
早期Unix系统有一个特性:如果系统在执行一个低速系统调用而阻塞期间捕捉到一个信号,则该系统调用就被中断不再执行。
To support this feature, the system calls are divided into two categories: the "slow" system calls and all the others. The slow system calls are those that can block forever. Included in this category are
-
Reads that can block the caller forever if data isn't present with certain file types (pipes, terminal devices, and network devices)
-
Writes that can block the caller forever if the data can't be accepted immediately by these same file types
-
Opens that block until some condition occurs on certain file types (such as an open of a terminal device that waits until an attached modem answers the phone)
-
The pause function (which by definition puts the calling process to sleep until a signal is caught) and the wait function
-
Certain ioctl operations
-
Some of the interprocess communication functions (Chapter 15)
again: if ((n = read(fd, buf, BUFFSIZE)) < 0) { if (errno == EINTR) goto again; /* just an interrupted system call */ /* handle other errors */ }
不同的系统不同的函数,对于中断系统调用是否自动重新启动又不同的标准。
10.6. Reentrant(可重复进入) Functions
可以在signal handler中被调用 Some functions can't be reentrant, because (a) they are known to use static data structures, (b) they call malloc or free, or (c) they are part of the standard I/O library. only one errno variable per thread. 当在signal handler中调用可重入函数时,应该在调用前保存errno,调用后恢复errno。
10.7. SIGCLD Semantics
SIGCLD与SIGCHLD有两点不同。
If the process specifically sets its disposition to SIG_IGN, children of the calling process will not generate zombie processes. Note that this is different from its default action (SIG_DFL), which from Figure 10.1 is to be ignored. Instead, on termination, the status of these child processes is discarded. If it subsequently calls one of the wait functions, the calling process will block until all its children have terminated, and then wait returns 1 with errno set to ECHILD. If we set the disposition of SIGCLD to be caught, the kernel immediately checks whether any child processes are ready to be waited for and, if so, calls the SIGCLD handler. 这样在一些系统中可能存在一些问题,子进程结束时,进入handler后不断的调用signal(SIGCLD,……) Linux 2.4.22 also doesn't exhibit this problem, because it doesn't call the SIGCHLD signal handler when a process arranges to catch SIGCHLD and child processes are ready to be waited for, even though SIGCLD and SIGCHLD are defined to be the same value.
10.8. Reliable-Signal Terminology and Semantics
We say that a signal is delivered to a process when the action for a signal is taken. During the time between the generation of a signal and its delivery, the signal is said to be pending.
A process has the option of blocking the delivery of a signal. If a signal that is blocked is generated for a process, and if the action for that signal is either the default action or to catch the signal, then the signal remains pending for the process until the process either (a) unblocks the signal or (b) changes the action to ignore the signal. The system determines what to do with a blocked signal when the signal is delivered, not when it's generated. This allows the process to change the action for the signal before it's delivered. The sigpending function (Section 10.13) can be called by a process to determine which signals are blocked and pending.
一个信号多次发送时,一般的系统都只保留一次。当有多个信号时,顺序也没有明确规定。 Each process has a signal mask that defines the set of signals currently blocked from delivery to that process. We can think of this mask as having one bit for each possible signal. If the bit is on for a given signal, that signal is currently blocked. A process can examine and change its current signal mask by calling sigprocmask, which we describe in Section 10.12. Usually, sigset_t.
10.9. kill and raise Functions
The kill function sends a signal to a process or a group of processes. The raise function allows a process to send a signal to itself. #include <signal.h> int kill(pid_t pid, int signo); int raise(int signo); pid == 0 The signal is sent to all processes whose process group ID equals the process group ID of the sender and for which the sender has permission to send the signal. (excludes some system processes) pid < 0 The signal is sent to all processes whose process group ID equals the absolute value of pid and for which the sender has permission to send the signal. Again, the set of all processes excludes certain system processes
pid == -1
The signal is sent to all processes on the system for which the sender has permission to send the signal.
As we've mentioned, a process needs permission to send a signal to another process. The superuser can send a signal to any process. For other users, the basic rule is that the real or effective user ID of the sender has to equal the real or effective user ID of the receiver. There is also one special case for the permission testing: if the signal being sent is SIGCONT, a process can send it to any other process in the same session. 发送0信号可以用来检测一个进程是否存在。 If the call to kill causes the signal to be generated for the calling process and if the signal is not blocked, either signo or some other pending, unblocked signal is delivered to the process before kill returns.
10.10. alarm and pause Functions
The alarm function allows us to set a timer that will expire at a specified time in the future. When the timer expires, the SIGALRM signal is generated. If we ignore or don't catch this signal, its default action is to terminate the process.
#include <unistd.h> unsigned int alarm(unsigned int seconds); Returns: 0 or number of seconds until previously set alarm 由于调度,产生SIGALRM后还有一段时间才能处理信号。 一般都捕捉这个信号然后先做一些清理工作再结束进程。 #include <unistd.h> int pause(void); Returns: -1 with errno set to EINTR //一定最终产生这个错误,见interrupted sys call 当其他的handler函数结束之后,才会发出信号结束pause 当sleep与其他的signal一块儿用的时候可能会使其他handler无法完成使pause结束。 A common use for alarm, in addition to implementing the sleep function, is to put an upper time limit on operations that can block. 使用alarm做I/O的timeout,简单版需要考虑io是否实现为中断系统调用后是否重读。 改进使用longjmp和setjmp,解决重读问题,但是与其他signal handler可能有冲突。 可选select或者poll
10.11. Signal Sets
#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); #define sigemptyset(ptr) (*(ptr) = 0) #define sigfillset(ptr) (*(ptr) = ~(sigset_t)0, 0) //确保返回值是0 #include <signal.h> #include <errno.h> /* <signal.h> usually defines NSIG to include signal number 0 */ #define SIGBAD(signo) ((signo) <= 0 || (signo) >= NSIG) int sigaddset(sigset_t *set, int signo) { if (SIGBAD(signo)) { errno = EINVAL; return(-1); } *set |= 1 << (signo - 1); /* turn bit on */ return(0); } int sigdelset(sigset_t *set, int signo) { if (SIGBAD(signo)) { errno = EINVAL; return(-1); } *set &= ~(1 << (signo - 1)); /* turn bit off */ return(0); } int sigismember(const sigset_t *set, int signo) { if (SIGBAD(signo)) { errno = EINVAL; return(-1); } return((*set & (1 << (signo - 1))) != 0); }
10.12. sigprocmask Function
#include <signal.h> int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset); If oset is a non-null pointer, the current signal mask for the process is returned through oset.
how | Description |
---|---|
SIG_BLOCK | The new signal mask for the process is the union of its current signal mask and the signal set pointed to by set. That is, set contains the additional signals that we want to block. |
SIG_UNBLOCK | The new signal mask for the process is the intersection of its current signal mask and the complement of the signal set pointed to by set. That is, set contains the signals that we want to unblock. |
SIG_SETMASK | The new signal mask for the process is replaced by the value of the signal set pointed to by set. |
Note that SIGKILL and SIGSTOP can't be blocked.
If set is a null pointer, the signal mask of the process is not changed, and how is ignored.
After calling sigprocmask, if any unblocked signals are pending, at least one of these signals is delivered to the process before sigprocmask returns.(pending中的信号在解除阻塞的函数内就会被发送) 注意在block一些sigset后,原来的sigset应该保存下来,最后使用SIG_SETMASK回复。不能SIG_UNBLOCK,因为之前可能已经也block了这些sigset。
10.13. sigpending Function
#include <signal.h> int sigpending(sigset_t *set); SIGQUIT ctrl+backslash(/)
10.14. sigaction Function
#include <signal.h> int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact); The argument signo is the signal number whose action we are examining or modifying. If the act pointer is non-null, we are modifying the action. If the oact pointer is non-null, the system returns the previous action for the signal through the oact pointer. struct sigaction { void (*sa_handler)(int); /* addr of signal handler, */ /* or SIG_IGN, or SIG_DFL */ sigset_t sa_mask; /* additional signals to block */ int sa_flags; /* signal options, Figure 10.16 */ /* alternate handler */ void (*sa_sigaction)(int, siginfo_t *, void *); }; 当sa_handler传入一个handler时,sa_mask的信号先被暂时block,当处理完信号后,因为sa_mask被阻塞的信号回复到原来。需要考虑同一个信号多次到达时需要阻塞。 Note that we must use sigemptyset to initialize the sa_mask member of the structure. 这种设置在再一次改变之前,一直有效
Description | |
---|---|
SA_INTERRUPT | System calls interrupted by this signal are not automatically restarted (the XSI default for sigaction). See Section 10.5 for more information. |
SA_NOCLDSTOP | If signo is SIGCHLD, do not generate this signal when a child process stops (job control). This signal is still generated, of course, when a child terminates (but see the SA_NOCLDWAIT option below). As an XSI extension, SIGCHLD won't be sent when a stopped child continues if this flag is set. |
SA_NOCLDWAIT | If signo is SIGCHLD, this option prevents the system from creating zombie processes when children of the calling process terminate. If it subsequently calls wait, the calling process blocks until all its child processes have terminated and then returns -1 with errno set to ECHILD. (Recall Section 10.7.)像SIGCLD |
SA_NODEFER | When this signal is caught, the signal is not automatically blocked by the system while the signal-catching function executes (unless the signal is also included in sa_mask). Note that this type of operation corresponds to the earlier unreliable signals. |
SA_ONSTACK | If an alternate stack has been declared with sigaltstack(2), this signal is delivered to the process on the alternate stack. |
SA_RESETHAND | The disposition for this signal is reset to SIG_DFL, and the SA_SIGINFO flag is cleared on entry to the signal-catching function. Note that this type of operation corresponds to the earlier unreliable signals. The disposition for the two signals SIGILL and SIGTRAP can't be reset automatically, however. Setting this flag causes sigaction to behave as if SA_NODEFER is also set. |
SA_RESTART | System calls interrupted by this signal are automatically restarted. (Refer to Section 10.5.) |
SA_SIGINFO | This option provides additional information to a signal handler: a pointer to a siginfo structure and a pointer to an identifier for the process context. |
The sa_sigaction field is an alternate signal handler used when the SA_SIGINFO flag is used with sigaction.
Normally, the signal handler is called as
void handler(int signo);
but if the SA_SIGINFO flag is set, the signal handler is called as
void handler(int signo, siginfo_t *info, void *context); struct siginfo { int si_signo; /* signal number */ int si_errno; /* if nonzero, errno value from <errno.h> */ int si_code; /* additional info (depends on signal) */ pid_t si_pid; /* sending process ID */ uid_t si_uid; /* sending process real user ID */ void *si_addr; /* address that caused the fault */ int si_status; /* exit value or signal number */ long si_band; /* band number for SIGPOLL */ /* possibly other fields also */ }; 最少含有si_signo, si_code,不同的信号,其si_code不同。 We intentionally try to set the SA_RESTART flag for all signals other than SIGALRM, so that any system call interrupted by these other signals is automatically restarted. The reason we don't want SIGALRM restarted is to allow us to set a timeout for I/O operations. The default is to not restart system calls when the signal handler is installed with sigaction.
An implementation of signal using sigaction
此处的signal的实现不同于2.6.35版本ubuntu的实现。系统的signal自动清除了handler,并且没有屏蔽当前信号。
#include "apue.h" /* Reliable version of signal(), using POSIX sigaction(). */ Sigfunc * signal(int signo, Sigfunc *func) { struct sigaction act, oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (signo == SIGALRM) { #ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT; #endif } else { #ifdef SA_RESTART act.sa_flags |= SA_RESTART; #endif } if (sigaction(signo, &act, &oact) < 0) return(SIG_ERR); return(oact.sa_handler); }
10.15. sigsetjmp and siglongjmp Functions
在使用信号处理函数中使用longjmp有一个问题:当进入到信号处理函数时,当前信号被自动加入信号屏蔽集中,如果longjmp了,信号屏蔽集该怎么办? POSIX对此并没有定义,而是声明了两个新函数sigsetjmp和siglongjmp来在signal handler中使用。 The only difference between these functions and the setjmp and longjmp functions is that sigsetjmp has an additional argument. If savemask is nonzero, then sigsetjmp also saves the current signal mask of the process in env. When siglongjmp is called, if the env argument was saved by a call to sigsetjmp with a nonzero savemask, then siglongjmp restores the saved signal mask. 使用sigsetjmp时应该设置一个canjmp标志,保证siglongjmp在sigsetjmp之后才能触发。这种保护在setjmp和longjmp中不许要,因为sigjmp完全是随机的,而普通jmp可以确定先后。
10.16. sigsuspend Function
#include <signal.h> int sigsuspend(const sigset_t *sigmask); Returns: -1 with errno set to EINTR The signal mask of the process is set to the value pointed to by sigmask. Then the process is suspended until a signal is caught or until a signal occurs that terminates the process. If a signal is caught and if the signal handler returns, then sigsuspend returns, and the signal mask of the process is set to its value before the call to sigsuspend. 效果相当于,将回复除某些信号(sigmask中)外的所有信号和pause作为一个原子事件。 一、保护一些代码不受某些信号影响 阻塞 受保护区 sigsuspend() //等待时除了个别信号外,其余信号暂时unblock //此时回复到sigsuspend之前的信号状态。 二、Another use of sigsuspend is to wait for a signal handler to set a global variable. 三、Synchronize a parent and child. 先block USR1、USR2.然后tell函数发送信号,wait函数suspend。 此时,等待中的父子进程只能suspend,无法做别的事情。必须使用多线程来处理信号。 一种尽可能好的技巧是,一个全局变量标志信号是否发生,sighandler使其置1。while(函数){如果发生了->处理}。这样可以一边调用函数,一边处理信号。
10.17. abort Function
#include <stdlib.h> void abort(void);
This function never returns
ISO C requires that if the signal is caught and the signal handler returns, abort still doesn't return to its caller. POSIX.1 states that, when the signal handler returns, abort terminates the process. 符合POSIX的实现,fflush(NULL),解除对SIGABRT的阻塞。handler不能为IGN,要变成DLT,如果是自定义的,应该在最后关闭程序。
10.18. system Function(?)
POSIX.1 requires that system ignore SIGINT and SIGQUIT and block SIGCHLD(?). 忽略SIGINT和SIGQUIT是因为,system执行的命令有可能是有交互的命令,此时控制权应该交给执行的程序。但SIGQUIT和SIGINT会发送给所有的foreground process,原程序不应该终止。 返回值?信号终止是128+号
10.19. sleep Function
#include <unistd.h> unsigned int sleep(unsigned int seconds); Although sleep can be implemented with the alarm function (Section 10.10), this isn't required. If alarm is used, however, there can be interactions between the two functions. 注意与alarm的混用。
10.20. Job-Control Signals
SIGCHLD | Child process has stopped or terminated. |
SIGCONT | Continue process, if stopped. |
SIGSTOP | Stop signal (can't be caught or ignored). |
SIGTSTP | Interactive stop signal. |
SIGTTIN | Read from controlling terminal by member of a background process group. |
SIGTTOU | Write to controlling terminal by member of a background process group. |
When we type the suspend character (usually Control-Z), SIGTSTP is sent to all processes in the foreground process group. When any of the four stop signals (SIGTSTP, SIGSTOP, SIGTTIN, or SIGTTOU) is generated for a process, any pending SIGCONT signal for that process is discarded. Similarly, when the SIGCONT signal is generated for a process, any pending stop signals for that same process are discarded. 一个典型的处理SIGTSTP的程序 static void sig_tstp(int signo) /* signal handler for SIGTSTP */ { sigset_t mask; /* ... move cursor to lower left corner, reset tty mode ... */ /* * Unblock SIGTSTP, since it's blocked while we're handling it. */ sigemptyset(&mask); sigaddset(&mask, SIGTSTP); sigprocmask(SIG_UNBLOCK, &mask, NULL); signal(SIGTSTP, SIG_DFL); /* reset disposition to default */ kill(getpid(), SIGTSTP); /* and send the signal to ourself */ /* we won't return from the kill until we're continued */ signal(SIGTSTP, sig_tstp); /* reestablish signal handler */ /* ... reset tty mode, redraw screen ... */ }
10.21. Additional Features
Signal Names
Some systems provide the array
extern char *sys_siglist[];
The array index is the signal number, giving a pointer to the character string name of the signal.
#include <signal.h> void psignal(int signo, const char *msg); #include <string.h> char *strsignal(int signo);
Signal Mappings
Solaris provides a couple of functions to map a signal number to a signal name and vice versa.
#include <signal.h> int sig2str(int signo, char *str); int str2sig(const char *str, int *signop); Exercise 10.6(?) #include <apue.h> #include <sys/wait.h> #include <fcntl.h> #include <stdio.h> #include "../10.18/main.c" //signal的实现不同 static volatile sig_atomic_t sigflag; /* set nonzero by sig handler */ static sigset_t newmask, oldmask, zeromask; static void sig_usr(int signo) /* one signal handler for SIGUSR1 and SIGUSR2 */ { sigflag = 1; } void TELL_WAIT(void) { if (signal(SIGUSR1, sig_usr) == SIG_ERR) err_sys("signal(SIGUSR1) error"); if (signal(SIGUSR2, sig_usr) == SIG_ERR) err_sys("signal(SIGUSR2) error"); sigemptyset(&zeromask); sigemptyset(&newmask); sigaddset(&newmask, SIGUSR1); sigaddset(&newmask, SIGUSR2); /* * Block SIGUSR1 and SIGUSR2, and save current signal mask. */ if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) err_sys("SIG_BLOCK error"); } void TELL_PARENT(pid_t pid) { kill(pid, SIGUSR2); /* tell parent we're done */ } void WAIT_PARENT(void) { while (sigflag == 0) sigsuspend(&zeromask); /* and wait for parent */ sigflag = 0; /* * Reset signal mask to original value. */ if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) err_sys("SIG_SETMASK error"); } void TELL_CHILD(pid_t pid) { kill(pid, SIGUSR1); /* tell child we're done */ } void WAIT_CHILD(void) { while (sigflag == 0) sigsuspend(&zeromask); /* and wait for child */ sigflag = 0; /* * Reset signal mask to original value. */ if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) err_sys("SIG_SETMASK error"); } volatile int num =0; int main(void) { FILE* fd; pid_t childPid; // int num=0; char fatherMsg[]="Father writes"; char childMsg[]="Child writes"; int status; if ((fd = fopen("file.out","w+")) < 0) { perror("create file error!"); exit(1); } if(fprintf(fd,"%d/n",num)<0) { perror("write error!"); exit(1); } fflush(fd); num++; TELL_WAIT(); printf("begin fork./n"); if((childPid = fork()) < 0) { perror("can't fork!"); exit(1); } else if(childPid ==0) { while(num<=50) { fprintf(fd,"%d/t%s/n",num,childMsg); num++; fflush(fd); TELL_PARENT(getppid()); WAIT_PARENT(); } exit(0); } else { while(num<=50) { WAIT_CHILD(); fprintf(fd,"%d/t%s/n",num,fatherMsg); fflush(fd); num++; TELL_CHILD(childPid); } wait(&status); fclose(fd); exit(0); } exit(0); }