我正在用 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)
发送信号后,主进程结束,但脚本继续打印。
脚本中的信号处理是否有所不同?您需要如何停止子进程忽略正在运行的进程?
一个办法:
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
。