TL;DR:自 2014 年 5 月 28 日提交发布以来sudo
,不转发命令进程组中的进程发送的信号——python 进程(sudo 的父进程)和 tcpdump 进程(孙子进程)默认位于同一进程组中,因此不将发送的信号转发给进程。sudo 1.8.11
sudo
SIGTERM
.terminate()
tcpdump
当作为 root 用户和作为普通用户 + sudo 运行该代码时,它显示相同的行为
以普通用户身份运行会引发OSError: [Errno 1] Operation not permitted
异常.terminate()
(如预期的那样)。
运行方式root
重现了该问题:进程没有被终止,sudo
并且代码卡在 Ubuntu 15.10 上。tcpdump
.terminate()
.communicate()
相同的代码会杀死 Ubuntu 12.04 上的两个进程。
tcpdump_process
名称具有误导性,因为变量指的是sudo
进程(子进程),而不是tcpdump
(孙子进程):
python
└─ sudo tcpdump -w example.pcap -i eth0 -n icmp
└─ tcpdump -w example.pcap -i eth0 -n icmp
正如@Mr.E 在评论中指出的那样,您不需要sudo
在这里:您已经是 root (尽管您不应该是 - 您可以在没有 root 的情况下嗅探网络)。如果你掉线sudo
;.terminate()
作品。
一般来说,.terminate()
不会递归地杀死整个进程树,因此预计孙进程会存活下来。虽然sudo
是一个特殊情况,来自 sudo(8) 手册页:
当命令作为进程的子sudo
进程运行时,sudo
会将
其接收到的信号中继给命令。重点是我的
即,sudo
应该中继SIGTERM
到tcpdump
并tcpdump
停止在SIGTERM
tcpdump(8) 手册页上捕获数据包:
Tcpdump 将,...,继续捕获数据包,直到它被 SIGINT 信号(例如,通过键入中断字符,通常是 control-C 生成)或 SIGTERM 信号(通常由 kill(1) 命令生成)中断;
即,预期的行为是:tcpdump_process.terminate()
发送 SIGTERM 到sudo
哪个中继tcpdump
应该停止捕获的信号,两个进程都退出并将stderr 输出.communicate()
返回到 python 脚本。tcpdump
注意:原则上该命令可以在不创建子进程的情况下运行,来自同一个 sudo(8) 手册页:
作为一种特殊情况,如果策略插件没有定义关闭函数并且不需要 pty,sudo
将直接执行命令而不是先调用 fork(2)
因此.terminate()
可以将 SIGTERMtcpdump
直接发送到进程——尽管这不是解释:sudo tcpdump
在我的测试中在 Ubuntu 12.04 和 15.10 上创建两个进程。
如果我sudo tcpdump -w example.pcap -i eth0 -n icmp
在 shell 中运行,则kill -SIGTERM
终止这两个进程。它看起来不像 Python 问题(Python 2.7.3(在 Ubuntu 12.04 上使用)在 Ubuntu 15.10 上的行为相同。Python 3 在这里也失败了)。
它与进程组(作业控制)有关:传递preexec_fn=os.setpgrp
给subprocess.Popen()
以便sudo
将在一个新的进程组(作业)中作为领导者,因为tcpdump_process.terminate()
在这种情况下,shell 会起作用。
发生了什么?它适用于以前的版本。
解释在sudo 的源代码中:
不要转发命令进程组中的进程发送的信号,不要转发它,因为我们不希望孩子间接杀死自己。例如,这可能发生在某些调用 kill(-1, SIGTERM) 以杀死所有其他进程的重启版本中。重点是我的
preexec_fn=os.setpgrp
更改sudo
的进程组。sudo
的后代如tcpdump
进程继承该组。python
并且tcpdump
不再在同一个进程组中,因此发送的信号由to.terminate()
中继并退出。sudo
tcpdump
Ubuntu 15.04 使用Sudo version 1.8.9p5
问题中的代码按原样工作的地方。
Ubuntu 15.10 使用Sudo version 1.8.12
包含commit。
wily (15.10) 中的 sudo(8) 手册页仍然只讨论子进程本身——没有提到进程组:
作为一种特殊情况,sudo 不会中继它正在运行的命令发送的信号。
它应该是:
作为一种特殊情况,sudo 不会中继正在运行的命令的进程组中的进程发送的信号。
您可以在Ubuntu 的错误跟踪器和/或上游错误跟踪器上打开文档问题。