You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 18:45:06