big*_*gie 22 python subprocess signals
我正在使用python来管理一些模拟.我构建参数并使用以下命令运行程序:
pipe = open('/dev/null', 'w')
pid = subprocess.Popen(shlex.split(command), stdout=pipe, stderr=pipe)
Run Code Online (Sandbox Code Playgroud)
我的代码处理不同的信号.Ctrl + C将停止模拟,询问我是否要保存,然后正常退出.我有其他信号处理程序(例如强制数据输出).
我想要的是向我的python脚本发送一个信号(SIGINT,Ctrl + C),它将询问用户他想要发送给程序的信号.
阻止代码工作的唯一因素是,无论我做什么,Ctrl + C都将"转发"到子进程:代码将捕获并退出:
try:
<wait for available slots>
except KeyboardInterrupt:
print "KeyboardInterrupt catched! All simulations are paused. Please choose the signal to send:"
print " 0: SIGCONT (Continue simulation)"
print " 1: SIGINT (Exit and save)"
[...]
answer = raw_input()
pid.send_signal(signal.SIGCONT)
if (answer == "0"):
print " --> Continuing simulation..."
elif (answer == "1"):
print " --> Exit and save."
pid.send_signal(signal.SIGINT)
[...]
Run Code Online (Sandbox Code Playgroud)
所以无论我做什么,程序都会收到我只希望我的python脚本看到的SIGINT.我怎样才能做到这一点???
我也尝试过:
signal.signal(signal.SIGINT, signal.SIG_IGN)
pid = subprocess.Popen(shlex.split(command), stdout=pipe, stderr=pipe)
signal.signal(signal.SIGINT, signal.SIG_DFL)
Run Code Online (Sandbox Code Playgroud)
运行程序,但这会得到相同的结果:程序捕获SIGINT.
感谢名单!
Mar*_*ota 18
结合其他一些可以解决问题的答案 - 没有信号发送到主应用程序将被转发到子进程.
import os
from subprocess import Popen
def preexec(): # Don't forward signals.
os.setpgrp()
Popen('whatever', preexec_fn = preexec)
Run Code Online (Sandbox Code Playgroud)
这确实可以使用ctypes
.我不会真的推荐这个解决方案,但我有兴趣做些什么,所以我想我会分享它.
parent.py
#!/usr/bin/python
from ctypes import *
import signal
import subprocess
import sys
import time
# Get the size of the array used to
# represent the signal mask
SIGSET_NWORDS = 1024 / (8 * sizeof(c_ulong))
# Define the sigset_t structure
class SIGSET(Structure):
_fields_ = [
('val', c_ulong * SIGSET_NWORDS)
]
# Create a new sigset_t to mask out SIGINT
sigs = (c_ulong * SIGSET_NWORDS)()
sigs[0] = 2 ** (signal.SIGINT - 1)
mask = SIGSET(sigs)
libc = CDLL('libc.so.6')
def handle(sig, _):
if sig == signal.SIGINT:
print("SIGINT from parent!")
def disable_sig():
'''Mask the SIGINT in the child process'''
SIG_BLOCK = 0
libc.sigprocmask(SIG_BLOCK, pointer(mask), 0)
# Set up the parent's signal handler
signal.signal(signal.SIGINT, handle)
# Call the child process
pid = subprocess.Popen("./child.py", stdout=sys.stdout, stderr=sys.stdin, preexec_fn=disable_sig)
while (1):
time.sleep(1)
Run Code Online (Sandbox Code Playgroud)
child.py
#!/usr/bin/python
import time
import signal
def handle(sig, _):
if sig == signal.SIGINT:
print("SIGINT from child!")
signal.signal(signal.SIGINT, handle)
while (1):
time.sleep(1)
Run Code Online (Sandbox Code Playgroud)
请注意,这会对各种libc结构做出一系列假设,因此可能非常脆弱.运行时,您将看不到消息"来自孩子的SIGINT!" 打印.但是,如果您将通话注释掉sigprocmask
,那么您将会.似乎做的工作:)
POSIX 表示使用execvp(这是 subprocess.Popen 使用的)运行的程序应该继承调用进程的信号掩码。
我可能是错的,但我不认为调用signal
会修改掩码。你想要的sigprocmask
,python不直接公开。
这将是一个 hack,但您可以尝试通过ctypes直接调用 libc 来设置它。我肯定有兴趣看到有关此策略的更好答案。
另一种策略是在主循环中轮询标准输入以获取用户输入。(“按 Q 退出/暂停”——类似的东西。)这回避了处理信号的问题。