• 原文地址:Killing a process and all of its descendants
  • 原文作者:igor_sarcevic
  • 译文出自:掘金翻译计划
  • 本文永久链接:https://github.com/xitu/gold-miner/blob/master/TODO1/killing-a-process-and-all-of-its-descendants.md
  • 译者:江五渣
  • 校对者:TokenJan,portandbridge

如何杀死一个进程和它的所有子进程

在类 Unix 系统中杀死进程比预期中更棘手。上周我在调试一个在 Semaphore 中终止作业的问题。更具体地说,这是一个有关于在作业中终止正在运行的进程的问题。以下是我从中学到的要点:

  • 类 Unix 操作系统有着复杂的进程间关系:父子进程、进程组、会话、会话的领导进程。但是,在 Linux 与 MacOS 等操作系统中,这其中的细节并不统一。符合 POSIX 的操作系统支持使用负 PID 向进程组发送信号。
  • 使用系统调用向会话中的所有进程发送信号并非易事。
  • 用 exec 启动的子进程将继承其父进程的信号配置。例如,如果父进程忽略 SIGHUP 信号,它的子进程也会忽略 SIGHUP 信号。
  • “孤儿进程组内发生了什么”这一问题的答案并不简单。

杀死父进程并不会同时杀死子进程

每个进程都有一个父进程。我们可以使用 pstree 或 ps 工具来观察这一点。

# 启动两个虚拟进程
$ sleep 100 &
$ sleep 101 &$ pstree -p
init(1)-+|-bash(29051)-+-pstree(29251)|-sleep(28919)`-sleep(28964)$ ps j -APPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND0     1     1     1 ?           -1 Ss       0   0:03 /sbin/init
29051  1470  1470 29051 pts/2     2386 SN    1000   0:00 sleep 100
29051  1538  1538 29051 pts/2     2386 SN    1000   0:00 sleep 101
29051  2386  2386 29051 pts/2     2386 R+    1000   0:00 ps j -A1 29051 29051 29051 pts/2     2386 Ss    1000   0:00 -bash

调用 ps 命令可以显示 PID(进程 ID) 和 PPID(父进程 ID)。

我对父子进程间的关系有着错误的假设。我认为如果我杀死了父进程,那么也会杀死它的所有子进程。然而这是错误的。相反,子进程将会成为孤儿进程,而 init 进程将重新成为它们的父进程。

让我们看看通过终止 bash 进程(sleep 命令的当前父进程)来重建进程间的父子关系后发生了哪些变化。

$ kill 29051 # 杀死 bash 进程$ pstree -A
init(1)-+|-sleep(28919)`-sleep(28965)

于我而言,重新分配父进程的行为很奇怪。例如,当我使用 SSH 登录一台服务器,启动一个进程,然后退出时,我启动的进程将会被终止。我错误地认为这是 Linux 上的默认行为。当我离开一个 SSH 会话时,进程的终止与进程组、会话的领导进程和控制终端都有关。

什么是进程组和会话领导进程?

让我们再次观察上述事例中 ps j 命令的输出。

$ ps j -APPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND0     1     1     1 ?           -1 Ss       0   0:03 /sbin/init
29051  1470  1470 29051 pts/2     2386 SN    1000   0:00 sleep 100
29051  1538  1538 29051 pts/2     2386 SN    1000   0:00 sleep 101
29051  2386  2386 29051 pts/2     2386 R+    1000   0:00 ps j -A1 29051 29051 29051 pts/2     2386 Ss    1000   0:00 -bash

除了使用 PPID 和 PID 表示的父子进程关系外,进程间还有其他两种关系:

  • 用 PGID 表示的进程组
  • 用 SID 表示的会话

我们可以在支持作业控制的 Shell 环境中观察到进程组,例如 bash 和 zsh,它们为每个管道命令都创建了一个进程组。进程组是一个或多个进程(通常与一个作业关联)的集合,可以从同一个终端接收信号。每个进程组都有一个唯一的进程组 ID。

