子进程的终止属异步事件,父进程无法预知其子进程何时终止(即使父进程向子进程发送SIGKILL信号,子进程终止的确切时间还依赖于系统的调度:子进程下一次在何时使用CPU)。父进程应该使用wait()或者类似调用来防止僵尸子进程的累积,以及采用如下两个方法来避免这一问题:

  • 父进程调用不带 WNOHANG 标志的 wait(),或 waitpid()方法,此时如果尚无已经终止的子进程,那么调用将会阻塞
  • 父进程周期性地调用带有 WNOHANG 标志的 waitpid(),执行针对已终止子进程的非阻塞式检查(轮询)。

这两种方法使用起来都有所不便:

  • 一方面,可能并不希望父进程以阻塞的方式来等待进程的终止
  • 另一方面,反复调用非阻塞的waitpid()会造成CPU资源的浪费,并增加应用程序设计的复杂的。

为了规避这些问题,可以采用针对SIGCHLD信号的处理程序

为SIGCHLD建立信号处理程序

无论一个子进程于何时终止,系统都会向其父进程发送 SIGCHLD 信号。对该信号的默认处理是将其忽略,不过也可以按照信号处理程序来捕获它。在处理程序中,可以使用wait()或者类似方法来收拾僵尸进程。

但是,当调用信号处理程序时,会暂时将引发调用的信号阻塞起来(除非为 sigaction()指定了 SA_NODEFER 标志),且不会对 SIGCHLD 之流的标准信号进行排队处理。这样一来,当SIGCHILD信号处理函数正在为一个终止的子进程运行时,如果相继由两个子进程终止,即使产生了两次SIGCHLD信号,父进程也只能捕获到一个。结果是,如果父进程的SIGCHLD信号处理程序每次只调用一次wait(),那么一些僵尸进程可能会成为“漏网之鱼”。

解决方案是:在SIGCHLD处理的程序内部循环以WNOHANG标准来调用waitpid(),直至再无其他终止的子进程需要处理为止。通常 SIGCHLD 处理程序都简单地由以下代码组成,仅仅捕获已终止子进程而不关心其退出状态

while(waitpid(-1, NULL, WNOHANG) > 0) continue;

上述循环会一直持续下去,直至 waitpid()返回 0,表明再无僵尸子进程存在,或-1,表示有错误发生(可能是 ECHILD,意即再无更多的子进程)

SIGCHLD 处理程序的设计问题

假设创建 SIGCHLD 处理程序的时候,该进程已经有子进程终止。那么内核会立即为父进程产生 SIGCHLD 信号吗?SUSv3 对这一点并未规定。一些源自系统 V(System V)的实现在这种情况下会产生 SIGCHLD 信号;而另一些系统,包括 Linux,则不这么做。为保障可移植性,应用应在创建任何子进程之前就设置好 SIGCHLD 处理程序,将这一隐患消解于无形

需要更深入考虑的问题是可重入性(reentrancy):在信号处理程序中使用系统调用(比如waitpid())可能会改变全局变量errno的值。当主程序试图显示设置errno或是在系统调用失败后检查 errno 值时,这一变化会与之发生冲突。出于这一原因,有时在编写 SIGCHLD 信号处理程序时,需要在一进入处理程序时就使用本地变量来保存 errno 值,而在返回前加以恢复

看个例子:

