白发人送黑发人

一个普遍的常识是,在Linux里面总是“白发人送黑发人”,子进程死亡,父进程透过wait()等待子进程死亡,并清理子进程僵尸,当然父进程也可以因此而获得子进程的死亡原因。

子曾经曰过:“Talk is cheap. Show me the code”,我们来看看实际的代码:

上述代码中,子进程在第18行通过pause()等待信号,父进程在代码的第22行通过waitpid()等待子进程的结束。其中的参数status是一个输出参数,可以获得子进程死亡的原因。

比如我们现在把上述程序运行起来:

./a.out

child process id: 3320

然后用信号2去杀死这个子进程3320:

kill -2 3320

父进程waitpid()返回,然后status里面获知原因,父进程打印:

child process is killed by signal 2

如果我们把子进程中的pause()删除,改为直接退出_exit(1):

则父进程探测到子进程死亡后,可打印它的退出状态:

$ ./a.out

child process id: 3362

child process exits, status=1

由此可以看出,父进程对子进程的死亡和死亡原因是了如指掌。

这一点从内核的源代码里面也可以看出来:

在wait_task_zombie()中,父进程会透过子进程的僵尸分析获得子进程的exit_code组合,并进一步拼装status。

事出必有因

那么,父进程为什么必须知道子进程的死亡呢?父进程为什么一定要苦苦地知道子进程的死亡原因?

前一个问题很好回答,如果我们用init进程启动了一个httpd的服务供客户访问我们的网站,然后httpd进程半夜挂了。首先,作为公司的网管,他无法知道httpd死了;其次,他如果知道httpd死了,他也不可能半夜开车去把httpd命令重新输入一遍。所以,这个过程应该由Linux的某种机制自动完成,比如如果init知道了httpd死亡的话,它可以内在地自动重新启动一个httpd进程。

后一个问题稍微有点复杂,我们要结合一个实际的init项目的例子来解答。这里我们以systemd为例。systemd是目前主流Linux发型版采用的init项目,比如我的Ubuntu 18.10就是:

$ ls -l /sbin/init

... /sbin/init -> /lib/systemd/systemd

/sbin/init是systemd的一个符号链接。

我们在systemd里面,如果要添加一个开机就启动的后台服务,可以在/lib/systemd/system/目录增加一个service文件。比如,这里我增加了一个非常简单地service文件:

/lib/systemd/system/simple-server.service

它的内容如下:

simple-server是我写的一个极限简单的打印hello world的服务:

我们在Ubuntu中使能这个服务:

$ sudo systemctl enable simple-server

Created symlink

/etc/systemd/system/multi-user.target.wants/simple-server.service

→ /lib/systemd/system/simple-server.service.

当场开始这个服务:

$ sudo systemctl start simple-server

接下来我们查询下状态,发现是active的:

这个时候我们在系统里面是可以看到simple-server这个进程的,它是顶层systemd这个init进程(PID为1)的子进程:

这个时候,我们把simple-server这个进程杀掉:

$ sudo killall simple-server

再次查看状态:

这个时候,我们看到systemd已经检测出来simple-server对应的进程已经被TERM信号kill,服务的状态是inactive。

我们发现simple-server这个进程也不复存在:

pidof什么都没有!!!

pidof什么都没有!!!

pidof什么都没有!!!

你刚才不是说init检测到service死了后,“可以”自动重启服务吗?比如init重新启动httpd?那么,现在我杀死了simple-server,为什么systemd没有自动重新启动它呢?

注意我说的是“可以”,不是说“必须”。

因地制宜

实际上,在systemd里面,一个服务死亡后,要不要重新启动,什么情况下要重新启动,都是可以由用户来定制的。

我们可以在.service文件的[Service]里面的Restart字段写明什么情况下,我们应该重新启动死亡的子进程。比如,我们可以在.service文件中,增加一行:

第6行的Restart=always,实际含义是,无论simple-server因为什么原因死掉,都无条件重新启动它。

systemd的文档

https://www.freedesktop.org/software/systemd/man/systemd.service.html

里面有一张表,

详细解释了Restart设置为no、always、on-success、on-failure等各种情况下,systemd是否要重新启动这个service。所以systemd实际上区分了5种不同的原因,可进一步阅读:

服务是否重新启动

If set to no (the default), the service will not be restarted. If set to on-success, it will be restarted only when the service process exits cleanly. In this context, a clean exit means an exit code of 0, or one of the signals SIGHUP, SIGINT, SIGTERM or SIGPIPE, and additionally, exit statuses and signals specified in SuccessExitStatus=. If set to on-failure, the service will be restarted when the process exits with a non-zero exit code, is terminated by a signal (including on core dump, but excluding the aforementioned four signals), when an operation (such as service reload) times out, and when the configured watchdog timeout is triggered. If set to on-abnormal, the service will be restarted when the process is terminated by a signal (including on core dump, excluding the aforementioned four signals), when an operation times out, or when the watchdog timeout is triggered. If set to on-abort, the service will be restarted only if the service process exits due to an uncaught signal not specified as a clean exit status. If set to on-watchdog, the service will be restarted only if the watchdog timeout for the service expires. If set to always, the service will be restarted regardless of whether it exited cleanly or not, got terminated abnormally by a signal, or hit a timeout.

systemd作为一个父进程,完全可以根据子进程的死亡原因,决定进一步的对策。比如,我们设置为on-failure,含义就是进程不正常死亡(比如exit code不是0、被会引起coredump的信号比如segment fault而死)的情况下,我们才重新启动它。

这个完全可以根据真实service的特点而量身定制。比如,对于oneshot的服务(就是开机只需要运行一次的服务,比如开机进行某种设置,完成一个文件系统的check,完成了就自动退出的进程)。这种,我们就不可能执行:

