首页 > 解决方案 > 将 SIGINT 发送到运行脚本的分叉 exec 进程不会杀死它

问题描述

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

将相同的信号发送到正常的可执行文件就可以了。

例子:

仅模仿长工作脚本 (work.sh) 的 Bash 脚本:

#! /bin/bash

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

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;
}

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

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

标签: cbashsignals

解决方案


一个解法:

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

#! /bin/bash

trap exit SIGINT

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

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

#1 Working...
#2 Working...
#3 Working...
#4 Working...
#5 Working...

之后它返回到 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 时忽略的信号不能被捕获、重置或列出。

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

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

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


推荐阅读