Python:如何防止子进程接收 CTRL-C / Control-C / SIGINT

问题描述 投票:0回答:5

我目前正在为在 shell 中运行的专用服务器开发一个包装器。包装器通过子进程生成服务器进程,并观察其输出并对其输出做出反应。

必须明确向专用服务器发出正常关闭的命令。因此,CTRL-C 一定不能到达服务器进程。

如果我捕获 KeyboardInterrupt 异常或覆盖 python 中的 SIGINT 处理程序,服务器进程仍然会收到 CTRL-C 并立即停止。

所以我的问题是: 如何防止子进程接收 CTRL-C / Control-C / SIGINT?

python subprocess signals sigint keyboardinterrupt
5个回答
43
投票

#python IRC-Channel (Freenode) 中的某人帮助我指出了 subprocess.Popen(...):preexec_fn

参数

如果 preexec_fn 设置为可调用 对象,该对象将被调用 之前的子进程 孩子被处决。 (仅限 Unix)

因此,以下代码解决了该问题(仅限 UNIX):

import subprocess
import signal

def preexec_function():
    # Ignore the SIGINT signal by setting the handler to the standard
    # signal handler SIG_IGN.
    signal.signal(signal.SIGINT, signal.SIG_IGN)

my_process = subprocess.Popen(
    ["my_executable"],
    preexec_fn = preexec_function
)

注意: 实际上并没有阻止信号到达子进程。相反,上面的 preexec_fn 会覆盖信号的默认处理程序,以便忽略该信号。因此,如果子进程再次覆盖 SIGINT 处理程序,此解决方案可能不起作用。

另一个注意事项:该解决方案适用于各种子流程,即它也不限于用Python编写的子流程。例如,我正在为其编写包装器的专用服务器实际上是用 Java 编写的。


26
投票

结合其他一些答案可以解决这个问题 - 发送到主应用程序的信号不会转发到子进程。

在 Python 3.11 及更高版本中,您可以使用

process_group
参数来表示
Popen
:

from subprocess import Popen

Popen('do_not_want_signals', process_group=0)

在 Python 3.10 及更早版本上,您可以使用

preexec_fn
:

import os
from subprocess import Popen

def preexec(): # Don't forward signals.
    os.setpgrp()

Popen('do_not_want_signals', preexec_fn=preexec)
# The above can be shortened to:
Popen('do_not_want_signals', preexec_fn=os.setpgrp)

9
投票

你可以这样做,让它在 Windows 和 UNIX 中工作:

import subprocess
import sys

def pre_exec():
    # To ignore CTRL+C signal in the new process
    signal.signal(signal.SIGINT, signal.SIG_IGN)

if sys.platform.startswith('win'):
    #https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
    #CREATE_NEW_PROCESS_GROUP=0x00000200 -> If this flag is specified, CTRL+C signals will be disabled
    my_sub_process=subprocess.Popen(["executable"], creationflags=0x00000200)
else:
    my_sub_process=subprocess.Popen(["executable"], preexec_fn = pre_exec)

2
投票

经过一小时的各种尝试,这对我有用:

process = subprocess.Popen(["someprocess"], creationflags=subprocess.DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP)

这是 Windows 的解决方案。


1
投票

在生成子进程之前尝试将 SIGINT 设置为“忽略”(之后将其重置为默认行为)。 如果这不起作用,您需要阅读

作业控制

并学习如何将进程放入其自己的后台进程组中,这样^C甚至不会导致内核发送首先向它发出信号。 (如果不编写 C 帮助程序,在 Python 中可能无法实现。) 另请参阅

这个旧问题

© www.soinside.com 2019 - 2024. All rights reserved.