# 启动一个由 tail 和 grep 命令组成的进程组
$ tail -f /var/log/syslog | grep "CRON" &$ ps jPPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29051 19701 19701 29051 pts/2    19784 SN    1000   0:00 tail -f /var/log/syslog
29051 19702 19701 29051 pts/2    19784 SN    1000   0:00 grep CRON
29051 19784 19784 29051 pts/2    19784 R+    1000   0:00 ps j
29050 29051 29051 29051 pts/2    19784 Ss    1000   0:00 -bash

请注意,在前半段中,tail 和 grep 的 PGID 是相同的。

会话是进程组的集合,通常由一个控制终端和一个会话领导进程组成。如果会话中有一个控制终端,它就具有单个前台进程组,除了该控制终端,会话中的所有其他进程组都是后台进程组。

并非所有的 bash 进程都是会话,但是当你使用 SSH 登录一台远程服务器时,你通常会得到一个会话。当 bash 作为会话领导进程运行时,它将 SIGHUP 信号传播给它的子进程。SIGHUP 信号的传播方式就是我一直以来坚信子进程会与父进程一起消亡的核心原因。

在 Unix 中会话的实现并非一致

在上述事例中,你可以注意到 SID (进程的会话 ID)出现的位置。它是会话中所有进程共享的 ID。

但是,你需要记住,并非所有的 Unix 系统都遵循这一实现。单一 UNIX 规范只讨论“会话领导进程”,没有类似于进程 ID 或进程组 ID 的“会话 ID”。会话领导进程是一个具有唯一进程 ID 的单进程,因此我们可以讨论的会话 ID 是会话领导者的进程 ID。

System V Release 4 引入了会话 ID。

实际上,这意味着你能在 Linux 上通过 ps 命令获取会话 ID,但是在 BSD 及其变体(如 MacOS)上,会话 ID 并不存在,或始终为零。

杀死进程组或会话中的所有进程

我们可以使用该 PGID,通过 kill 命令向整个进程组发送信号:

$ kill -SIGTERM -- -19701

我们用一个负数 -19701 向进程组发送信号。如果我们传递的是一个正数,这个数将被视为进程 ID 用于终止进程。如果我们传递的是一个负数,它被视为 PGID,用于终止整个进程组。

负数来自系统调用的直接定义。

杀死会话中的所有进程与之完全不同。如我们在前一节说到的,有些系统没有会话 ID 的概念。即使是具有会话 ID 的系统,例如 Linux,也没有提供系统调用来终止会话中的所有进程。你需要遍历 /proc 输出的进程树,收集所有的 SID,然后一一终止进程。

Pgrep 实现了遍历、收集并通过会话 ID 杀死进程的算法。使用以下命令:

pkill -s <SID>

被 nohup 忽略的信号传播到子进程

被忽略的信号,就像是被 nohup 忽略的信号那样,都被传播到进程的所有子进程中。这种信号传播方式就是我上周在 bug 排查中遇到的最终瓶颈。

我的程序是用于运行 bash 命令的代理程序,而我在该程序中验证到的是,我已经建立了一个具有控制终端的 bash 会话。该控制终端是 bash 会话中其他启动进程的会话领导进程。我的进程树如下所示:

agent -++- bash (session leader) -+| - process1| - process2

我假设,当我使用 SIGHUP 杀死 bash 会话时,它的子进程也会同时终止。对代理的集成测试也证明了这一点。

但是,我忽略了这个代理是以 nohup 启动的。当你使用 exec 启动子进程时,就像我们在代理中启动 bash 进程一样,它会从它的父进程继承信号状态。

最后一个结论使我惊讶万分。

如果发现译文存在错误或其他需要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 本文永久链接 即为本文在 GitHub 上的 MarkDown 链接。

