以下是优雅退出子进程的函数
def signal_handler(sig, frame):
print('Interrupt received, shutting down...')
current_process = psutil.Process(os.getpid())
for child in current_process.children(recursive=True):
child.terminate()
gone, still_alive = psutil.wait_procs(current_process.children(), timeout=5)
for p in still_alive:
p.kill()
listener.stop()
sys.exit(0)
函数解读:signal_handler(sig, frame)
这个函数的作用是捕获系统信号(如 SIGINT
或 SIGTERM
),并在收到信号后优雅地关闭当前进程及其所有子进程,确保资源释放和进程的正确退出。以下是对函数的详细分解和解读:
函数参数
-
sig
:- 表示捕获的信号类型,例如:
-
SIGINT
:通常由用户按下Ctrl+C
触发。 -
SIGTERM
:通常由系统发送,用于通知进程应该退出。
-
- 这是信号处理函数的标准参数,用于标识具体的信号。
- 表示捕获的信号类型,例如:
-
frame
:- 表示当前信号触发时的栈帧信息。
- 通常不需要直接使用,但可以用于调试或记录信号触发时的上下文。
函数逻辑分解
1. 打印收到中断信号的提示
print('Interrupt received, shutting down...')
- 当进程收到信号时,向终端输出一条提示信息,表明程序正在关闭。
- 这一步是为了让用户或系统知道进程正在处理退出信号。
2. 获取当前进程及其子进程
current_process = psutil.Process(os.getpid())
- 使用
psutil
库获取当前进程对象。-
os.getpid()
:获取当前进程的 PID(进程 ID)。 -
psutil.Process()
:通过 PID 创建一个psutil
进程对象,用于管理或操作进程。
-
for child in current_process.children(recursive=True):
child.terminate()
-
current_process.children(recursive=True)
:- 获取当前进程的所有直接或间接子进程(递归方式)。
- 如果当前进程启动了多个子进程,这一步会返回所有子进程的列表。
-
child.terminate()
:- 对每个子进程调用
terminate()
方法,向子进程发送一个SIGTERM
信号,通知其应该退出。 - 这是优雅地终止子进程的第一步。
- 对每个子进程调用
3. 等待子进程退出,处理未退出的进程
gone, still_alive = psutil.wait_procs(current_process.children(), timeout=5)
-
psutil.wait_procs()
:- 等待子进程退出。
- 参数:
-
current_process.children()
:获取当前进程的所有直接子进程。 -
timeout=5
:最多等待 5 秒,超时后继续执行。
-
- 返回值:
-
gone
:已成功退出的子进程列表。 -
still_alive
:仍然存活的子进程列表。
-
for p in still_alive:
p.kill()
- 如果某些子进程在 5 秒内未退出(即仍然存活),调用
kill()
方法强制终止它们。-
p.kill()
:- 向子进程发送
SIGKILL
信号,强制终止进程。 - 这一步是确保所有子进程都被终止,不留下僵尸进程。
- 向子进程发送
-
4. 停止监听器
listener.stop()
- 这里的
listener
是一个日志监听器对象,负责集中管理日志记录。 - 调用
listener.stop()
方法停止日志监听器,释放相关资源。 - 注意:
listener
对象必须在函数外部定义,并传递给signal_handler
,否则这里会报错。
5. 退出主进程
sys.exit(0)
-
sys.exit()
:- 退出当前进程。
- 参数
0
表示正常退出(非错误状态)。 - 在所有子进程和资源释放完成后,主进程调用这个方法彻底退出。
函数用途
这个函数的主要用途是处理关闭信号,确保以下几点:
-
优雅地退出子进程:
- 通过
terminate()
和kill()
方法,确保所有子进程都被正确关闭。 - 避免子进程继续运行导致资源泄漏或僵尸进程问题。
- 通过
-
释放资源:
- 停止日志监听器等资源,确保没有未释放的文件句柄或队列。
-
正常退出主进程:
- 在完成所有清理后,主进程以正常状态退出。
可能的改进
-
处理异常情况:
- 如果某些子进程在
terminate()
或kill()
时出现异常,可以捕获并记录日志。
- 如果某些子进程在
-
动态传递
listener
:- 当前代码中
listener
是一个外部对象,未明确传递给signal_handler
。可以通过参数传递listener
,避免未定义错误。
- 当前代码中
总结
这个函数结合了 psutil
和信号处理机制,能够优雅地关闭主进程及其子进程,避免资源泄漏和僵尸进程问题,同时确保系统稳定性。