信号驱动任务指的是通过信号来驱动任务的执行,每发送一次信号,任务就执行一次。实现该目的所需的函数就是 pause 或者 sigsuspend,pause和sigsuspend函数可以暂停当前进程,直至收到信号才会继续运行之后的程序。
目录
1、认识 pause / sigsuspend 函数
(1) pause 函数
(2) sigsuspend 函数
2、信号驱动任务执行的两种方式
(1) sigprocmask + pause
(2) sigsuspend
pause 函数的作用是暂停当前进程(进入休眠状态),直至收到信号(任意信号),才会唤醒当前进程。
因为信号的处理动作有终止、忽略、捕捉、屏蔽,所以也对应了下面四种情况:
sigsuspend函数的作用也是暂停当前进程直至收到信号,但 sigsuspend 还可以主动设置屏蔽哪些信号,收到这些被屏蔽的信号时,不会解除挂起。
参数 mask:需要屏蔽的信号集。当前进程的屏蔽字会被替换为当前参数的屏蔽信号集,等到函数返回,会还原当前进程的屏蔽字。
返回值:只会返回 -1。
下面就以 2号信号为例,实现信号驱动任务执行。2号信号 SIGINT 可以终止前台进程,我们需要捕获2号信号,并设置相应的信号处理函数来避免当前进程被终止。
执行任务的时候,我们不希望任务执行到一半被打断,所以在执行任务的之前我们需要调用 sigprocmask函数来屏蔽未来会收到的信号(当前场景下指的就是2号)
#include
#include
#include void handler(int signum)
{printf("收到并处理%d号信号\n", signum);
}int main(){signal(2, handler); // 捕捉2号信号sigset_t set;sigemptyset(&set); // 清空信号集sigaddset(&set, 2); // 向信号集中添加2号信号while(1){sigprocmask(SIG_BLOCK, &set, NULL); // 屏蔽2号信号printf("------------------\n");printf("task is running\n");printf("------------------\n");sleep(1);sigprocmask(SIG_UNBLOCK, &set, NULL); // 解除2号信号的屏蔽pause();}return 0;
}
正常情况应该是,解除2号信号的屏蔽以后,pause函数挂起的时候捕捉到了信号,此时会先去调用信号处理函数,然后解除挂起就会重新去打印“task is running” 。然而,从下面的结果可以看出,只执行了信号处理函数,但是pause函数没有解除挂起。
原因就是,由于发送信号较为频繁,在打印“task is running” 的时候收到了2号信号,但是此时2号信号被屏蔽了,解除2号信号的屏蔽以后,立马就去调用信号处理函数了,等到pause函数挂起以后,信号就已经处理完了。
因此,当信号发送较为频繁的时候,不建议使用 pause函数 来驱动任务执行。
sigsuspend 函数被调用以后,直接进入阻塞等待的状态
假设 sigsuspend 函数的屏蔽信号集中包含了3号信号,那么sigsuspend收到3号信号就不会解除挂起;如果收到了2号信号,那么sigsuspend函数就会解除挂起,并调用对应的信号处理函数。
#include
#include
#include void handler(int signum)
{printf("收到并处理%d号信号\n", signum);
}int main(){signal(2, handler); // 捕捉2号信号sigset_t set;sigemptyset(&set); // 清空信号集sigaddset(&set, 2); // 向信号集中添加2号信号sigset_t mask; // sigsuspend的屏蔽信号集sigemptyset(&mask);sigaddset(&mask, 3); // 向屏蔽信号集添加3号信号while(1){sigprocmask(SIG_BLOCK, &set, NULL); // 屏蔽2号信号printf("------------------\n");printf("task is running\n");printf("------------------\n");sleep(1);sigsuspend(&mask); // 挂起状态:将进程屏蔽字替换为屏蔽信号集mask// 解除挂起:恢复原本的进程屏蔽字 }return 0;
}
sigsuspend之所以不会像pause那样,是因为sigsuspend函数解除挂起以后的动作是原子的,解除挂起以后,再调用信号处理函数,保证了下一次任务的执行;pause采用的方式是先解除屏蔽,再解除挂起,这就存在一个问题,如果信号在解除挂起之前就被处理了,那么pause函数根本就收不到信号。