Linux 如何杀死一个进程和它的所有子进程相关推荐

  1. 在 Linux 中杀死一个进程

    在 Linux 中,假如一个进程的 PID 为 3810,那么结束一个进程可以使用如下命令: $ kill -9 3810 以 Postman 为例,首先我们需要找到它的进程号,然后才能杀死. 查找进 ...

  2. Linux kill 杀死指定进程

    Linux kill 杀死指定进程 一  杀死指定进程 现知道有一个php线程正在运行,需要杀死 root 26278 1 0 2015 ? 00:00:31 /usr/local/php/bin/p ...

  3. linux如何kill僵尸进程,linux 如何杀死僵尸进程——原理及操作

    linux 如何杀死僵尸进程 Posted on 2011 年 9 月 20 日 by Open-Source In UNIX System terminology, a process that h ...

  4. linux杀死指定名称的进程,Linux kill 杀死指定进程

    一  杀死指定进程 现知道有一个php线程正在运行,需要杀死 root 26278 1 0 2015 ? 00:00:31 /usr/local/php/bin/php /var/www/html/r ...

  5. linux查看进程grep工作组,Linux下查看一个进程打开了哪...-linux 如何找到进程的工作目录...-使用 grep 恢复误删的文本文件_169IT.COM...

    Linux下查看一个进程打开了哪些文件的命令示例,供大家学习参考. 查看进程14755(httpd)打开了哪些文件: 代码如下: localhost:~# lsof -p 14755 COMMAND ...

  6. linux启动一个进程吗,当你在Linux上启动一个进程时会发生什么?

    本文是关于 fork 和 exec 是如何在 Unix 上工作的.你或许已经知道,也有人还不知道.几年前当我了解到这些时,我惊叹不已. 我们要做的是启动一个进程.我们已经在博客上讨论了很多关于系统调用 ...

  7. linux启动一个进程吗,你知道,当你在 Linux 上启动一个进程时会发生什么嘛?

    原标题:你知道,当你在 Linux 上启动一个进程时会发生什么嘛? 本文是关于 fork 和 exec 是如何在 Unix 上工作的.你或许已经知道,也有人还不知道.几年前当我了解到这些时,我惊叹不已 ...

  8. linux如何启动一个进程而不阻塞,当你在 Linux 上启动一个进程时会发生什么? | Linux 中国...

    原标题:当你在 Linux 上启动一个进程时会发生什么? | Linux 中国 本文是关于 fork 和 exec 是如何在 Unix 上工作的.你或许已经知道,也有人还不知道.几年前当我了解到这些时 ...

  9. 如何在linux中关闭一个进程

    如何在linux中kill一个进程 刚开始学习Linux的时候,总是直接点击×来关闭程序.虽然知道这是关闭整个终端程序,但是奈何如此简单的事情对于新手来说都是很困难的.

最新文章

  1. RHEL5下构建LVS负载均衡系统详解(二)
  2. javascript动画函数封装(升级版)
  3. eclipse中使用svn提交,更新代码。
  4. 简述configure,pkg-config,pkg_config_path三者的关系
  5. 常用的遍历文件夹批处理命令
  6. mysql+rownumber的用法_mysql中如何实现row_number
  7. Java堆、栈、内存分析
  8. H - Message Bomb Gym - 102798H
  9. mysql dns反向解析_Mysql DNS反向解析导致连接超时过程分析(skip-name-resolve)
  10. 水滴石穿C语言之extern声明辨析
  11. Esfog_UnityShader教程_UnityShader语法实例浅析
  12. 完美解决IE(IE6/IE7/IE8)不兼容HTML5标签的方法
  13. 软件工程的持续交付(CDF)和规范
  14. Django学习笔记二
  15. hdu 3065 病毒侵袭持续中
  16. 《数据库原理与应用》课程实验报告三 --数据库的嵌套查询
  17. android获取版本信息、屏幕信息和设备编号
  18. RFSoC应用笔记 - RF数据转换器 -05- RFSoC关键配置之RF-ADC内部解析(三)
  19. 数字 一阶低通滤波器 详细分析 冰三点水
  20. 一、JSX语法的基本使用

热门文章

  1. MySQL安装的requirements问题
  2. [附源码]Java计算机毕业设计SSM宠物短期寄养平台
  3. Python知识点解析之urlopen()讲解
  4. python3中urlopen_urlopen仅适用于Python3中的某些URL
  5. 谷歌浏览器安装VUE插件
  6. CocosCreator之KUOKUO趣味文章:小怪A星寻路详解
  7. Android实现文档在线预览功能
  8. 致一样不甘于现状的你我
  9. java开发选 e3v3 i5,【选CPU必看】i5、E3、i7之间的区别以及到底怎么选?干货!
  10. android 缺省页设计,缺省页设计