打开一个名为“ termA”的终端,然后使用callback.sh
运行创建的文件/bin/bash callback.sh
。
cat callback.sh
#!/bin/bash
myCallback() {
echo "callback function called at $(date)"
}
trap myCallback SIGUSR1
sleep 20
打开一个名为“ termB”的新终端,然后运行:
pkill -USR1 -f callback.sh
termA 20秒后显示以下内容;永远不会立即显示在termA中:
callback function called at Mon Nov 19 08:21:52 HKT 2018
该结论证实,当Bash在前台执行外部命令时,它不会处理任何接收到的信号,直到前台进程终止(请参阅signal trap)。
对callback.sh
进行一些更改:
cat callback.sh
#!/bin/bash
myCallback() {
echo "callback function called at $(date)"
}
trap myCallback SIGUSR1
while true; do
read -p "please input something for foo: " foo
done
添加一个无限的while循环并删除sleep 20
。
打开一个名为“ termA”的终端,并使用callback.sh
运行创建的文件/bin/bash callback.sh
;第一次,该信息会立即弹出。
please input something for foo:
打开一个名为“ termB”的新终端,然后运行pkill -USR1 -f callback.sh
;第一次,该信息会在termA中立即弹出。
callback function called at Mon Nov 19 09:07:14 HKT 2018
问题1: callback.sh
包含一个无限的while循环。如何解释以下内容?
在前台进程终止之前,它不会处理收到的任何信号
在这种情况下,前台进程永远不会终止。
在termB中继续,第二次运行pkill -USR1 -f callback.sh
。
callback function called at Mon Nov 19 09:07:14 HKT 2018
上面的信息再次在termA中突然弹出。
问题2: termA中未显示please input something for foo:
,
转到termB,第三次运行pkill -USR1 -f callback.sh
,
以下信息再次显示在termA中。
callback function called at Mon Nov 19 09:07:24 HKT 2018
在termA中仍然没有显示please input something for foo:
。
为什么信息please input something for foo:
冻结?
答案 0 :(得分:7)
在解释您的问题之前,请先了解read
命令的工作原理。它从stdin
读取输入数据,直到遇到EOF
。可以肯定地说,从磁盘文件读取时,对read
命令的调用是非阻塞的。但是,当stdin
连接到终端时,该命令将阻塞,直到用户键入内容为止。
关于信号处理方式的简单说明。请参见C
中的以下代码片段,该代码片段仅作用于SIGINT
(又称CTRL+C
)
#include <stdio.h>
#include <signal.h>
/* signal handler definition */
void signal_handler(int signum){
printf("Hello World!\n");
}
int main(){
//Handle SIGINT with a signal handler
signal(SIGINT, signal_handler);
//loop forever!
while(1);
}
它将注册信号处理程序,然后进入无限循环。当我们点击Ctrl-C
时,我们都同意信号处理程序signal_handler()
应该执行并且"Hello World!"
打印到屏幕上,但是程序处于无限循环中。为了打印"Hello World!"
,一定是因为它中断了循环以执行信号处理程序,对吗?因此它应该退出循环以及程序。让我们看看:
gcc -Wall -o sighdl.o signal.c
./sighdl.o
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
如输出所示,每次我们发布Ctrl-C
时,"Hello World!"
都会打印,但是程序返回到无限循环。只有在发出带有SIGQUIT
的{{1}}信号之后,程序才真正退出。根据您的Ctrl-\
设置,它会转储内核或打印出接收到的信号编号。
虽然解释循环将退出是合理的,但它没有考虑信号处理的主要原因,即异步事件处理。这意味着信号处理程序的行为超出了程序控制的标准流程;实际上,整个程序都保存在一个上下文中,并且仅在要执行信号处理程序的情况下创建了一个新的上下文。一旦信号处理程序完成其动作,上下文将被切换回并且正常的执行流程开始(即ulimit
。
要回答您的问题,
结论证实,当bash在前台执行外部命令时,直到前台进程终止,它才处理收到的任何信号
这里要注意的关键是外部命令部分。在第一种情况下,while(1)
是外部进程,而在第二种情况下,sleep
是外壳程序本身的内置程序。因此,在这两种情况下,信号向这两者的传播都不同
read
1。打开一个名为termA的终端,并第一次使用/ bin / bash callback.sh运行创建的文件callback.sh,该信息立即弹出。
是的,此行为是预期的。因为此时仅定义了函数,并且陷阱处理程序已注册到函数type read
read is a shell builtin
type sleep
sleep is /usr/bin/sleep
,并且脚本中尚未接收到信号。随着执行顺序的进行,myCallback
提示符下的消息将被第一次抛出。
2。打开一个名为termB的新终端并第一次运行
read
,该信息会在termA中立即弹出
是的,当pkill -USR1 -f callback.sh
命令正在等待字符串后,接着是 Enter 键,它向read
发信号,它从接收到的信号EOF
在另一个终端上,将保存当前的执行上下文,并切换控件,以显示当前日期的字符串的信号处理程序。
处理程序完成执行后,上下文将恢复到SIGUSR1
循环,在该循环中while
命令仍在等待输入字符串。在read
命令成功执行之前,所有后续信号陷阱将只在信号处理程序中打印字符串。
继续执行termB,第二次运行
read
。
与之前所述相同,pkill -USR1 -f callback.sh
命令在while循环中不会一次完成,只有成功读取字符串后,循环的下一次迭代才会开始,并会抛出新的提示消息
图片来源:Michael KerrisK的Linux编程接口
答案 1 :(得分:4)
结论证实,当bash在前台执行外部命令时,直到前台进程终止,它才处理收到的任何信号。
bash
可以在C
/实现级别上处理信号,但是在前台进程终止之前,不会运行由trap
设置的处理程序。 standard要求这样做:
When a signal for which a trap has been set is received while the shell is waiting for the completion of a utility executing a foreground command, the trap associated with that signal shall not be executed until after the foreground command has completed.
请注意,该标准在内置命令和外部命令之间没有区别;对于read
这样的“实用程序”,它不是在单独的进程中执行的,则不清楚信号是在其“上下文”中还是在主外壳的信号中发生,以及是否设置了处理程序trap
应该在read
返回之前或之后运行。
问题1:callback.sh包含一个无限的while循环,如何解释它直到前台进程终止才处理收到的任何信号。在这种情况下,前台进程永远不会终止。
错了。 bash
没有在单独的进程中执行while
循环。由于没有正在等待的前台进程bash
,因此它可以立即运行使用trap
设置的任何处理程序。如果read
是外部命令,则不是while
就是前台进程。
issue2:termA中没有
please input something for foo: shown
;转到termB,第三次运行
pkill -USR1 -f callback.sh
。termA中再次显示以下信息:
callback function called at Mon Nov 19 09:07:24 HKT 2018
在termA中仍然没有显示
please input something for foo:
。 为什么信息please input something for foo:
冻结?
它不会冻结。只需按 Enter ,它将再次显示。
就是这样
a)bash
将在信号中断时重新启动内置的read
-它不会返回并再次通过while
循环
b)在这种情况下,它不会重新显示用-p
设置的提示。
请注意,bash
在处理了键盘上的SIGINT
的情况下甚至不会再次显示提示,即使它确实丢弃了远离用户的字符串:>
$ cat goo
trap 'echo INT' INT
echo $$
read -p 'enter something: ' var
echo "you entered '$var'"
$ bash goo
24035
enter something: foo<Ctrl-C>^CINT
<Ctrl-C>^CINT
<Ctrl-C>^CINT
bar<Enter>
you entered 'bar'
如果信号是从另一个窗口通过you entered 'foobar'
而不是从终端通过 Ctrl-C 发送的,则将打印kill -INT <pid>
。
最后一部分的所有内容(如何中断read
等)都是bash
特有的。在ksh
或dash
之类的其他shell中,甚至在以POSIX模式(bash
运行)的bash --posix
中,任何已处理的信号实际上都会中断内置的read
。在上面的示例中,shell将打印you entered ''
并在第一个^ C之后退出,如果从循环中调用read
,则循环将重新启动。
答案 2 :(得分:1)
termA不会冻结。它只是在输入框中显示回调。只需按Enter
键即可继续输入。