6.10.1 pause函数
pause函数将调用线程挂起,使进程进入可中断的睡眠状态,直到传递了一个信号为止。
这个信号的动作或者是执行用户定义的信号处理函数,或者是终止进程。
如果是执行用户自定义的信号处理函数,那么pause会在信号处理函数执行完毕后返回;
如果是终止进程,pause函数就不返回了。
如果内核发出的信号被忽略,那么进程就不会被唤醒。
pause函数的定义如下:
int pause (void);
比较有意思的是,pause函数如果可以返回,那它总是返回-1,并且errno为EINTR。
如果希望pause函数等待某个特定的信号,就必须确定哪个信号会让pause返回。
事实上,pause并不能主动区分使pause返回的信号是不是正在等待的信号,我们必须间接地完成这个任务。
常用的方法是,在期待的特定信号的信号处理函数中,将某变量的值设置为1,待pause返回后,通过查看该变量的值是否为1来判定等待的特定信号是否被捕获,方法如下面的代码所示:
static volatile sig_atomic_t sig_received_flag = 0;
while(sig_received_flag == 0)
pause();
如果只有等待的那个信号的处理函数会将sig_received_flag置成1,那么进程就会一直阻塞,直到接收到特定的信号为止。
看起来很美好,可是上面的逻辑是有漏洞的。检查sig_received_flag==0和调用pause之间存在一个时间窗口,如果在该时间窗口内收到信号,并且信号处理函数将sig_received_flag置1,那么主控制流根本就不知道这件事情,进程就会依然阻塞。也就是说,等待的信号已经到来,但是进程错过了。在收到下一个信号之前,pause函数不会返回,进程也就没有机会发现其实在等待的信号早就已经收到了。
因为检查和pause之间存在时间窗口,所以就有了错失信号的情况,如表6-16所示。
表6-16 错失等待信号的时序条件
下面通过另一个例子来描述一下pause的困境。
程序执行过程中,关键部分不期望被信号打断,于是临时阻塞信号,关键部分完成之后,就解除信号的阻塞,然后暂停执行直到有信号到达为止:
/*关键代码结束 */
sigprocmask(SIG_SETMASK,&orig_mask,NULL);
/*此处信号可能已经递送给进程了,导致 pause无法返回 */
pause();