Python进程在等待信号量时如何优雅地处理SIGTERM退出?

12 投票
1 回答
9377 浏览
提问于 2025-04-28 00:59

我有一个Python程序,它会启动5个其他的Python程序,使用的是多进程模块。我们把这个主程序叫做P0,其他的叫做P1到P5。现在的要求是,如果我们给P0发送一个SIGTERM信号,它应该先关闭P1到P5,然后自己再退出。

问题是,P1和P5正在等待信号量(semaphore)。所以当我给这些进程发送SIGTERM信号时,它们会调用信号处理程序并退出。但是因为它们在等待信号量,所以会抛出一个异常。有没有办法在退出之前捕获这个异常,这样P0到P5就可以优雅地退出呢?

错误追踪信息:

Traceback (most recent call last):
  File "/usr/lib64/python2.7/multiprocessing/process.py", line 258, in _bootstrap
Traceback (most recent call last):
Process Process-2:
  File "/usr/lib64/python2.7/multiprocessing/process.py", line 258, in _bootstrap
Traceback (most recent call last):
self.run()
File "/usr/lib64/python2.7/multiprocessing/process.py", line 114, in run
self._target(*self._args, **self._kwargs)
Process Process-5:
Traceback (most recent call last):
File "/usr/lib64/python2.7/multiprocessing/process.py", line 258, in _bootstrap
  self.run()
File "/usr/lib64/python2.7/multiprocessing/process.py", line 114, in run
  self._target(*self._args, **self._kwargs)
File "/opt/fireeye/scripts/mip/StaticAnalysisRunner.py", line 45, in run
  qsem.acquire()
暂无标签

1 个回答

17

你可以安装一个信号处理器,当接收到特定信号时,它会抛出一个异常,然后这个异常会在子进程中被捕获,从而优雅地处理退出。

下面是一个示例脚本,它在子进程中等待一个信号量,并在收到一个 SIGTERM 信号时优雅地终止。

#!/usr/bin/env python

import signal
import time
import multiprocessing

class GracefulExit(Exception):
    pass


def signal_handler(signum, frame):
    raise GracefulExit()


def subprocess_function():
    try:
        sem = multiprocessing.Semaphore()
        print "Acquiring semaphore"
        sem.acquire()
        print "Semaphore acquired"

        print "Blocking on semaphore - waiting for SIGTERM"
        sem.acquire()
    except GracefulExit:
        print "Subprocess exiting gracefully"


if __name__ == "__main__":

    # Use signal handler to throw exception which can be caught to allow
    # graceful exit.
    signal.signal(signal.SIGTERM, signal_handler)

    # Start a subprocess and wait for it to terminate.
    p = multiprocessing.Process(target=subprocess_function)
    p.start()

    print "Subprocess pid: %d" % p.pid

    p.join()

这个脚本的一个示例运行结果如下:

$ ./test.py 
Subprocess pid: 7546
Acquiring semaphore
Semaphore acquired
Blocking on semaphore - waiting for SIGTERM
----> Use another shell to kill -TERM 7546
Subprocess exiting gracefully

从子进程中没有错误追踪信息,流程显示子进程以优雅的方式退出。这是因为 SIGTERM 信号被子进程的信号处理器捕获,处理器抛出了一个普通的 Python 异常,这个异常可以在进程内部被处理。

撰写回答