c - 将 SIGINT 发送到运行脚本的 forked exec 进程不会杀死它

标签 c bash signals

我正在用 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;
}

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

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

最佳答案

解决方案:

将此行添加到您的 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。

问题:

我找到了这个 webpage在对此 question 的评论中链接在 Unix stackexchange 上(为了完整起见,这里还有接受的答案中的 webpage linked。)这里有一段引述可以解释发生了什么:

bash is among a few shells that implement a wait and cooperative exit approach at handling SIGINT/SIGQUIT delivery. When interpreting a script, upon receiving a SIGINT, it doesn't exit straight away but instead waits for the currently running command to return and only exits (by killing itself with SIGINT) if that command was also killed by that SIGINT. The idea is that if your script calls vi for instance, and you press Ctrl+C within vi to cancel an action, that should not be considered as a request to abort the script.

So imagine you're writing a script and that script exits normally upon receiving SIGINT. That means that if that script is invoked from another bash script, Ctrl-C will no longer interrupt that other script.

This kind of problem can be seen with actual commands that do exit normally upon SIGINT by design.

编辑:

我找到了另一个 Unix stackexchange answer这更好地解释了它。如果您查看 bash(1) 手册页,以下内容也非常具有解释性:

Non-builtin commands run by bash have signal handlers set to the values inherited by the shell from its parent. When job control is not in effect, asynchronous commands ignore SIGINT and SIGQUIT in addition to these inherited handlers.

尤其是考虑到这一点时:

Signals ignored upon entry to the shell cannot be trapped, reset or listed.

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

When a simple command other than a builtin or shell function is to be executed, it is invoked in a separate execution environment.

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

关于c - 将 SIGINT 发送到运行脚本的 forked exec 进程不会杀死它,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58473685/

相关文章:

在 C 中计算 64x64 int 产品的高 64 位

regex - BASH:如何在 sed 命令中对字符串使用 Regex Negative Lookahead?

c++ - Qt/C++ 基于比较的信号调度的优雅方式

c - C中的多线程程序中哪些信号不应该被线程阻塞?

c - iPhone内存操作性能

c - 如何显示 hh :mm:ss:SSS format in C 中耗时

c - 如何在 C 中使用 libtomcrypt API 并对其执行基准测试

c - 使用 "Bash on Ubuntu on Windows"编译的 C 脚本是否比在 Windows 中直接编译和运行慢?

bash - 在 awk printf 中使用可变长度的提示

任何信号上的 C++ 轮询错误