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

send_signal(signal.SIGINT)不生效?Ubuntu下Python子进程信号问题咨询

Why SIGINT Sent via subprocess.Popen Isn't Triggering KeyboardInterrupt in Child Process

Hey Thomas, great question—this is a super common gotcha with subprocesses and signal handling on Unix-like systems like Ubuntu. Let’s break down why this is happening and how to fix it.

The Root Cause

When you use shell=True in subprocess.Popen, the command you’re running gets executed inside a shell process (usually /bin/sh), not directly as the Python interpreter running script.py. Here’s the play-by-play:

  • Your test.py sends SIGINT to the shell process, not the actual python script.py child process.
  • By default, shell processes ignore SIGINT unless they’re running in the foreground as the session leader (which isn’t the case here—the shell is a background child of test.py).
  • The shell just waits for its own child (the Python process running script.py) to finish, so script.py keeps executing until it completes naturally—hence you see i am done instead of the KeyboardInterrupt message.

When you manually press Ctrl+C, the terminal sends SIGINT to the entire foreground process group, which includes test.py, the shell (if used), and script.py. That’s why the KeyboardInterrupt triggers in script.py then.

The simplest fix is to run the Python process directly without a shell. Pass the command as a list of arguments instead of a single string:

Modify test.py like this:

import subprocess
import signal
from threading import Thread
import time
import os

# Pass command as a list (no shell=True)
p = subprocess.Popen(['python', 'script.py'])
t = Thread(target=p.wait)
t.start()
print('sleeping')
time.sleep(2)
print('interrupt')
p.send_signal(signal.SIGINT)
t.join()
print('process finished')

Now p refers directly to the Python process running script.py, so sending SIGINT to it will trigger the KeyboardInterrupt exception exactly as you expect.

Fix 2: Send SIGINT to the Entire Process Group (If You Need shell=True)

If you must use shell=True (e.g., for complex shell syntax like pipes or globbing), you need to send SIGINT to the entire process group that the shell created. The shell becomes the leader of a new process group, and script.py is part of that group.

Update test.py to send the signal to the whole process group:

import subprocess
import signal
from threading import Thread
import time
import os

p = subprocess.Popen('python script.py', shell=True)
t = Thread(target=p.wait)
t.start()
print('sleeping')
time.sleep(2)
print('interrupt')

# Get the process group ID of the shell and send SIGINT to all processes in the group
os.killpg(os.getpgid(p.pid), signal.SIGINT)
t.join()
print('process finished')

This will send SIGINT to both the shell and the script.py process, triggering the KeyboardInterrupt in script.py.

Quick Note on SIGTERM

SIGTERM is a generic termination signal, and Python doesn’t map it to KeyboardInterrupt by default—KeyboardInterrupt is specifically tied to SIGINT (the signal sent by Ctrl+C). If you wanted SIGTERM to trigger a custom handler, you’d need to register one in script.py, but that’s a separate issue from your original problem.

Give these fixes a try, and you should see your expected output!

内容的提问来源于stack exchange,提问作者Thomy800

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:32:29