例如,这不会终止tcpdump:
import subprocess,shlex,time tcpdump_command = "sudo tcpdump -w example.pcap -i eth0 -n icmp" tcpdump_process = subprocess.Popen( shlex.split(tcpdump_command),stdout=subprocess.PIPE,stderr=subprocess.PIPE) time.sleep(1) tcpdump_process.terminate() tcpdump_out,tcpdump_err = tcpdump_process.communicate()
发生了什么?它适用于以前的版本.
It shows the same behavIoUr when running that code while being the root user and while being a regular user + sudo
以普通用户身份运行会引发OSError:[Errno 1] .terminate()上的操作不允许异常(如预期的那样).
以root身份运行会重现问题:sudo和tcpdump进程不会在.terminate()上被杀死,代码会在Ubuntu 15.10上停留在.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
作为@L_403_2@,你在这里不需要sudo:你已经是root(虽然你不应该 – 你可以sniff the network without root).如果你放弃sudo; .terminate()有效.
通常,.terminate()不会递归地终止整个进程树,因此预期孙进程会幸存.虽然sudo是一个特例,from sudo(8) man page:
When the command is run as a child of the
sudo
process,sudo
will
relay signals it receives to the command.emphasis is mine
即,sudo应该将SIGTERM中继到tcpdump和tcpdump
should stop capturing packets on SIGTERM
,from tcpdump(8) man page:
Tcpdump will,…,continue capturing packets until it is
interrupted by a SIGINT signal (generated,for example,by typing your
interrupt character,typically control-C) or a SIGTERM signal
(typically generated with the kill(1) command);
即,预期的行为是:tcpdump_process.terminate()将SIGTERM发送到sudo,它将信号中继到tcpdump,tcpdump应该停止捕获并且两个进程都退出并且.communicate()将tcpdump的stderr输出返回给python脚本.
注意:原则上,可以在不创建子进程的情况下运行命令,from the same sudo(8) man page:
As a special case,if the policy plugin does not define a close
function and no pty is required,sudo
will execute the command
directly instead of calling fork(2) first
因此.terminate()可以直接将SIGTERM发送到tcpdump进程 – 虽然不是解释:sudo tcpdump在我的测试中在Ubuntu 12.04和15.10上都创建了两个进程.
如果我在shell中运行sudo tcpdump -w example.pcap -i eth0 -n icmp,那么kill -SIGTERM会终止这两个进程.它看起来不像Python问题(Python 2.7.3(在Ubuntu 12.04上使用)在Ubuntu 15.10上表现相同.Python 3也在这里失败).
它与进程组(job control)有关:将preexec_fn = os.setpgrp传递给subprocess.Popen(),以便sudo将在一个新进程组(作业)中,它是shell中的领导者,使得tcpdump_process.terminate()在这种情况下工作.
What happened? It works on prevIoUs versions.
Do not forward signals sent by a process in the command’s process
group,do not forward it as we don’t want the child to indirectly kill
itself. For example,this can happen with some versions of reboot
that call kill(-1,SIGTERM) to kill all other processes.emphasis is mine
preexec_fn = os.setpgrp更改了sudo的进程组. sudo的后代如tcpdump进程继承了该组. python和tcpdump不再位于同一进程组中,因此.terminate()发送的信号由sudo中继到tcpdump并退出.
Ubuntu 15.04使用Sudo版本1.8.9p5,问题中的代码按原样运行.
Ubuntu 15.10使用包含the commit的Sudo版本1.8.12.
sudo(8) man page in wily (15.10)仍然只讨论子进程本身 – 没有提到进程组:
As a special case,sudo will not relay signals that were sent by the
command it is running.
它应该是:
As a special case,sudo will not relay signals that were sent by a process in the process group of the command it is running.
您可以在Ubuntu’s bug tracker和/或the upstream bug tracker上打开文档问题.