我在使用
signal
模块管理进程行为时遇到了一个奇怪的问题。
我想在
SIGTERM
运行后从 b.py
发送 a.py
信号来终止 a.py
中的主进程和子进程。
现在我发现当
signal.signal(signal.SIGTERM, handle_signal)
放置在 __main__
入口点之前时,子进程不会按预期终止。他们还在奔跑。
但是,如果我将
signal.signal(signal.SIGTERM, handle_signal)
放在子进程启动之后,那么当 a.py
收到来自 SIGTERM
的 b.py
信号时,子进程可以按预期终止。
import multiprocessing
import os
import signal
import time
def process1():
while True:
print("the child process is running")
time.sleep(1)
def handle_signal(signum, frame):
print("signal received:", signum, "pid:", os.getpid())
# register signal
signal.signal(signal.SIGTERM, handle_signal) # place here 1
if "__main__" == __name__:
a = multiprocessing.Process(target=process1)
a.daemon = True
a.start()
# signal.signal(signal.SIGTERM, handle_signal) # place here 2
child_pid = a.pid
parent_pid = os.getpid()
parent_pid_group = os.getpgid(parent_pid)
with open("./crawler.pid", "w") as f:
f.write(str(parent_pid_group))
a.join()
print("Parent id:", parent_pid)
print("Child id", child_pid)
print("all terminated!")
import os
import signal
with open("./crawler.pid", "r") as f:
try:
pid = int(f.read())
os.killpg(pid, signal.SIGTERM)
print("Signal sent successfully to process", pid)
except Exception as e:
print("Error sending signal:", e)
错误的输出 - 信号在子进程启动之前注册:
╰─ python a.py
the child process is running
the child process is running
the child process is running
the child process is running
the child process is running
signal received: 15 pid: 1379
signal received: 15 pid: 1380
the child process is running
the child process is running
the child process is running
the child process is running
^CTraceback (most recent call last):
File "a.py", line 34, in <module>
a.join()
File "/usr/lib/python3.8/multiprocessing/process.py", line 149, in join
res = self._popen.wait(timeout)
File "/usr/lib/python3.8/multiprocessing/popen_fork.py", line 47, in wait
return self.poll(os.WNOHANG if timeout == 0.0 else 0)
File "/usr/lib/python3.8/multiprocessing/popen_fork.py", line 27, in poll
pid, sts = os.waitpid(self.pid, flag)
KeyboardInterrupt
signal received: 15 pid: 1380
Process Process-1:
Traceback (most recent call last):
File "/usr/lib/python3.8/multiprocessing/process.py", line 315, in _bootstrap
self.run()
File "/usr/lib/python3.8/multiprocessing/process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File "a.py", line 10, in process1
time.sleep(1)
KeyboardInterrupt
----------------------
╰─ python b.py
Signal sent successfully to process 1379
正确的输出 - 信号在子进程启动后注册:
╰─ python a.py
the child process is running
the child process is running
the child process is running
signal received: 15 pid: 1961
Parent id: 1961
Child id 1962
all terminated!
---------------------
╰─ python b.py
Signal sent successfully to process 1961
这是python机制还是操作系统设计?
这是python机制还是操作系统设计?
是操作系统设计。例如,类似的 C 程序也会发生同样的情况。
在每种情况下,子进程
multiprocessing.Process
都会在分叉时继承其父进程的信号处理。
在第一种情况下,该配置是您的喋喋不休的信号处理程序。这就是为什么子级和父级都报告接收到信号 15。当传递 SIGTERM 时,每个都运行处理程序,然后恢复其循环(子级)或其阻塞
join()
(父级)。
在第二种情况下,该配置是默认的,SIG_DFL。父进程在子进程被分叉后安装聊天处理程序,子进程被发送到进程组的信号终止,而父进程只是报告它。