为什么 Python 在从 shell 脚本中使用 & 启动时会忽略 SIGINT?

问题描述 投票:0回答:1

在调试另一个问题时,我发现如果使用

&
从 shell 脚本启动 Python,SIGINT 的信号处理设置将更改为忽略它。

x.py
内容:

import signal
print(signal.getsignal(signal.SIGINT))

noproblem.sh
内容:

python3 x.py

problem.sh
内容:

python3 x.py &

直接运行

x.py
、直接使用
&
或通过
noproblem.sh
运行时,SIGINT的信号处理程序是默认的
signal.default_int_handler
,它负责引发键盘中断:

07:14 ~ $ python3 x.py
<built-in function default_int_handler>
07:14 ~ $ python3 x.py &
[1] 126909
07:14 ~ $ <built-in function default_int_handler>

[1]+  Done                    python3 x.py
07:14 ~ $ bash noproblem.sh
<built-in function default_int_handler>

但是通过

x.py
运行
problem.sh
,SIGINT 会被忽略:

07:14 ~ $ bash problem.sh
07:14 ~ $ Handlers.SIG_IGN

我找不到任何文档解释为什么会发生这种情况。

signal
模块文档没有提及此行为。这是故意的还是bug?

python signals
1个回答
2
投票

经过进一步挖掘,我成功找到了原因。

首先,如果 Python 从其父进程继承 SIG_IGN 的操作系统级别设置,则它不会安装其默认的 SIGINT 处理程序。它仅在继承 SIG_DFL 设置时安装该处理程序。我们可以在 signal 模块的

源代码
中看到这一点:

IntHandler = PyDict_GetItemString(d, "default_int_handler");
if (!IntHandler)
    goto finally;
Py_INCREF(IntHandler);

_Py_atomic_store_relaxed(&Handlers[0].tripped, 0);
for (i = 1; i < NSIG; i++) {
    void (*t)(int);
    t = PyOS_getsig(i);
    _Py_atomic_store_relaxed(&Handlers[i].tripped, 0);
    if (t == SIG_DFL)
        Handlers[i].func = DefaultHandler;
    else if (t == SIG_IGN)
        Handlers[i].func = IgnoreHandler;
    else
        Handlers[i].func = Py_None; /* None of our business */
    Py_INCREF(Handlers[i].func);
}
if (Handlers[SIGINT].func == DefaultHandler) {
    /* Install default int handler */
    Py_INCREF(IntHandler);
    Py_SETREF(Handlers[SIGINT].func, IntHandler);
    PyOS_setsig(SIGINT, signal_handler);
}

其次,shell 脚本默认在禁用作业控制的情况下运行,并且当禁用作业控制时以

&
启动的进程会继承 SIGINT 和 SIGQUIT 的 SIG_IGN 设置。引用POSIX

如果在 shell 执行异步列表时禁用作业控制(请参阅 set -m 的说明),则列表中的命令将从 shell 继承 SIGINT 和 SIGQUIT 信号的忽略 (SIG_IGN) 信号操作。

我找不到明确的标准引用说在 shell 脚本中默认禁用作业控制,只有 quotes 说交互式 shell 默认启用作业控制(隐含地暗示非交互式 shell 的相反设置):

-米

如果实现支持用户可移植性实用程序选项,则应支持此选项。所有作业都应在其自己的进程组中运行。在后台作业完成后 shell 立即发出提示之前,应将报告后台作业退出状态的消息写入标准错误。如果前台作业停止,shell 应向标准错误写入一条消息以达到该效果,其格式如作业实用程序所描述。此外,如果作业更改状态而不是退出(例如,如果它停止输入或输出或被 SIGSTOP 信号停止),则 shell 应在写入下一个提示之前立即写入类似的消息。 交互式 shell 默认启用此选项。

© www.soinside.com 2019 - 2024. All rights reserved.