Windows下子进程未接收到信号(SIGTERM)

5
我有一个服务器,启动了一个子进程。我可以通过send_signal(SIGTERM)来杀死该进程,但是不够优雅。 如果我从shell中调用子进程(即作为单个进程),则定义的信号处理程序将会触发并优雅地退出。
server.py: (所以,从另一个脚本中,我首先调用start_app(),然后调用exit_app()
def start_app():
    app = subprocess.Popen("python app.py")

def exit_app():
    p = app.poll()
    if p==None:
        print("Subprocess is alive") # debug
    app.send_signal(signal.SIGTERM)

app.py

def exit_signal_handler(signal, frame):
    print("Terminate signal received")
    app.exit()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    signal.signal(signal.SIGTERM, exit_signal_handler)
    signal.signal(signal.SIGINT, exit_signal_handler)
    sys.exit(app.exec())

再次说明,如果我从shell中调用app.py并发送SIGTERM信号,我会得到一个跟踪信息Terminate signal received,然后app关闭。但是当app.py由服务器启动并且我在服务器中调用exit_app时,我会得到一个跟踪信息Subprocess is alive(来自server.py),app被杀死,但信号没有被捕获在app的信号处理程序exit_signal_handler中。
编辑: 似乎send_signal()并没有向子进程发送信号,以至于子进程能够捕获该信号。它向子进程发送一个信号,以执行某个操作:
    def send_signal(self, sig):
        """Send a signal to the process
        """
        if sig == signal.SIGTERM:
            self.terminate()
        elif sig == signal.CTRL_C_EVENT:
            os.kill(self.pid, signal.CTRL_C_EVENT)
        elif sig == signal.CTRL_BREAK_EVENT:
            os.kill(self.pid, signal.CTRL_BREAK_EVENT)
        else:
            raise ValueError("Unsupported signal: {}".format(sig))

这可能回答了我的问题,但我会让它保持开放状态...

@georgexsh killed = 结束,退出,进程不再在CPU上运行。而poll()只是为了检查子进程是否真的还活着,然后才发送SIGTERM - niCk cAMel
@georgexsh 嗯...好观点。我明白你的意思,但是在__main__中发生的事情在以subprocess()方式运行时会被打印出来。我也在那里使用了print(),只是这里没有包含它。 - niCk cAMel
您可以通过将日志写入文件并检查app.poll()的返回值是否为0或-15来确认此操作,其中0表示正常退出且信号处理程序正常工作。 - georgexsh
编辑是signal.send_signal()例程的复制粘贴。是的,Windows系统。 - niCk cAMel
糟糕,我假设是Linux系统。 - georgexsh
显示剩余2条评论
1个回答

10
作为Windows用户,SIGTERM处理程序是无用的,更多参考资料

在Windows上,C运行时实现了标准C所需的六个信号:SIGINT、SIGABRT、SIGTERM、SIGSEGV、SIGILL和SIGFPE。

SIGABRT和SIGTERM仅为当前进程实现。

但你可以使用signal.CTRL_BREAK_EVENT作为替代方案
即在app.py中创建一个信号处理程序来处理SIGBREAK,但从父进程发送CTRL_BREAK_EVENT。另外,请确保使用creationflags=subprocess.CREATE_NEW_PROCESS_GROUP启动子进程(否则它也会杀死父进程)。
app = subprocess.Popen("python app.py", shell=True, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
time.sleep(1)

while 1:
    p = app.poll()
    if p is not None:
        break
    app.send_signal(signal.CTRL_BREAK_EVENT)
    time.sleep(2)

app.py:

exit = False

def exit_signal_handler(signal, frame):
    global exit
    print("Terminate signal received")
    exit = True

signal.signal(signal.SIGBREAK, exit_signal_handler)
while not exit:
    pass

也许我表达不够清晰,可能需要编辑一下问题。在这两种情况下,应用程序都会被 SIGTERM 终止,但是当作为子进程运行时,app.py 中的信号处理程序没有被调用... 应用程序只是迅速地被终止了。 - niCk cAMel
1
不错的参考资料。问题是,如果我注册了 SIGINT 并在键盘上按下 ctrl+c,我的处理程序会捕获该信号... 但是当作为子进程运行时,我无法从脚本中发送信号,因为 send_signal(signal.SIGINT) 抛出错误(如我的编辑中所示)。这一切都很奇怪。 - niCk cAMel
啊啊啊...你注册了SIGBREAK但发送的是CTRL_BREAK_EVENT - niCk cAMel
3
谢谢!除了 CTRL_C_EVENT + SIGBREAK,这个 creationflags=subprocess.CREATE_NEW_PROCESS_GROUP 是关键的,如果没有它,它会同时杀死父进程。 - niCk cAMel
@niCkcAMel 好的。但是我对QT和Windows应用程序开发都没有任何经验。 - georgexsh
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接