Windows下Python脚本接收外部SIGINT时未触发处理器直接退出问题
问题
我有两个Python 3.11.6脚本:
- 第一个脚本打印自身PID并等待信号,注册了SIGINT信号处理器,预期收到信号时打印提示并不退出;
- 第二个脚本用于向第一个脚本发送SIGINT信号。
实际表现:通过键盘按CTRL+C时第一个脚本可正常触发处理器,但通过第二个脚本发送SIGINT时,第一个脚本无任何输出直接退出。尝试捕获异常,也未触发。
第一个脚本
import os import time import signal def sighandler(signum,frame): print('Received signal {},ignoring'.format(signum)) signal.signal(signal.SIGINT,sighandler) while 1: time.sleep(1) print("sig pid:"+str(os.getpid()))
第二个脚本
import signal import sys import os import time import psutil import multiprocessing pids=[] def getArgs(): i=1 try: while sys.argv[i]: pids.append(int(sys.argv[i])) i=i+1 except: pass def execution(pid): print("SIGINT") os.kill(pid,signal.SIGINT) time.sleep(5) if psutil.pid_exists(pid): print("Process still exist killing bz FTASK") os.system('taskkill /F /T /PID '+str(pid)) return 0 if __name__=='__main__': getArgs() print(pids) time.sleep(5) print("killing") pool = multiprocessing.Pool() outputs = pool.map(execution, pids)
尝试过的捕获异常写法(无效)
if __name__=='__main__': print(os.name) signal.signal(signal.SIGINT,sighandler) try: while 1: time.sleep(1) print("sig pid:"+str(os.getpid())) except Exception as e: print("ahoj") print(e)
解决办法
问题出在Windows系统下的信号机制差异:
- 键盘按CTRL+C是通过控制台发送
CTRL_C_EVENT事件,能被Python的信号处理器正常捕获; - 而
os.kill(pid, signal.SIGINT)在Windows下并非发送真正的控制台中断事件,而是直接终止进程,所以不会触发信号处理器。
修改第二个脚本,改用ctypes调用Windows API发送CTRL_C_EVENT事件到目标进程的控制台进程组:
import sys import time import psutil import multiprocessing import ctypes from ctypes import wintypes # 定义Windows API kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) CTRL_C_EVENT = 0 kernel32.GenerateConsoleCtrlEvent.argtypes = [wintypes.DWORD, wintypes.DWORD] kernel32.GenerateConsoleCtrlEvent.restype = wintypes.BOOL pids = [] def getArgs(): i = 1 try: while sys.argv[i]: pids.append(int(sys.argv[i])) i += 1 except: pass def execution(pid): print("Sending SIGINT (CTRL+C)") try: # 获取目标进程的控制台进程组ID process = psutil.Process(pid) pgid = process.pid # 直接启动的控制台进程,进程组ID等于自身PID # 发送CTRL_C_EVENT到进程组 if not kernel32.GenerateConsoleCtrlEvent(CTRL_C_EVENT, pgid): raise ctypes.WinError(ctypes.get_last_error()) time.sleep(5) if psutil.pid_exists(pid): print("Process still exists, force killing") os.system('taskkill /F /T /PID ' + str(pid)) except Exception as e: print(f"Error: {e}") return 0 if __name__ == '__main__': getArgs() print(pids) time.sleep(5) print("killing") pool = multiprocessing.Pool() outputs = pool.map(execution, pids)
注意:
- 此方法要求目标进程和发送进程在同一个控制台会话中(即都在同一个cmd/PowerShell窗口启动);
- 如果目标进程是双击启动的独立控制台进程,需确保发送进程有权限访问目标控制台,或调整进程组ID的获取逻辑。
内容的提问来源于stack exchange,提问作者Pavol
相关产品推荐
相关产品推荐

