如何杀死一个进程?

我们最近在实践 Chaos Engineering,而实现的一个最简单的 Chaos,就是 kill 一个进程。即使是这么一个简单的 Chaos 实验,我也遇到不少有意思的问题,这里记录一下。

首先介绍一个 trick,我们在 Linux 上查找进程的时候,一般会使用 ps -ef | grep nginx ,但是这样一般会在结果中得到两个进程,一个是找到的进程,另一个是 grep 本身。

那么怎么让 grep 不要出现在结果中呢?

比较简单的一种方法是,再 grep 一次就可以啦: ps -ef | grep nginx | grep -v grep 。高级一点的方法可以这样:ps -ef | grep [n]ginx 。原理是 grep 接收一个正则表达式,这样虽然最后还是 match 的 nginx 本身,但是我们运行的命令因为加了 [] 就不直接含有 nginx 这个字符串了。

但是如果要直接 kill 掉进程的话,大可不必先 grep 出来再执行 kill,直接使用 pkill 命令就可以了。

我用来测试的进程名字叫做 host-networking-manager,所以 pkill host-networking-manager ,但是返回的竟然是 1?没成功吗?看了一下,果然没成功,为什么?

最后折腾了一顿发现,pkill 其实是用 pgrep 去找到进程 kill 的。pgrep 找到目标进程是通过 /proc/[pid]/stat 文件。这个文件中的进程名字其实是只有 15个字符长度的。答案在 man 2 prctl 里面:

PR_SET_NAME (since Linux 2.6.9)

Set the name of the calling thread, using the value in the location pointed to by (char *) arg2. The name can be up to 16 bytes long, and should be null-terminated if it contains fewer bytes.

Linux 中的每一个进程都有一个 struct_task_struct 结构体,这个结构体定义在 include/linux/sched.h 里面。

这里面有一个字段 char_comm[TASK_COMM_LEN] 定义了可执行文件的,不包含 Path 的名字,最大长度是 16 bytes,除去最后一个留给 null 的,就只有最多 15 个字符。

可以打开 /proc 下的文件看一下:

所以说,正确 kill 这个进程的方式应该是 pkill host-networking 。

或者使用另一个方法,pkill -f host-networking-manager -f flag 会告诉 pkill 使用 /proc/pid/cmdline 这个文件来匹配进程。这个文件里面包含了进程启动的时候的完整命令,包括参数。(为什么这里我要高亮呢?请继续阅读……)

在尝试使用 -f 参数的时候,我遇到了一个诡异的现象。比如我使用我的 chaos 程序运行一个 yaml 定义的实验的时候。chaos run kill-host-networking-manager 我的实验自己会退出…… 从 log 的信息来看,它也收到了一个 kill 命令。

就在我百思不得其解这个 kill 信号是哪里来的时候,在高人的指点下,原来是我自己发的…… 当我使用 pkill -f host-networking-manager 的时候,由于执行这个命令的进程本身也有 host-networking-manager 这个名字(chaos run kill-host-networking-manager)所以它自己也会匹配上。相当于自己也会把自己杀掉!

那么为什么不使用 -f 参数就没有问题呢?因为在 /proc/pid/stat 文件中,我的父进程叫做 chaos 所以不必匹配到……

 

另外几种比较准确地根据一个名字杀掉进程的方法:

  1. 如果使用 systemd 启动的,可以使用这个命令查看 PID systemctl show –property MainPID <unitfile>.service
  2. pidof 命令也可以准确地找到进程的 PID。

以上 Tips 是 GrayCode 提供的。

Leave a comment

电子邮件地址不会被公开。 必填项已用*标注