为什么Python在从shell脚本开始时忽略SIGINT?

2024-09-30 20:22:03 发布

您现在位置:Python中文网/ 问答频道 /正文

在调试另一个question时,我发现如果Python是从带有&的shell脚本启动的,那么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.pyproblem.sh,SIGINT被忽略:

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

我找不到任何文档来解释为什么会发生这种情况。{a2}没有提到这种行为。这是故意的,还是错误


Tags: inpydefault内容signalshfunctionpython3
1条回答
网友
1楼 · 发布于 2024-09-30 20:22:03

经过进一步的挖掘,我终于找到了原因

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

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

If job control is disabled (see the description of set -m) when the shell executes an asynchronous list, the commands in the list shall inherit from the shell a signal action of ignored (SIG_IGN) for the SIGINT and SIGQUIT signals.

我在shell脚本中找不到一个明确的标准引用,说默认情况下禁用了作业控制,只有quotes说默认情况下为交互式shell启用了作业控制(模糊地暗示非交互式shell的相反设置):

-m

This option shall be supported if the implementation supports the User Portability Utilities option. All jobs shall be run in their own process groups. Immediately before the shell issues a prompt after completion of the background job, a message reporting the exit status of the background job shall be written to standard error. If a foreground job stops, the shell shall write a message to standard error to that effect, formatted as described by the jobs utility. In addition, if a job changes status other than exiting (for example, if it stops for input or output or is stopped by a SIGSTOP signal), the shell shall write a similar message immediately prior to writing the next prompt. This option is enabled by default for interactive shells.

相关问题 更多 >