Linux 中你可能一直在错误的使用 kill 进程

我先抛出如下问题:

  1. 杀死了父进程后,子进程也会被杀死吗?
  2. 你了解进程组吗?

请你先思考下。再看结论:

graph TB
a[kill 父进程] -->b{父进程 ID 和进程组 ID 是否相同?}
b -->|是| c[父进程和子进程都会被 kill]
b -->|否| d[父进程会被 kill, 但是子进程不一定会被 kill]
c --> e[通过 kill 进程组可以 kill 父进程和全部子进程]
d --> e

classDef mainStep fill:#02d7f2,color:#000
class e mainStep

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

启动进程模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 启动一个虚拟进程。
# ganzhixiong @ ganzhixiongdeMacBook-Pro in ~ [22:52:49]
$ sleep 300 &
[1] 19307

# 再启动一个虚拟进程。
# ganzhixiong @ ganzhixiongdeMacBook-Pro in ~ [22:54:14]
$ sleep 300 &
[2] 19317

# 查看这两个进程。
# ganzhixiong @ ganzhixiongdeMacBook-Pro in ~ [22:54:15]
$ ps j -A | egrep -i 'PGID|sleep' | cut -c 1-200
USER PID PPID PGID SESS JOBC STAT TT TIME COMMAND
ganzhixiong 19307 18589 19307 0 1 SN s010 0:00.00 sleep 300
ganzhixiong 19317 18589 19317 0 1 SN s010 0:00.00 sleep 300
ganzhixiong 19332 18589 19331 0 3 S+ s010 0:00.00 egrep -i PGID|sleep

# 查看这两个进程的父进程。
# ganzhixiong @ ganzhixiongdeMacBook-Pro in ~ [22:54:20]
$ ps -fp 18589
UID PID PPID C STIME TTY TIME CMD
501 18589 18585 0 10:43PM ttys010 0:02.52 -zsh

# 结束这两个进程对应的父进程。
# ganzhixiong @ ganzhixiongdeMacBook-Pro in ~ [22:56:27]
$ sudo kill -9 18589

[进程已完成]

用 Node.js 应用演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# admin @ Main in ~ [23:24:35] C:1
$ ps jf -A | egrep -i 'PGID|homens'
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
24536 26167 26166 24536 pts/0 26166 S+ 1000 0:00 | \_ grep -E --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox -i PGID|homens
1 18911 18907 16898 ? -1 Sl 0 0:00 node /ktt/NewNS/YS-LoraHomeNS/app.js
18911 18927 18907 16898 ? -1 Sl 0 3:58 \_ /ktt/common/download/node-v6.9.2-linux-x64/bin/node /ktt/NewNS/YS-LoraHomeNS/app.js
18927 18946 18907 16898 ? -1 Sl 0 0:35 | \_ /ktt/common/download/node-v6.9.2-linux-x64/bin/node /ktt/NewNS/YS-LoraHomeNS/lib/dispatcher/ThreadLogicDispatcher Main.BJ.KTT/1
18911 18928 18907 16898 ? -1 Sl 0 0:00 \_ /ktt/common/download/node-v6.9.2-linux-x64/bin/node /ktt/NewNS/YS-LoraHomeNS/lib/schedule/Dispatcher/handler Main.BJ.KTT/Master
18911 18991 18907 16898 ? -1 Sl 0 0:01 \_ /ktt/common/download/node-v6.9.2-linux-x64/bin/node /ktt/NewNS/YS-LoraHomeNS/lib/Weather/index Main.BJ.KTT/Master

# admin @ Main in ~ [23:26:50] C:1
$ sudo kill -TERM 18911
[sudo] password for admin:

# 可以看到,结束了父进程,并没有同时结束所有的子进程。并且子进程的父进程的 ID 变成了 1。
# admin @ Main in ~ [23:27:02]
$ ps jf -A | egrep -i 'PGID|homens'
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
24536 26326 26325 24536 pts/0 26325 S+ 1000 0:00 | \_ grep -E --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox -i PGID|homens
1 18928 18907 16898 ? -1 Sl 0 0:00 /ktt/common/download/node-v6.9.2-linux-x64/bin/node /ktt/NewNS/YS-LoraHomeNS/lib/schedule/Dispatcher/handler Main.BJ.KTT/Master
1 18991 18907 16898 ? -1 Sl 0 0:01 /ktt/common/download/node-v6.9.2-linux-x64/bin/node /ktt/NewNS/YS-LoraHomeNS/lib/Weather/index Main.BJ.KTT/Master

通过以上结果,可以得出如下结论:
当结束父进程时,并不一定会结束所有的子进程。

进程组

进程组用 PGID 表示进程组是一个或多个进程(通常与一个作业关联)的集合,可以从同一个终端接收信号。

从上面 Node.js 应用,可以得知该进程组 ID 为 18907。
下面我再举例说明:

1
2
3
4
5
# ganzhixiong @ ganzhixiongdeMacBook-Pro in ~ [0:39:49] 
$ ps j
USER PID PPID PGID SESS JOBC STAT TT TIME COMMAND
ganzhixiong 24846 19638 24846 0 2 SN s010 0:00.00 tail -f /var/log/system.log
ganzhixiong 24847 19638 24846 0 2 SN s010 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn CRON

可以发现 tailgrep 的 PGID 是相同的。

通过结束进程组来结束进程及其全部子进程

1
kill -SIGTERM -18907

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

参考

Linux 中你可能一直在错误的使用 kill 进程

https://ganzhixiong.com/p/b77ef582/

Author

干志雄

Posted on

2021-08-21

Updated on

2021-08-21

Licensed under

Comments