详细学习了以下这几个章节
10.3signal函数
10.4 不可靠信号
10.5 中断系统调用
10.6 可重入函数
10.8 可靠信号术语与语义
10.11 信号集
10.12 sigpromask函数
10.13 sigpending函数
10.14 sigaction函数
10.16 sigsuspend函数
|
// man signal NAME signal - ANSI C signal handling SYNOPSIS #include <signal.h> typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); The behavior of signal() varies across Unix versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead. 参数:signum:表示表10.1中信号名 handler:常量SIG_IGN、常量SIG_DFL或当接到此信号后要调用的函数的地址。 SIG_IGN:内核忽略此信号。 SIG_DFL:内核采用默认操作。 要调用的函数地址:当信号发生时,调用该函数,我们称这种处理为“捕捉”该信号。称此函数为信号处理函数(signal handler)或信号捕捉函数(signal catching function)。 注意:The signals SIGKILL and SIGSTOP cannot be caught or ignored. 返回值:signal() returns the previous value of the signal handler, or SIG_ERR on error. |
//example:filename signal1.c #include <unistd.h> #include <signal.h> #include <stdio.h> void sig_func(int signo) { if(signo == SIGUSR1) printf("received SIGUSR1\n"); else if (signo == SIGUSR2) printf("received SIGUSR2\n"); else printf("other signal\n"); } int main(void) { printf("before signal\n"); if( SIG_ERR ==( signal(SIGUSR1,sig_func) )) printf("signal error\n"); for(;;) sleep(5); printf("after signal"); return 0; } |
compile:$ gcc -Wall signal1.c -o signal1 run:$./signal1 & reuslt :./signal1 & [2] 7559 root@hpnl-desktop:/home/hpnl/Downloads/test# before signal kill -USR1 7559 root@hpnl-desktop:/home/hpnl/Downloads/test# received SIGUSR1 kill 7559 |
信号(signal) 是软件中断。它使得程序员可以处理异步事件。为了向进程发送一个信号, 内核在进程表条目的信号域中设置一个位,对应于收到的信号的类型。信号函数的 ANSI C 原型是:
void (*signal (int sigNum, void (*sigHandler)(int))) (int); |
或者,另一种描述形式:
typedef void sigHandler(int); SigHandler *signal(int, sigHandler *); |
当进程处理所捕获的信号时,正在执行的正常指令序列就会被信号处理器临时中断。然后进程继续执行, 但现在执行的是信号处理器中的指令。如果信号处理器返回,则进程继续执行信号被捕获时正在执行的 正常的指令序列。
现在,在信号处理器中您并不知道信号被捕获时进程正在执行什么内容。如果当进程正在使用 malloc
在它的堆上分配额外的内存时,您通过信号处理器调用 malloc
,那会怎样?或者,调用了正在处理全局数据结构的某个函数,而 在信号处理器中又调用了同一个函数。如果是调用 malloc
,则进程会 被严重破坏,因为 malloc
通常会为所有它所分配的区域维持一个链表,而它又 可能正在修改那个链表。
甚至可以在需要多个指令的 C 操作符开始和结束之间发送中断。在程序员看来,指令可能似乎是原子的 (也就是说,不能被分割为更小的操作),但它可能实际上需要不止一个处理器指令才能完成操作。 例如,看这段 C 代码:
temp += 1; |
在 x86 处理器上,那个语句可能会被编译为:
mov ax,[temp] inc ax mov [temp],ax |
这显然不是一个原子操作。
这个例子展示了在修改某个变量的过程中运行信号处理器可能会发生什么事情:
清单 1. 在修改某个变量的同时运行信号处理器
#include <signal.h> #include <stdio.h> struct two_int { int a, b; } data; void signal_handler(int signum){ printf ("%d, %d\n", data.a, data.b); alarm (1); } int main (void){ static struct two_int zeros = { 0, 0 }, ones = { 1, 1 }; signal (SIGALRM, signal_handler); data = zeros; alarm (1); while (1) {data = zeros; data = ones;} } |
这个程序向 data
填充 0,1,0,1,一直交替进行。同时,alarm 信号 处理器每一秒打印一次当前内容(在处理器中调用printf
是安全的,当信号发生时 它确实没有在处理器外部被调用)。您预期这个程序会有怎样的输出?它应该打印 0,0 或者 1,1。但是实际的输出 如下所示:
0, 0 1, 1 (Skipping some output...) 0, 1 1, 1 1, 0 1, 0 ... |
在大部分机器上,在 data
中存储一个新值都需要若干个指令,每次存储一个字。 如果在这些指令期间发出信号,则处理器可能发现 data.a
为 0 而 data.b
为 1,或者反之。另一方面,如果我们运行代码的机器能够在一个 不可中断的指令中存储一个对象的值,那么处理器将永远打印 0,0 或 1,1。
NAME sigemptyset, sigfillset, sigaddset, sigdelset, sigismember - POSIX signal set operations. SYNOPSIS #include <signal.h> int sigemptyset(sigset_t *set); int sigfillset(sigset_t *set); int sigaddset(sigset_t *set, int signum); int sigdelset(sigset_t *set, int signum); int sigismember(const sigset_t *set, int signum); Feature Test Macro Requirements for glibc (see feature_test_macros(7)): sigemptyset(), sigfillset(), sigaddset(), sigdelset(), sigismember(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE DESCRIPTION These functions allow the manipulation of POSIX signal sets. sigemptyset() initializes the signal set given by set to empty, with all signals excluded from the set. sigfillset() initializes set to full, including all signals. sigaddset() and sigdelset() add and delete respectively signal signum from set. sigismember() tests whether signum is a member of set. Objects of type sigset_t must be initialized by a call to either sigemptyset() or sigfillset() before being passed to the functions sigaddset(), sigdelset() and sigismember() or the additional glibc functions described below (sigisemptyset(), sigandset(), and sigorset()). The results are undefined if this is not done. RETURN VALUE sigemptyset(), sigfillset(), sigaddset(), and sigdelset() return 0 on success and -1 on error. sigismember() returns 1 if signum is a member of set, 0 if signum is not a member, and -1 on error. |
NAME sigprocmask - examine and change blocked signals 检测或者改变阻塞信号。 SYNOPSIS #include <signal.h> int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); Feature Test Macro Requirements for glibc (see feature_test_macros(7)): sigprocmask(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE DESCRIPTION sigprocmask() is used to fetch and/or change the signal mask of the calling thread. The signal mask is the set of signals whose delivery is currently blocked for the caller (see also signal(7) for more details). 信号屏蔽字是一组信号递送被阻塞的信号集合。就是信号屏蔽字里面的信号都被阻塞递送了。 The behavior of the call is dependent on the value of how, as follows. SIG_BLOCK The set of blocked signals is the union of the current set and the set argument. 被阻塞的信号集是参数set的信号集合 SIG_UNBLOCK The signals in set are removed from the current set of blocked signals. It is permissible to attempt to unblock a signal which is not blocked. 被设置的信号集合从当前阻塞信号集合中移除。 SIG_SETMASK The set of blocked signals is set to the argument set 阻塞的信号集合被赋值为参数set。 对于SIG_UNBLOCK与SIG_SETMASK,我们尽量选择在SIGBLOCK时,保存当前的信号集,然后在恢复时采用SIG_SETMASK,恢复之前的信号集。之所以不采用SIG_UNBLOCK,是因为可能在这之前,已经阻塞过该信号。 If oldset is non-null, the previous value of the signal mask is stored in oldset. If set is NULL, then the signal mask is unchanged (i.e., how is ignored), but the current value of the signal mask is nevertheless returned in oldset (if it is not NULL). The use of sigprocmask() is unspecified in a multithreaded process; see pthread_sigmask(3). RETURN VALUE sigprocmask() returns 0 on success and -1 on error. |
NAME sigpending - examine pending signals 检测被阻塞的未决的信号。 SYNOPSIS #include <signal.h> int sigpending(sigset_t *set); Feature Test Macro Requirements for glibc (see feature_test_macros(7)): sigpending(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE DESCRIPTION sigpending() returns the set of signals that are pending for delivery to the calling thread (i.e., the signals which have been raised while blocked). The mask of pending signals is returned in set. sigpending函数返回一个信号集,该信号集内包含调用线程中处于未决状态的信号。也就是当前处于未决状态的信号的集合。 RETURN VALUE sigpending() returns 0 on success and -1 on error. |
NAME sigaction - examine and change a signal action 作用:检测和改变信号的动作。 SYNOPSIS #include <signal.h> int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); Feature Test Macro Requirements for glibc (see feature_test_macros(7)): sigaction(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE DESCRIPTION The sigaction() system call is used to change the action taken by a process on receipt of a specific signal. (See signal(7) for an overview of signals.) sigaction系统调用用于改变进程在接收到一个特定信号时的行为。 signum specifies the signal and can be any valid signal except SIGKILL and SIGSTOP. 参数signum指定信号的类型,可以是除了SIGKILL和SIGSTOP以外的任何信号类型。这点和signal类似。 If act is non-null, the new action for signal signum is installed from act. If oldact is non-null, the previous action is saved in oldact. 如果参数act非空,那么signum信号的新的动作由参数act决定。如果oldact非空,那么oldact保存之前的行为。 The sigaction structure is defined as something like: struct sigaction { void (*sa_handler)(int);//指定SIG_DFL,SIG_IGN,orsignal handling function。类似siganl函数的第二个参数。 void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void);//不会被指定 }; On some architectures a union is involved: do not assign to both sa_handler and sa_sigaction. The sa_restorer element is obsolete and should not be used. POSIX does not specify a sa_restorer element. sa_handler specifies the action to be associated with signum and may be SIG_DFL for the default action, SIG_IGN to ignore this signal, or a pointer to a signal handling function. This function receives the signal number as its only argument. sa_handler指定与signum相关的action。 If SA_SIGINFO is specified in sa_flags, then sa_sigaction (instead of sa_handler) specifies the signal-handling function for signum. This function receives the signal number as its first argument, a pointer to a siginfo_t as its second argument and a pointer to a ucontext_t (cast to void *) as its third argu‐ ment. 如果sa_flags中指定了SA_SIGINFO字段,那么sa_sigaction而不是sa_handler为signum指定信号处理函数。这个函数接收signal number作为第一个参数,指向siginfo_t的指针作为第二个参数,一个执行void*作为第三个参数。 注意:sa_sigaction和sa_handler这两个字段,这两个实现可能使用同一个存储区,所以应用程序只能一次使用这两个字段中的一个,也就是说,sa_handler与sa_sigaction只能二选一。 sa_mask specifies a mask of signals which should be blocked (i.e., added to the signal mask of the thread in which the signal handler is invoked) during execution of the signal handler. In addition, the signal which triggered the handler will be blocked, unless the SA_NODEFER flag is used. sa_mask字段说明了一个信号集,在调用该信号捕捉函数之前,这一信号集要加到进程的信号屏蔽字中。sa_mask指定了在信号执行函数执行期间,哪些信号将会被阻塞。 sa_flags specifies a set of flags which modify the behavior of the signal. It is formed by the bitwise OR of zero or more of the following:详见man sigaction。指定了一组调整信号行为的标识。 siginfo_t结构包含了信号产生原因的有关信息。 The siginfo_t argument to sa_sigaction is a struct with the following elements:详见man sigaction RETURN VALUE sigaction() returns 0 on success and -1 on error. |
NAME sigsuspend - wait for a signal 作用:等待信号 SYNOPSIS #include <signal.h> int sigsuspend(const sigset_t *mask); Feature Test Macro Requirements for glibc (see feature_test_macros(7)): sigsuspend(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE DESCRIPTION sigsuspend() temporarily replaces the signal mask of the calling process with the mask given by mask and then suspends the process until delivery of a signal whose action is to invoke a signal handler or to terminate a process. sigsuspend暂时用参数mask的信号集代替当前进程的信号集,然后暂停进程知道信号的处理程序被触发或者进程中止。 If the signal terminates the process, then sigsuspend() does not return. If the signal is caught, then sigsuspend() returns after the signal handler returns, and the signal mask is restored to the state before the call to sigsuspend(). 如果信号中止进程,那么不会返回sigsuspend。如果信号被捕获,那么在信号处理程序执行完毕后,返回sigsuspend,并且signal mask会恢复到调用sigsuspend之前的信号集。 It is not possible to block SIGKILL or SIGSTOP; specifying these signals in mask, has no effect on the process's signal mask. RETURN VALUE sigsuspend() always returns -1, normally with the error EINTR |
sigsuspend函数在一个原子操作中,先恢复信号屏蔽字,然后再使进程暂停。 注意:我个人理解,sigsuspend就是为了解除信号集的阻塞,并且进行一系列的处理,从而方便进程的进一步操作。 |
// signal2.c /*该程序为了说明sigaction、sigemptyset、sigaddset、sigprocmask、sigsuspend、sigpending函数 */ #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <signal.h> #include <stdlib.h> #include <string.h> int test_quit; static void sig_quit(int signo) { printf("caught SIGQUIT\n"); test_quit = 1; } int main() { sigset_t oldmask; sigset_t pendmask; sigset_t waitmask; struct sigaction act,oact; memset(&act,0,sizeof(struct sigaction)); act.sa_handler = sig_quit; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGQUIT,&act,&oact); //signal(SIGQUIT, sig_quit); sigemptyset(&act.sa_mask); sigaddset(&act.sa_mask, SIGQUIT); sigprocmask(SIG_BLOCK, &act.sa_mask, &oldmask); printf("Please entrl CTRL+\\ before sleep\n"); sleep (5);//休眠5秒钟 printf("after sleep\n"); sigpending(&pendmask);/*这儿是为了说明SIGQUIT信号的确是悬而未决的*/ if (sigismember(&pendmask, SIGQUIT)) { printf("\nSIGQUIT pending\n"); } //sigprocmask(SIG_SETMASK, &oldmask, NULL);/*恢复默认的信号集,此时解除了SIGQUIT信号的阻塞,SIGQUIT信号被送到进程。*/ sigemptyset(&waitmask); sigsuspend(&waitmask); printf("hello,world\n"); if(test_quit == 1) printf("test_quit == 1\n"); else printf("test_quit is not 1\n"); printf("before sleep\n"); sleep(5); printf("after sleep\n"); return (0); } |
说明:^\是ctrl+\在中断上的显示。 有两种运行方式,分别说明了sigpending和sigsuspend函数的作用。运行方式run1 and run2如下所示: compile :$ gcc -Wall signal2.c -o signal2
这个程序比较好的说明了sigpending的作用,在提示“Please entrl CTRL+\ before sleep”时输入CTRL+\,则出现了阻塞未决的SIGQUIT信号,因此sigpending的信号集中有SIGQUIT信号。
run1:$./signal2Please entrl CTRL+\ before sleep/*出现这句话时,输入CTRL+\*/ ^\after sleep SIGQUIT pending caught SIGQUIT hello,world test_quit == 1 before sleep ^\after sleep run2:$./siganl2 Please entrl CTRL+\ before sleep /*出现这句话时,没有输入CTRL+\*/ after sleep ^\caught SIGQUIT/*在sigsuspend等待时,输入CTRL+\*/ hello,world test_quit == 1 before sleep after sleep |