如何杀死一个进程?

我们最近在实践 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 提供的。



如何杀死一个进程?”已经有4条评论

  1. “原理是 grep 接收一个正则表达式,这样虽然最后还是 match 的 nginx 本身,但是我们运行的命令因为加了 [] 就不直接含有 nginx 这个字符串了。”

    试验了下,确实是这样的,执行 ps -ef | grep [x]xx 的时候可以过滤掉 grep 命令本身。但是为什么执行把ps的输出保存到文件之后,就不其作用了呢?(cat ps.log | grep [n]ginx)而且对于这个 “不直接含有 nginx 这个字符串” 没太理解

    PS:zsh情况不生效,bash下可以。

    • 但是为什么执行把ps的输出保存到文件之后,就不其作用了呢?(cat ps.log | grep [n]ginx)

      这跟保存文件没有关系,zsh 默认使用 globbing,[ 会被 zsh 解释成别的意思,要把 [] 放到引号中。(详细解释

      不直接含有 nginx 这个字符串

      指的是没有出现过 nginx 这几个字符,实际上出现的是 [n]ginx 这几个字符,所以 grep 不会匹配到了。

Leave a comment

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