什么时候处理信号,为什么某些信息会冻结?

时间:2018-11-19 01:20:19

标签: linux bash trap

  1. 打开一个名为“ termA”的终端,然后使用callback.sh运行创建的文件/bin/bash callback.sh

    cat  callback.sh
    #!/bin/bash
    myCallback() {
        echo "callback function called at $(date)"
    }
    trap myCallback SIGUSR1
    sleep 20 
    
  2. 打开一个名为“ 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

  1. 打开一个名为“ termA”的终端,并使用callback.sh运行创建的文件/bin/bash callback.sh;第一次,该信息会立即弹出。

    please input something for foo: 
    
  2. 打开一个名为“ 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:冻结?

3 个答案:

答案 0 :(得分:7)

在解释您的问题之前,请先了解read命令的工作原理。它从stdin读取输入数据,直到遇到EOF。可以肯定地说,从磁盘文件读取时,对read命令的调用是非阻塞的。但是,当stdin连接到终端时,该命令将阻塞,直到用户键入内容为止。

信号处理程序如何工作?

关于信号处理方式的简单说明。请参见C中的以下代码片段,该代码片段仅作用于SIGINT(又称CTRL+C

enter image description here

#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特有的。在kshdash之类的其他shell中,甚至在以POSIX模式(bash运行)的bash --posix中,任何已处理的信号实际上都会中断内置的read 。在上面的示例中,shell将打印you entered ''并在第一个^ C之后退出,如果从循环中调用read,则循环将重新启动。

答案 2 :(得分:1)

termA不会冻结。它只是在输入框中显示回调。只需按Enter键即可继续输入。