进程捕捉到信号对其进行处理时,进程正在执行的正常序列就被信号处理程序临时中断,它首先执行该信号处理程序中的指令。如果从信号处理程序返回(例如没有调用exit或longjmp),则继续执行在捕捉到信号时进程正在执行的正常指令序列。
1. 在信号处理程序中,我们要保证调用”异步信号安全”的函数,即可重入的函数
不可重入的函数大多(a)已知它们使用静态数据结构。(b)它们调用malloc或free(c)它们是标准I/O函数
2. 由于每个线程只有一个errno变量,所以信号处理程序可能会修改其原先值。因此,所有信号处理程序应当在函数的起始保存errno,结尾恢复errno
3. 每个进程都有一个信号屏蔽字(signal mask),它规定了当前要阻塞传递送到该进程的信号集
信号集signal set
int sigemptyset(sigset_t *set); //初始化由set指向的信号集,清除其中所有信号 int sigfillset(sigset_t *set); //初始化由set指向的信号集,使其包括所有信号
所有应用程序在使用信号集前,要对该信号集调用sigemptyset或sigfillset一次。
信号集初始化之后,可在该信号中增删特定的信号。
int sigaddset(sigset_t *set,int signo) int sigdelset(sigset_t *set,int signo)
进程的信号屏蔽字
int sigprocmask(int how,const sigset_t *restrict set, const sigset_t *restrict oset)
Oset若是非空指针,那么进程的当前信号屏蔽字通过oset返回
How的三种取值决定了如何修改当前信号屏蔽字:
SIG_BLOCK : 向当前信号屏蔽字中添加参数set包含的信号
SIG_UNBLOCK : 把当前信号屏蔽字中参数set包含的信号删去
SIG_SETMASK : 把参数set设为进程的信号屏蔽字。
请注意,sigprocmask仅为单线程进程定义的。处理多线程进程中信号的屏蔽使用另一个函数
执行信号的处理程序称为信号递达,信号从产生到递达之间的状态称为信号未决。被阻塞的信号将保持在未决状态,直到进程解决对此信号的阻塞。
int sigpending(sigset_t *set)
Set返回当前的未决信号
信号处理的范式
static int pipefd[2]; int signal_module_init() { struct sigaction act; //信号处理程序指定为sig_handler act.sa_handler = sig_handler; //在进入信号处理程序前,把act.sa_mask信号集加到进程的信号屏蔽字中。调用sigfillset把所有信号加入这个信号集。这表示当进入信号处理程序后,阻塞一切信号 sigfillset(&act.sa_mask) if(0 > sigaction(SIGINT,&act,0) || 0 > sigaction(SIGCHLD,&act,0) || ...... ) { write_log("failed to init signal:sigaction()"); return -1; } return signal_pipe_init(); } static int signal_pipe_init() { if( 0 < pipe(pipefd,O_CLOEXEC|O_NONBLOCK) ){ write_log("failed to init pipe"); return -1; } return 0; } static void sig_handler(int signo) { //定义一个数组,将你注册的每个信号的signo映射成一个唯一的字符 static const char sig_chars[NSIG+1] = { [SIGINT] = 'I', [SIGCHLD] = 'C', ..... }; char s; int saved_errno; //保存当前的errno。每个线程仅有一个errno变量,不应让信号处理程序中的errno影响正常流程中的errno。因此我们需要在信号处理程序的起始存储errno,在末尾恢复errno saved_errno = errno; s = sig_chars[signo]; write(pipefd[1],&s,sizeof(s)); errno = saved_errno; } //然后在Reactor中监听pipefd[0]. 其回调函数如下: void got_signal(ev) { int res,ret; char c; int fd = ev->fd; for(;;){ //fd是非阻塞的 do { res = read(fd,&c,1); } while(res == -1 && errno == EINTR); //pipe中没有可读数据 if(res <= 0){ break; } switch(c){ case 'I': dosomething1(); break; case 'C': dosomething2(); break; ...... } } return; }