例如,这不会终止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
作为@Mr.E pointed out in the comments,你在这里不需要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@H_403_37@ 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@H_403_37@,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上打开文档问题.