显式地等待信号
- 有时, 之程序需要显式地等待某个信号处理程序运行: 当shell创建一个前台作业之前, 在接收下一条用户命令之前, 它必须等待作业终止, 被
SIGCHLD
处理程序回收 - 实例代码:
- 父进程设置
SIGINT
和SIGCHLD
的处理程序, 然后进入一个无限循环#include "csapp.h" volatile sig_atomic_t pid; void sigchld_handler(int s){ int olderrno = errno; pid = waitpid(-1, NULL, 0); errno = olderrno; } void sigint_handler(int s){ } int main(int argc, char **argv){ sigset_t mask, prev; Signal(SIGCHLD, sigchld_handler); Signal(SIGINT, sigint_handler); Sigemptyset(&mask); Sigaddset(&mask, SIGCHLD); while(1){ Sigprocmask(SIG_BLOCK, &mask, &prev); if (Fork() == 0){ exit(0); } pid = 0; Sigprocmask(SIG_SETMASK, &prev, NULL); while(!pid) // pause(); // sleep(); printf("."); } exit(0); }
- 父进程设置
while(!pid)
- 一些存在的问题:
- 循环体为空: 浪费资源
pause()
: 如果在while
后和pause
之前收到了SIGCHLD
信号,pause
将会永远睡眠sleep(1)
: 低效
- 合适的解决方法:
sigsuspend
: 暂时使用mask
替换当前的阻塞集合, 然后挂起当前进程, 直到收到一个信号, 其行为要么是运行一个处理程序, 要么是终止该进程- 终止: 进程不从
sigsuspend
返回就直接终止 - 运行一个处理程序:
sigsuspend
从处理程序处返回, 恢复调用sigsuspend
时原有的阻塞集合[!note]
sigsuspend
等价于以下代码的原子调用#include "csapp.h" volatile sig_atomic_t pid; void sigchld_handler(int s){ int olderrno = errno; pid = waitpid(-1, NULL, 0); errno = olderrno; } void sigint_handler(int s){ } int main(int argc, char **argv){ sigset_t mask, prev; Signal(SIGCHLD, sigchld_handler); Signal(SIGINT, sigint_handler); Sigemptyset(&mask); Sigaddset(&mask, SIGCHLD); while(1){ Sigprocmask(SIG_BLOCK, &mask, &prev); if (Fork() == 0){ exit(0); } pid = 0; Sigprocmask(SIG_SETMASK, &prev, NULL); while(!pid) sigsuspend(&prev); printf("."); } exit(0); }
- 终止: 进程不从
- 一些存在的问题: