有2个函数 sigprocmask和sigsuspend
sigprocmask系统调用用于改变当前阻塞信号集,比如进程想阻塞SIGMIN+1信号。就把这个信号加入掩码中,然后调用 sigprocmask具体参数见man手册。
而sigsuspend是挂起进程,等待信号。等收到信号后,继续执行进程。见如下程序:
sigemptyset(&zeromask); sigemptyset(&newmask);
sigaddset(&newmask, SIGMIN+1);
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) { //阻塞SIGMIN+1
fprintf(stderr, "call sigprocmask() error/n");
goto error;
}
if(sigsuspend(&zeromask) != -1) { //等待信号到来(zeromask表示可以等待任何信号
fprintf(stderr, "sigsuspend() error/n");
goto error;
}
有4个问题,1。如果前面SIGMIN+1被阻塞了,那后面则表示进程一直收不到,被suspend悬挂么?2。这样写如何就有防止信号丢失的作用 呢?3。假设在sigsuspend运行之前收到SIGMIN+1,那么sigsuspend会从sigqueue里面取出来处理还是等待新的 SIGMIN+1到来?4。sigsuspend是否应该只放行SIGMIN+1而不是用空信号集?
1。其实sigsuspend是一个原子操作,包含4个步骤:
(1) 设置新的mask阻塞当前进程;
(2) 收到信号,恢复原先mask;
(3) 调用该进程设置的信号处理函数;
(4) 待信号处理函数返回后,sigsuspend返回。
所以第一步之后,进程是可以接受任何信号的。接收到SIGMIN+1,恢复之前阻塞信号集,并处理SIGMIN+1信号。
2。为什么有防止信号丢失的作用呢?
这个功能是sigsuspend 4步原子操作决定的。如果不用sigsuspend(&zeromask)而改用
sigprocmask(SIG_BLOCK,&emptyset,NULL); pause();
这2个函数不是原子操作,在2 个函数之间信号到来了,继续pause,由于就没有信号到来,则进程还会停在那里等待新的SIGMIN+1到来。所以用sigsuspend来取代这2个 函数功能,就能保证在sigsuspend函数运行时收到的信号不会丢失,会被处理的。
3.第三个问题,按道理是从sigqueue里面取,而不会重新等待新的SIGMIN+1信号的到来。不过我没有从官方文档找到这种说 法,sigsuspending是获得当前已传送到进程,却被阻塞的所有信号,在set指向的信号集中保存未决(阻塞)的信号。认定 sigsuspending和sigsuspend有相似之处。再加上从不能丢失信号,答案偏向于前者。
4。这个我也想知道,道理嘛,就是为了预防对其他信号也返回,并继续执行进程。但是假设其他信号没有处理函数也就是原子操作第三步没完 成,sigsuspend不会继续执行,这种做法就没有意义了。所以还需要继续看看source code.
这几天的研究表明,在sigsuspend之所以置为zeromask是为了对之前阻塞的信号进行处理。不过对于apache module里面的信号,可以以旧采取blocked mask+(SIGMIN+1).目的是不想影响apache对自己设定阻塞信号的处理。在进入module之前阻塞的,从module出来后依然保持阻 塞就是了。
------------------------------------------分割线--------------------------------------------------
对于sigprocmask这个函数,刚开始看也是不太了解这个函数究竟能做什么,对于第一个参数有什么作用。在网上找了不少的介绍,先来看看原 型: int sigprocmask(int how,const sigset_t *set,sigset_t *oldset); sigset_t是封装的一种数据类型,实际上是一个unsigned long int __val[_SIGSET_NWORDS];在/usr/include/bits/sigset.h里面定义,用来包含所有的信号集的。 每个进程都有一个用来描述哪些信号递送到进程时将被阻塞的信号集,该信号集中的所有信号在递送到进程后都将被阻塞。 sigprocmask是最为关键的一个函数。在使用之前要先设置好信号集合set。这个函数的作用是将指定的信号集合set加入到进程的信号阻塞集合之 中去,如果提供了oldset那么当前的进程信号阻塞集合将会保存在oldset里面。参数how决定函数的操作方式。
SIG_BLOCK:增加一个信号集合到当前进程的阻塞集合之中。 每个进程有一个默认的信号阻塞集合,如果像下面: sigemptyset(set);/*定义信号集set*/ sigaddset(set,SIGINT);/*把信号SIGINT添加到信号集中*/ sigprocmask(SIG_BLOCK,set,NULL);/*把set设置为信号阻塞集 则是把SIGINT加入到阻塞信号集合中。
SIG_UNBLOCK:从当前的阻塞集合之中删除一个信号集合。
SIG_SETMASK:将当前的信号集合设置为信号阻塞集合。
sigeprocmask函数通常和sigemptyset、sigfillset、sigaddset、sigdelset、 sigismember函数配合使用,主要有两种用途: 1.我们不希望某些不太重要的信号来影响我们的进程,我们就可以把这些信号添加到信号屏蔽集中。使它们不打扰进程的执行。 2.如果系统现在很忙,没有时间及时相应信号,进程可以先把信号阻塞掉,等系统有空闲时间在去相应,这也保证了信号的可靠性。
看看下面一个例子: #includde”apue.h” static void sig_quit(int signo) { printf(”caught SIGQUIT/n”); signal(SIGQUIT, SIG_DFL);//将SIGQUIT的动作设为缺省值 } int main() { sigset_t newmask; sigset_t oldmask; sigset_t pendmask;
signal(SIGQUIT, sig_quit);//信号量捕捉函数,捕捉到SIGQUIT,跳转到函数指针sig_quit处执行
sigemptyset(&newmask);//初始化信号量集 sigaddset(&newmask, SIGQUIT);//将SIGQUIT添加到信号量集中
sigprocmask(SIG_BLOCK, &newmask, &oldmask);//将newmask中的SIGQUIT阻塞掉,并保存当前信号屏蔽字
sleep (5);//休眠5秒钟
sigpending(&pendmask);//检查信号是悬而未决的 if (sigismember(&pendmask, SIGQUIT))//SIGQUIT是悬而未决的。所谓悬而未决,是指SIGQUIT被阻塞还没有被处理 { printf(”/nSIGQUIT pending/n”); }
sigprocmask(SIG_SETMASK, &oldmask, NULL);//恢复被屏蔽的信号SIGQUIT
printf(”SIGQUIT unblocked/n”);
sleep(5);//再次休眠5秒钟
return (0); 以上示例是APUE P260, 执行结果是 $./a.out ^/ SIGQUIT pending caught SIGQUIT 在sigprocmask返回之前处理阻塞信号SIGQUIT,输出它 SIGQUIT unblocked ^/Quit (coredump)//因为已经被设置为缺省值,所以再次产生SIGQUIT信号,直接退出