1. 信号处理函数的定义

预定义的信号处理常量:
SIG_ERR:用于注册信号处理函数sigaction()的返回值,表示发生了错误SIG_DFL:指定使用默认动作来处理信号SIG_IGN:忽略这个信号。
2. 信号处理程序的安装

sigaction函数的返回值: 0 成功; -1 失败,并设置errno 。
有两个信号无法被捕获: SIGKILL 和 SIGSTOP
sa_mask:阻塞信号的集合。当前信号正在被处理的时候,哪些信号会被阻塞。
sa_flags:可以修改信号的行为:
SA_NOCLDSTOP:子进程被stop或者resume时,不发出 SIGCHLD 信号。SA_NOCLDWAIT:当子进程被terminate时,不会转变成zombie进程SA_NODEFER:正在处理当前信号时,不阻塞其他信号。优先级比sa_mask高
注:信号的阻塞和系统调用的阻塞:
- 信号的阻塞是指:在处理某个信号时,不允许被阻塞的信号被递交( deliver )
- 系统调用的阻塞:阻塞的系统调用会在资源不足时让进程睡眠;非阻塞的系统调用在资源不足时会立刻返回,以便进程再度进行系统调用
3. SIGCLD 信号
当一个子进程 终止 或 停止 时,父进程会收到这个信号。
它的默认处置为忽略。
可以这样处理子进程:

上述的子进程处理程序不能应对多个子进程的情形。
因为:在处理某个SIGCHLD信号时,此时可能有另一个SIGCHLD信号到来。UNIX中信号不会排队,所以这些后来的SIGCHLD信号就被丢掉了。这些终结的子进程没有被wait,变成了僵死进程。
对于多个子进程,应该这样:
int sig_chld(int signo) {pid_t pid;int stat;while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)printf("child %d terminated\n", pid);}
其中,waitpid函数的第一个参数-1表示第一个终结的子进程;第三个参数WNOHANG表示非阻塞。
不能在while使用wait函数。因为wait是阻塞的。不能得到while循环的退出条件;同时信号处理程序可能会永远阻塞下去。
4. 信号处理
- 慢系统调用:可能会永远阻塞的系统调用。比如
accept,服务器在没有接收到连接时,这个调用就不会返回。 - 如果父进程正阻塞与某个慢系统调用,比如
accept, read;这时来了另一个系统调用,并且被父进程处理了,如SIGCHLD,那么原来的慢系统调用就会出错,并设置错误码为EINTR,即Error Interrupt。 - 对于上述情况,某些系统实现的
signal函数可以将被中断的系统调用重启,而某些系统无法,我们应该手动这样做。(signal函数是ANSI C的标准,而不是POSIX标准。) - 对POSIX的
SA_RESTART支持,意味着某些系统调用可以重启。(可以通过#ifdef SA_RESTART判断系统是否支持这项特性。) - 手动重启被中断的系统调用:
for (;;) {client = sizeof(cliaddr);if ((connfd = accept(listenfd, (SA*)&cliaddr, &clilen)) < 0) {if (errno == EINTR) // accept系统调用被中断了continue; // 失败重试else // 其他错误err_sys("accept error");}}
kill 命令
向指定进程发送信号。默认发送
SIGTERM信号。
SIGINT,SIGTERM,SIGKILL
SIGKILL 可以通过 kill -9 发送,这个信号无法被捕获,进程会强制终止运行
SIGINT 通过键盘 ctrl-c 发送,可以被捕获
SIGTERM 是 kill 命令发送的默认信号,可以被捕获
