Python: 在 运行 子进程中处理中断
Python: handling interrupts whilst running subprocess
我为 tcpdump 编写了一个简单的 Python 解析器。这个想法是不断地运行 tcpdump 作为一个子进程,解析它的输出并在用户请求它们时输出基本报告(通过 Ctrl-Z 中断)而不停止脚本。
Ctrl-C 也应该输出报告并完全退出脚本,这样就可以了。
问题是当我按下 Ctrl-Z 时,中断处理程序被调用,它按预期输出 tcpdump_output
,但随后脚本 stops 处理 tcpdump 子进程的输出,甚至虽然它仍然是 运行ning 在后台(我用 ps 检查过)。
脚本的简化版本:
#!/usr/bin/env python
import subprocess as sub
import socket
import signal
import sys
tcpdump_output = ""
def signal_handler(sig, frame):
print('\nInterrupt detected. Output:')
print(tcpdump_output)
if(sig is signal.SIGINT):
print('Terminated.')
sys.exit(0)
def process_tcpdump_line(line):
print("processing tcpdump line: " + line)
global tcpdump_output
tcpdump_output += line + "\n"
# get host ip address
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
local_ip = s.getsockname()[0]
s.close()
# register interrupt handlers
signal.signal(signal.SIGINT, signal_handler) # Handle Ctrl-C
signal.signal(signal.SIGTSTP, signal_handler) # Handle Ctrl-Z
# prepare tcpdump command
dst = 'dst host ' + local_ip
p = sub.Popen(('sudo', 'tcpdump', '-nqnn', '-l', dst), stdout=sub.PIPE)
# process tcpdump output
for row in iter(p.stdout.readline, b''):
process_tcpdump_line(row.strip())
print("this is never reached.")
我尝试将 for 循环包装在 while True
中,但是在调试时我发现它没有什么不同,最后一行真的没有到达。
似乎在处理中断后,脚本挂起 p.stdout.readline
。
有没有办法在不影响子进程的情况下处理中断,或者以其他方式恢复其处理?
如果您检查 ps aux
输出,您会看到 tcpdump 进程的 STAT
列在您按下 CTRL+Z 后发生了变化:
CTRL+Z 之前:
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
root 15064 0.0 0.0 2463988 1096 s010 S+ 4:40PM 0:00.01 tcpdump -nqnn -l dst host 192.168.1.169
root 15063 0.0 0.0 2461648 2004 s010 S+ 4:40PM 0:00.01 sudo tcpdump -nqnn -l dst host 192.168.1.169
nickolay 15062 0.0 0.0 2432140 7136 s010 S+ 4:40PM 0:00.04 /opt/local/Library/Frameworks/Python.framework/Versions/3.7/Resources/Python.app/Contents/MacOS/Python tcpd.py
CTRL+Z 后:
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
root 15064 0.0 0.0 2463988 1096 s010 T+ 4:40PM 0:00.01 tcpdump -nqnn -l dst host 192.168.1.169
root 15063 0.0 0.0 2461648 2004 s010 T+ 4:40PM 0:00.01 sudo tcpdump -nqnn -l dst host 192.168.1.169
nickolay 15062 0.0 0.0 2432140 7212 s010 S+ 4:40PM 0:00.04 /opt/local/Library/Frameworks/Python.framework/Ver
T
状态means"stopped, either by a job control signal or because it is being traced."
所以问题是子进程也处理CTRL+Z并且是"stopped by job control"。
为了避免这种情况,ignore the signal in the child process:
p = sub.Popen(('sudo', 'tcpdump', '-nqnn', '-l', dst), stdout=sub.PIPE,
preexec_fn = lambda: signal.signal(signal.SIGTSTP, signal.SIG_IGN))
我为 tcpdump 编写了一个简单的 Python 解析器。这个想法是不断地运行 tcpdump 作为一个子进程,解析它的输出并在用户请求它们时输出基本报告(通过 Ctrl-Z 中断)而不停止脚本。
Ctrl-C 也应该输出报告并完全退出脚本,这样就可以了。
问题是当我按下 Ctrl-Z 时,中断处理程序被调用,它按预期输出 tcpdump_output
,但随后脚本 stops 处理 tcpdump 子进程的输出,甚至虽然它仍然是 运行ning 在后台(我用 ps 检查过)。
脚本的简化版本:
#!/usr/bin/env python
import subprocess as sub
import socket
import signal
import sys
tcpdump_output = ""
def signal_handler(sig, frame):
print('\nInterrupt detected. Output:')
print(tcpdump_output)
if(sig is signal.SIGINT):
print('Terminated.')
sys.exit(0)
def process_tcpdump_line(line):
print("processing tcpdump line: " + line)
global tcpdump_output
tcpdump_output += line + "\n"
# get host ip address
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
local_ip = s.getsockname()[0]
s.close()
# register interrupt handlers
signal.signal(signal.SIGINT, signal_handler) # Handle Ctrl-C
signal.signal(signal.SIGTSTP, signal_handler) # Handle Ctrl-Z
# prepare tcpdump command
dst = 'dst host ' + local_ip
p = sub.Popen(('sudo', 'tcpdump', '-nqnn', '-l', dst), stdout=sub.PIPE)
# process tcpdump output
for row in iter(p.stdout.readline, b''):
process_tcpdump_line(row.strip())
print("this is never reached.")
我尝试将 for 循环包装在 while True
中,但是在调试时我发现它没有什么不同,最后一行真的没有到达。
似乎在处理中断后,脚本挂起 p.stdout.readline
。
有没有办法在不影响子进程的情况下处理中断,或者以其他方式恢复其处理?
如果您检查 ps aux
输出,您会看到 tcpdump 进程的 STAT
列在您按下 CTRL+Z 后发生了变化:
CTRL+Z 之前:
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
root 15064 0.0 0.0 2463988 1096 s010 S+ 4:40PM 0:00.01 tcpdump -nqnn -l dst host 192.168.1.169
root 15063 0.0 0.0 2461648 2004 s010 S+ 4:40PM 0:00.01 sudo tcpdump -nqnn -l dst host 192.168.1.169
nickolay 15062 0.0 0.0 2432140 7136 s010 S+ 4:40PM 0:00.04 /opt/local/Library/Frameworks/Python.framework/Versions/3.7/Resources/Python.app/Contents/MacOS/Python tcpd.py
CTRL+Z 后:
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
root 15064 0.0 0.0 2463988 1096 s010 T+ 4:40PM 0:00.01 tcpdump -nqnn -l dst host 192.168.1.169
root 15063 0.0 0.0 2461648 2004 s010 T+ 4:40PM 0:00.01 sudo tcpdump -nqnn -l dst host 192.168.1.169
nickolay 15062 0.0 0.0 2432140 7212 s010 S+ 4:40PM 0:00.04 /opt/local/Library/Frameworks/Python.framework/Ver
T
状态means"stopped, either by a job control signal or because it is being traced."
所以问题是子进程也处理CTRL+Z并且是"stopped by job control"。
为了避免这种情况,ignore the signal in the child process:
p = sub.Popen(('sudo', 'tcpdump', '-nqnn', '-l', dst), stdout=sub.PIPE,
preexec_fn = lambda: signal.signal(signal.SIGTSTP, signal.SIG_IGN))