我正在用 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.
这包括信号处理程序(如果未明确存在)默认情况下将忽略 SIGINT
和 SIGQUIT
。
关于c - 将 SIGINT 发送到运行脚本的 forked exec 进程不会杀死它,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58473685/