//通过 SIGCHLD 信号处理程序捕获已终止的子进程
void                    /* Examine a wait() status using the W* macros */
printWaitStatus(const char *msg, int status)
{if (msg != NULL)printf("%s", msg);if (WIFEXITED(status)) {printf("child exited, status=%d\n", WEXITSTATUS(status));} else if (WIFSIGNALED(status)) {printf("child killed by signal %d (%s)",WTERMSIG(status), strsignal(WTERMSIG(status)));
#ifdef WCOREDUMP        /* Not in SUSv3, may be absent on some systems */if (WCOREDUMP(status))printf(" (core dumped)");
#endifprintf("\n");} else if (WIFSTOPPED(status)) {printf("child stopped by signal %d (%s)\n",WSTOPSIG(status), strsignal(WSTOPSIG(status)));#ifdef WIFCONTINUED     /* SUSv3 has this, but older Linux versions andsome other UNIX implementations don't */} else if (WIFCONTINUED(status)) {printf("child continued\n");
#endif} else {            /* Should never happen */printf("what happened to this child? (status=%x)\n",(unsigned int) status);}
}

从程序下面的执行例子可以看出,尽管有 3 个子进程退出,而父进程只捕获到两次 SIGCHLD 信号。

向已停止的子进程发送 SIGCHLD 信号

正如可以使用 waitpid()来监测已停止的子进程一样,当信号导致子进程停止时,父进程也就有可能收到SIGCHLD 信号。调用 sigaction()设置 SIGCHLD 信号处理程序时,如传入 SA_ NOCLDSTOP 标志即可控制这一行为。若未使用该标志,系统会在子进程停止时向父进程发送 SIGCHLD 信号;反之,如果使用了这一标志,那么就不会因子进程的停止而发出 SIGCHLD信号

因为默认情况下会忽略信号 SIGCHLD,SA_NOCLDSTOP 标志仅在设置 SIGCHLD 信号处理程序时才有意义。而且,SA_NOCLDSTOP 只对SIGCHLD 信号起作用

SUSv3 也允许,当信号SIGCONT 导致已停止的子进程恢复执行时,向其父进程发送SIGCHLD信号。(相当于 waitpid()的 WCONTINUED 标志。)始于版本 2.6.9,Linux 内核实现了这一特性

忽略终止的子进程

更有可能像这样处理终止子进程:将对SIGCHLD的处置显式设置为SIG_IGN,系统从而会将其后终止的子进程立即删除,而不是转为僵尸进程。这时,会将子进程的状态之不问,故而所有后续的 wait()(或类似)调用不会返回子进程的任何信息

注意,虽然对信号 SIGCHLD 的默认处置就是将其忽略,但显式设置对 SIG_IGN 标志的处置还是会导致这里所描述的行为差异。在这方面,对信号 SIGCHLD 的处理非常独特,不同于其他信号

如果许多Unix实现一样,在Linux系统中将对SIGCHLD信号的处置置为SIG_IGN并不会影响任何僵尸进程的状态,对它们的等待仍然要照常进行。在其他一些 UNIX 实现中(例如 Solaris 8),将对 SIGCHLD 的处置设置为 SIG_IGN 确实会删除所有已有的僵尸进程。

信号 SIGCHLD 的 SIG_IGN 语义由来已久,源于系统 V(System V)。SUSv3 也规定了此
处所描述的行为,不过原始的 POSIX.1 标准对此则未作表述。因此,在一些较老的 UNIX 实现中,忽略 SIGCHLD 并不影响僵尸进程的创建。要防止产生僵尸进程,唯一完全可移植的方法就是(可能是从SIGCHLD 信号处理程序的内部)调用 wait()或者waitpid()。

Unix/Linux编程:SIGCHLD信号相关推荐

  1. 学习Unix/Linux编程要学些什么

    最近利用空余时间看了一下<Unix/Linux编程实践教程>,原书名为:Understanding Unix/Linux Programming: A Guide to Theory an ...

  2. Unix/Linux编程:进程间通信(IPC)总结

    IPC工具分类 如上,Unix系统上IPC根据功能可以分为三类 通信:这些工具关注进程间的数据交换 同步:这些进程关注进程和线程操作之间的同步 信号:虽然信号的主要作用不为此,但是在特定场景下仍然可以 ...

  3. 《Unix/linux编程实践教程》------重定向程序的I/O

    <Unix/linux编程实践教程>书中举例命令more的用法: $more filename $command | more $more < filename 用法1直接显示fil ...

  4. Unix/Linux编程实践教程–书评

    花了两个月的时间把这本书读完了,完成了一部分的课后习题. 总的来说,这是一本挺好的Unix\Linux编程的入门书(虽然书中的小错误一大堆),书的开始部分简要介绍了Unix系统编程,讲述了如何使用男人 ...

  5. 【Linux】SIGCHLD信号解决僵尸进程问题

    1. 基本信息 SIGCHLD信号产生的条件: 子进程终止时 子进程接收到SIGSTOP信号停止时 子进程处在停止态,接受到SIGCONT后唤醒时 以上三种条件都会给父进程发送SIGCHLD信号,父进 ...

  6. 进程和程序:编写shell——《Unix/Linux编程实践教程》读书笔记(第8章)

    2019独角兽企业重金招聘Python工程师标准>>> 1.Unix shell的功能 shell是一个管理进程和运行程序的程序.所有常用的shell都有3个主要功能: (1)运行程 ...

  7. 【Linux】SIGCHLD信号

    文章目录 SIGCHLD信号 SIGCHLD信号 回忆: 为了避免出现僵尸进程,父进程需要使用wait或waitpid函数等待子进程结束,父进程可以阻塞等待子进程结束,也可以非阻塞地查询的是否有子进程 ...

  8. Unix/Linux编程:fork()进程详解

    文章目录 理论 进程 fork,wait,exec fork 实践 验证 `fork函数被调用一次但返回两次` 子进程和父进程之间不共享数据空间 父子进程间的文件共享 fork的内存语义 同步信号以规 ...

  9. stty详解-Unix/Linux编程实践教程第五章 学习stty

    读书笔记-第五章 连接控制 学习stty 先放上思维导图 为设备编程&设备就像文件 前面所学的知识都是基于文件的,而在unix/linux系统中,所有的设备(打印机,终端,话筒,摄像头等等)也 ...

最新文章

  1. 如何将参数传递给批处理文件?
  2. 机器学习 数据量不足问题----1 做好特征工程 2 不要用太多的特征 3 做好交叉验证 使用线性svm...
  3. java.lang.RuntimeException: Unable to start activity ComponentInfo
  4. c++工程师面试常见问题之c++中四种cast转换
  5. [蓝桥杯]算法提高 天天向上(记忆化搜索)
  6. 网络协议:TCP/IP、SOCKET、HTTP
  7. Redis学习---Redis操作之String
  8. java吧 博客系统_【Java】SpringMVC + Mybatis 实现的个人博客系统
  9. linux判断字符串命令行,bash – 将命令行参数与字符串进行比较
  10. android fragment传递参数_fragment之间传值的两种方法
  11. python课程-Python课程学习总结
  12. ACER 4750G开机卡在LOGO解决办法
  13. 2017年第八届C/C++ B组蓝桥杯省赛真题
  14. 美国国家安全局(NSA)网络攻击主战武器“验证器”
  15. ZCMU 1919 :kirito's 星爆气流斩 (多重背包问题)
  16. java上传图片怎么查看,Java上传图片并查看
  17. 无法打开到主机的连接。 在端口 23: 连接失败
  18. H5移动端实现图片上传
  19. 【实战】物联网安防监控项目【4】———从网页上控制A9的LED灯
  20. 真真切切的100%新手向---安装Arch Linux(更新时间 2018/07-26)

热门文章

  1. 人体口罩佩戴检测实战
  2. 信创是什么意思?涉及哪些行业?为什么要发展信创?
  3. 解决电脑自带office删除之后不能下载或者找到正版office的问题
  4. GO语言凭什么是区块链的首选语言
  5. java用方法重载就圆的面积,java中重载怎么写
  6. Airtest快问快答,你们想问的这里都有!(第1期)
  7. php运算符取整_php运算符 php取整函数
  8. 在Excel中金额大小写一键切换(如果你还在手动输入就OUT了)
  9. 微信公众号开发---踩坑日记
  10. R语言:如何批量导入搜狗词库