向运行脚本的分叉 exec 进程发送 SIGINT 不会杀死它

Fau*_*kus 1 c bash signals

我正在用 C 编写 shell 应用程序,遇到一个问题,发送 SIGINT 来处理正在运行的脚本不会阻止它。

向普通可执行文件发送相同的信号效果很好。

例子:

Bash 脚本只是模仿长工作脚本(work.sh):

#! /bin/bash

COUNTER=0
while true
do
    ((COUNTER+=1))
    echo "#${COUNTER} Working..."
    sleep 1
done
Run Code Online (Sandbox Code Playgroud)

C代码:

int main() {
    pid_t pid = fork();
    if (pid == 0) {
        char* cmds[] = { "./work.sh", NULL };
        if (execvp(cmds[0], cmds) == -1) {
            exit(1);
        }
    } else {
        sleep(5);
        kill(pid, SIGINT);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

发送信号后,主进程结束,但脚本继续打印。

脚本中的信号处理是否有所不同?您需要如何停止子进程忽略正在运行的进程?

gst*_*gst 5

一个办法:

work.sh在脚本顶部添加此行trap exit SIGINT以获得显式 SIGINT 处理程序:

#! /bin/bash

trap exit SIGINT

COUNTER=0
while true
do
    ((COUNTER+=1))
    echo "#${COUNTER} Working..."
    sleep 1
done
Run Code Online (Sandbox Code Playgroud)

运行work可执行文件现在打印:

#1 Working...
#2 Working...
#3 Working...
#4 Working...
#5 Working...
Run Code Online (Sandbox Code Playgroud)

之后它返回到 shell。

问题:

我发现这个网页链接在 Unix stackexchange 上这个问题的评论中(为了完整起见,这里也链接了接受的答案中的网页。)下面的引用可以解释发生了什么:

bash 是少数几个在处理 SIGINT/SIGQUIT 传递时实现等待和协作退出方法的 shell 之一。解释脚本时,收到 SIGINT 后,它不会立即退出,而是等待当前正在运行的命令返回,并且只有在该命令也被该 SIGINT 杀死时才退出(通过使用 SIGINT 杀死自身)。这个想法是,例如,如果您的脚本调用 vi,并且您在 vi 中按 Ctrl+C 取消操作,则不应将其视为中止脚本的请求。

因此,假设您正在编写一个脚本,并且该脚本在收到 SIGINT 后正常退出。这意味着如果从另一个 bash 脚本调用该脚本,Ctrl-C 将不再中断该其他脚本。

这种问题可以通过设计在 SIGINT 后正常退出的实际命令看到。

编辑:

我发现另一个 Unix stackexchange答案可以更好地解释它。如果您查看bash(1)手册页,以下内容也很容易解释:

bash 运行的非内置命令将信号处理程序设置为 shell 从其父级继承的值。当作业控制无效时,除了这些继承的处理程序之外,异步命令还会忽略 SIGINT 和 SIGQUIT。

特别是考虑到:

进入 shell 时被忽略的信号无法被捕获、重置或列出。

基本上,运行work.sh在单独的执行环境中运行它:

当要执行除内置函数或 shell 函数之外的简单命令时,会在单独的执行环境中调用它。

SIGINT这包括默认情况下将忽略的信号处理程序(如果未明确存在)SIGQUIT