Restart=always

或者

Restart=on-success

因为,既然这个oneshot服务已经成功执行了,我们没必要再次启动它。

更多多进程编程细节,欢迎报名在线课程《Linux系统编程两驾马车》之《多进程编程》,1月13日开课,早鸟报名1月8日结束。“多进程”编程部分的内容涉及多进程生命周期、init进程(以systemd为例)、系统服务、守护进程、进程间通信(IPC,信号、信号量、socket等)、共享内存、dma-buf设备级内存共享、以及多进程的调试技巧。

报名方法,点击:

1月13日晚开课:《Linux系统编程两驾马车》之《多进程编程》

(完)

Linux阅码场原创精华文章汇总

更多精彩,尽在"Linux阅码场",扫描下方二维码关注

点一点右下角”在看”,为阅码场打Call~

Linux中父进程为何要苦苦地知道子进程的死亡原因?相关推荐

  1. Linux中的进程之初步了解

    一.概念的理解 二.进程的属性 一.概念的理解 首先程序与进程是什么?程序与进程又有什么区别? 程序(procedure):不太精确地说,程序就是执行一系列有逻辑.有顺序结构的指令,帮我们达成某个结果 ...

  2. 专业介绍Linux中的进程管理

    Linux中的进程管理 1.什么是进程? 1)进程就是系统中处于执行期的工作. 对于[执行]两个字需要作出特别解释:执行并不同于运行,因为系统中的进程的状态大概分为四种: 进程状态 R(Running ...

  3. Linux中的进程创建函数fork

    为什么80%的码农都做不了架构师?>>>    Linux中的进程通过fork创建,并通过exec执行,分为两步. 在Linux中所有的进程都是pid为1的init进程的子进程,内核 ...

  4. Linux 的父进程和子进程的执行情况(附有案例代码)

    系列文章目录 该文章主要是针对面试做大致的了解,通俗易懂!!! 一.父进程.子进程的定义 1.父进程 指已创建一个或多个子进程的进程.在Linux里,除了进程0以外的所有进程都是由其他进程使用系统调用 ...

  5. Linux中的进程、线程和文件描述符

    说到进程,恐怕面试中最常见的问题就是线程和进程的关系了,那么先说一下答案: 在 Linux 系统中,进程和线程几乎没有区别 . Linux 中的进程就是一个数据结构,看明白就可以理解文件描述符.重定向 ...

  6. linux中每个进程都有唯一的进程标识,Linux进程标识

    1.进程标识 (1)进程标识说明 每个进程都有一个非负整型的唯一进程ID.因为进程ID标识符总是唯一的,常将其用作其他标识符的一部分以保证其唯一性. 在Linux中,进程ID 0是调度进程,常常被称为 ...

  7. Linux中的进程是僵尸进程还是僵死进程

    在 Linux 中,进程可能是僵尸进程或僵死进程. 僵尸进程是一种已经结束运行但还没有被父进程回收的进程.当父进程没有调用 wait 或 waitpid 函数来回收子进程的结束状态时,子进程就会成为僵 ...

  8. linux:进程占用的端口,在linux中查看进程占用的端口号

    在Linux 上的 /etc/services 文件可以查看到更多关于保留端口的信息. 可以使用以下六种方法查看端口信息. ss:可以用于转储套接字统计信息. netstat:可以显示打开的套接字列表 ...

  9. Linux两个进程交换信息,如何在Linux中的进程之间交换二进制数据

    我需要创建一个可以进行无线网络扫描的linux应用程序,将结果放入一个结构中并以某种方式将其发送到另一个将使用该数据的主应用程序.我最初的想法是在主应用程序中创建一个管道,fork并通过execl启动 ...

最新文章

  1. idea console中文乱码_Python3的字符编码乱码问题解决思路
  2. sql查询从m到n的这几条记录
  3. mysql查询某张表的所有外键_oracle中查询所有外键引用到某张表的记录
  4. 无悔入华夏怎么一直显示服务器,无悔入华夏新手开局玩法 无悔入华夏新手攻略开局带的...
  5. PyTorch深度学习:60分钟入门(Translation)
  6. 大脑遗忘与数据结构中的对列相似
  7. jQuery学习-事件之绑定事件(五)
  8. linux mattrib 命令详解
  9. 光栅透过率计算 (Matlab)
  10. 【原创】VBA学习笔记(313)VBA字典相关:遍历字典,用key查item, 用item查key的方法
  11. stream has already been operated upon or closed错误
  12. 基于语音的疲劳度检测算法研究
  13. 计算机游戏英文文献,JAVA游戏英文文献翻译
  14. 记录:FC-SAN与IP-SAN比较
  15. Spring 源码解读第七弹!bean 标签的解析
  16. 计算机考研自我介绍大概多少字,考研复试英语自我介绍多少字合适呢
  17. EasyJWeb Tools中代码自动生成引擎详解
  18. 菜鸟成长记-各种奇葩错误总结
  19. 实验11-1-9 藏尾诗 (20分)
  20. 在快手,玩转世界杯的100种方法

热门文章

  1. Solidity之事件
  2. TDOP技术C++实现
  3. 水煮三国第二章:能把梳子卖给和尚吗?
  4. 【Spring实战】----Security4.1.3鉴权之美--基于投票的AccessDecisionManager实现及源码分析
  5. 机器学习算法 04 —— 决策树(ID3、C4.5、CART,剪枝,特征提取,回归决策树)
  6. Java找不到目标文件,java-构建后生成的目标文件不包含必须已经...
  7. 梅科尔工作室—罗森—鸿蒙笔记4
  8. react - 颜色选择器
  9. Anaconda安装caffe(超简单)
  10. ev3dev:c语言开发lego ev3主机