先来看看函数原型:
点击(此处)折叠或打开
- #include <signal.h>
- int sigsuspend(const sigset_t *mask);
函数sigsuspend将进程的信号屏蔽码设置为mask,然后与pause()函数一样等待信号的发生并执行完信号处理函数。信号处理函数执行完后再把进程的信号屏蔽码设置为原来的屏蔽字,然后sigsuspend函数才返回。 Sigsuspend总是返回-1。 --------《LinuxC编程实战》
将进程的信号屏蔽字设置为由sigmask指向的值。在捕捉到一个信号或发生了一个会终止该进程的信号之前,该进程被挂起。如果捕捉到一个信号而且从该信号处理程序返回,则sigsuspend返回,并且将该进程的信号屏蔽字设置为调用sigsuspend之前的值。
注意,此函数没有成功返回值。如果它返回到调用者则总是返回-1,并将errno设置为EINTR(表示为一个被中断的系统调用)。 ------ 《UNIX环境高级编程》第二版
sigsuspend是一个原子操作
(1)设置新的mask阻塞当前进程
(2)收到信号,恢复原先mask
(3)调用该进程设置的信号处理函数
(4)待信号处理函数返回后,sigsuspend返回
理解:suspend的功能就是将进程当前的屏蔽信号码设置为指定的信号set,让进程挂起,再等待一次非屏蔽的信号,接受到信号后,将信号屏蔽信号码恢复成原样。
sigsuspend的作用:
在运行一段程序时,我们可能想要让程序暂时的忽略某个信号,而后执行某个代码片断,执行后复原成原来的屏蔽信号码,接受原来可能在屏蔽其间所发出的被屏蔽掉的信号。
我们一开始可能想到的是用sigprocmask 和 pause来完成,代码如下:
点击(此处)折叠或打开
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <signal.h>
- int main(void)
- {
- sigset_t newmask, oldmask;
-
- sigemptyset(&newmask);
- sigaddset(&newmask, SIGINT);
-
- /*屏蔽SIGINT信号*/
- if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0 ) {
- perror("sigprocmask");
- exit(-1);
- }
-
- /*临界区*/
- /*此处是不想被SIGINT信号中断的代码*/
- /*-------code--------*/
- /*将屏蔽的SIGINT信号恢复*/
- if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
- perror("sigprocmask");
- exit(-1);
- }
- /*用pause接受原来被屏蔽掉的SIGINT信号*/
- pause();
- return 0;
- }
但是这样做有个问题,当信号在pause()之前发出,如果该信号之发送了一次,那进程先会执行一次信号处理函数,然后将会在pause处永久挂起。
为了避免这种BUG,引入了sigsuspend(),该函数操作是一个原子操作,也就是说在执行该于原子操作其间,CPU不会进行线程切换,这样就不会被其它的线程所打断。
修改后的代码如下:
点击(此处)折叠或打开
- #include <stdio.h>
- #include <stdlib.h>
- #include <signal.h>
- void handler_sig(int signo);
- int main(void)
- {
- sigset_t newmask, oldmask, zeromask;
- sigemptyset(&newmask);
- sigemptyset(&zeromask);
- sigaddset(&newmask, SIGINT);
- /*安装信号函数*/
- signal(SIGINT, handler_sig);
- /*屏蔽SIGINT信号*/
- if (-1 == sigprocmask(SIG_BLOCK, &newmask, &oldmask)) {
- perror("sigprocmask");
- exit(-1);
- }
- /*临界区*/
- /*不想被SIGINT中断的代码*/
- sleep(30);
- /*用sigsuspend捕获在屏蔽期间内发出的信号,并完成相应的处理函数*/
- if (-1 != sigsuspend(&zeromask)) {
- perror("sigsuspend");
- exit(-1);
- }
- /*恢复信号屏蔽*/
- if (-1 == sigprocmask(SIG_SETMASK, &oldmask, NULL)) {
- perror("sigprocmask");
- exit(-1);
- }
- return 0;
- }
- void handler_sig(int signo)
- {
- printf("revc SIGIO\n");
- }
程序在执行完相应的临界区代码后(此处临界区代码只写了sleep(30),只是为了保证之后在sleep期间收到SIGINT信号,实际中可根据需求加上相应的临界区代码),用sigsuspend接收在临界区发出的信号,借着执行如下的原子操作:
1.屏蔽&zeromask信号集所包含的信号,此处为空,所以可以捕获所有的信号。
2.捕获非屏蔽的信号,此处即捕获所有的信号。
3.执行捕获到的信号所对应的信号处理函数。
4.将当前所屏蔽的信号恢复成执行sigsuspend之前所屏蔽的信号。
执行完4步操作后,程序结束,而不会想之前的程序停在pause()。
sigsuspend在实际中还有很多应用,在此先介绍这一种用法,剩下日后遇到再补充。