我们最近在实践 Chaos Engineering,而实现的一个最简单的 Chaos,就是 kill 一个进程。即使是这么一个简单的 Chaos 实验,我也遇到不少有意思的问题,这里记录一下。
首先介绍一个 trick,我们在 Linux 上查找进程的时候,一般会使用 ps -ef | grep nginx
,但是这样一般会在结果中得到两个进程,一个是找到的进程,另一个是 grep 本身。
1 2 3 |
ps -ef | grep host-networking-manager root 2342 370341 0 23:05 pts/0 00:00:00 grep --color=auto host-networking-manager root 386327 1 0 22:43 ? 00:00:00 /usr/local/bin/host-networking-manager |
那么怎么让 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
下的文件看一下:
1 2 |
cat /proc/12356/stat 12356 (host-networking) S 1 12356 12356 0 -1 1077936384 4325 8441 |
所以说,正确 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
所以不必匹配到……
另外几种比较准确地根据一个名字杀掉进程的方法:
- 如果使用 systemd 启动的,可以使用这个命令查看 PID systemctl show –property MainPID <unitfile>.service
pidof
命令也可以准确地找到进程的 PID。
以上 Tips 是 GrayCode 提供的。
“原理是 grep 接收一个正则表达式,这样虽然最后还是 match 的 nginx 本身,但是我们运行的命令因为加了 [] 就不直接含有 nginx 这个字符串了。”
试验了下,确实是这样的,执行 ps -ef | grep [x]xx 的时候可以过滤掉 grep 命令本身。但是为什么执行把ps的输出保存到文件之后,就不其作用了呢?(cat ps.log | grep [n]ginx)而且对于这个 “不直接含有 nginx 这个字符串” 没太理解
PS:zsh情况不生效,bash下可以。
还有一处错误就是,应该是命令为:
systemctl show –property MainPID –value nginx.service
这跟保存文件没有关系,zsh 默认使用 globbing,
[
会被 zsh 解释成别的意思,要把[]
放到引号中。(详细解释)指的是没有出现过 nginx 这几个字符,实际上出现的是 [n]ginx 这几个字符,所以 grep 不会匹配到了。
Pingback: 是谁杀了我? | 卡瓦邦噶!