问题描述

起初是为了 systemd 的 service 单元文件中的 ExecStop 指令才整理的这篇文章,后来看 systemd 的文档说执行 stop 时,执行完 ExecStop 指令后,未结束的进程会由 systemd 来结束。

本来没有什么可写的,直接使用 kill(1) 命令来结束进程就可以了。但是,由几个有意思的问题:
1)如何结束一个进程的全部子进程?
2)如何结束一个进程及其子进程?
3)我想结束某个组或某个用户的进程该怎么做?

通常结束一个进程的时候,它的子进程不一定会退出,子进程可能会变成“孤儿进程”:

 

孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

 

所以有的时候,我们就需要结束某个进程以及它的子进程。

我们先从基础的开始吧。

根据进程号,结束进程

在 Linux 中,想要结束一个进程可以直接使用kill(1)命令并指定进程ID(PID)就可以了。比如,我们想要结束PID为3219的进程,只需要执行如下命令:

kill -9 3219
kill -KILL 3219

上面的两个命令是100%完全等价的,只是形式不一样而已,具体参考 kill(1) 手册。唯一需要注意:在 Shell 中,内建的kill命令,参考「注意事项」部分。

根据父进程号,结束子进程

如果想要结束进程所有的子进程的ID,可以使用pkill(1)命令。比如,我们想要结束PID为3219的进程的子进程,只需要执行如下命令:

pkill -KILL -P 3219

该命令可以结束某个进程的全部子进程。

结束进程及其全部子进程

方法一、使用进程组

可以使用kill(1)命令或者pkill(1)命令,配合“进程组”的ID来结束进程。比如进程组的ID为4536如下:

kill -KILL -4536
pkill -KILL -g 4536

注意,这里的“4536”是“进程组”的ID,不是进程ID,不是父进程ID,也不是进程所属组的ID,有一个名词叫做“进程组”(Process Group)。可以用下面的命令来体会组ID(gid)、进程组ID(pgid)及其他ID之间的差异,注意观察各个字段的输出:

ps -o pid,ppid,pgid,gid,sess,cmd -U root

上面的ID分别是进程ID、父进程ID、进程组的ID、进程的组ID、会话、命令。

方法二、使用一点 Shell 命令

但是下面的这条命令应该是一劳永逸了:

kill -KILL $(ps -o pid= --pid 6234 --ppid 6234)

上述命令结束进程ID为6234的全部进程以及它的子进程。

根据GID结束进程

如果要结束属于某个组的进程,可以使用pkill(1)命令。如下,结束GID为34的全部进程:

pkill -KILL -G 34
pkill -KILL -G mail

结束GID为34的组的全部进程。代表,GID的数值34也可以使组名来代替,如上的两个命令是等价的,因为GID为34的组名为“mail”。

根据终端来结束进程

可以使用pkill(1)命令,根据/dev/下的终端来结束进程:

pkill -9 -t 'pts/4'

结束终端为“pts/4”的进程,不需要“/dev/“前缀。但这也仅仅是适用于关联终端的进程。有些后台进程没有与终端关联。

根据进程名来结束进程

可以使用 pkill 命令,配合进程名来结束进程。如下示例,结束进程名为“xterm”的进程:

pkill -KILL -x "xterm"

根据会话来结束进程

依旧是使用 pkill 命令来结束进程。如下示例,结束会话号为 1256 的全部进程:

pkill -KILL -s 1256

结束僵尸进程(Kill Zombie Processes)

Alternative way to kill a zombie process - Unix & Linux Stack Exchange
linux - How to kill zombie process - Stack Overflow
How to find zombie process? - Ask Ubuntu

通过如下方法定位僵尸进程:

ps aux | grep 'Z'

通过如下方法结束僵尸进程:

// 向父进程发送 CHID 信号(但是可能失败,因为父进程可能没有合理处理 CHLD 信号)

kill -CHLD <PPID>

// 或者,通过结束父进程的方式,来结束僵尸进程

kill -KILL <PPID>

注意事项

内建于 Shell 的 kill 命令

在 Shell 中(比如 BASH),它可能内建 kill 命令。可以使用 type -a kill 命令进行查看:

# type -a kill
kill is a shell builtin
kill is /bin/kill

如果输出的第一行为”kill is a shell builtin“,这就表示你所执行的kill命令Shell内建的kill命令,而不是util-linux包中的kill(1)命令。而Shell内建kill命令的用法需要查看你所使用的Shell的文档,这里不再展开说明。如果你要使用util-linux软件包中的kill(1)命令,可以使用“绝对路径”(/bin/kill ...)或者“env”(env kill ...)来执行。还有一点要注意procp-ng软件包也提供了kill命令,所以到底使用的哪个包里的kill命令呢?这个需要你自己去判断了,但是看手册肯定不会错的。这些kill命令的用法大同小异,因此也没有什么可以担心的。

关于-KILL(-9)信号

如果关心数据完整性,请勿使用 -KILL(-9)信号,它会直接杀死进程。所有理智的进程都应该会处理 -TERM(-15)信号,通知进程结束自身。