何时处理信号以及为什么某些信息会冻结?

scr*_*apy 14 linux bash bash-trap

  1. 打开一个名为"伏藏"终端,并运行创建的文件callback.sh/bin/bash callback.sh.

    cat  callback.sh
    #!/bin/bash
    myCallback() {
        echo "callback function called at $(date)"
    }
    trap myCallback SIGUSR1
    sleep 20 
    
    Run Code Online (Sandbox Code Playgroud)
  2. 打开一个名为"termB"的新终端并运行:

    pkill -USR1  -f  callback.sh
    
    Run Code Online (Sandbox Code Playgroud)

在termA中20秒后显示以下内容; 它永远不会在termA中显示:

callback function called at Mon Nov 19 08:21:52 HKT 2018
Run Code Online (Sandbox Code Playgroud)

结论证实,当Bash在前台执行外部命令时,它不处理任何接收的信号,直到前台进程终止(参见信号陷阱).

做一点改变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
Run Code Online (Sandbox Code Playgroud)

添加无限while循环并删除sleep 20.

  1. 打开一个名为"特玛"终端,运行创建的文件callback.sh/bin/bash callback.sh; 第一次,信息立即弹出.

    please input something for foo: 
    
    Run Code Online (Sandbox Code Playgroud)
  2. 打开一个名为"termB"的新终端并运行pkill -USR1 -f callback.sh; 第一次,信息在termA中立即弹出.

    callback function called at Mon Nov 19 09:07:14 HKT 2018
    
    Run Code Online (Sandbox Code Playgroud)

问题1: callback.sh包含无限循环.这如何解释以下内容?

在前台进程终止之前,它不处理任何接收到的信号

在这种情况下,前台进程永远不会终止.

继续在termB中,pkill -USR1 -f callback.sh第二次运行.

callback function called at Mon Nov 19 09:07:14 HKT 2018
Run Code Online (Sandbox Code Playgroud)

上面的信息再次在termA中立即弹出.

问题2:没有please input something for foo:在termA中显示,转到termB,pkill -USR1 -f callback.sh第三次运行,以下信息再次显示在termA中.

callback function called at Mon Nov 19 09:07:24 HKT 2018
Run Code Online (Sandbox Code Playgroud)

仍然没有please input something for foo:在termA中显示.
为什么信息会please input something for foo:冻结?

Ini*_*ian 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);
}
Run Code Online (Sandbox Code Playgroud)

它将注册信号处理程序,然后进入无限循环.当我们点击时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!
Run Code Online (Sandbox Code Playgroud)

正如输出所示,每次我们发出Ctrl-C,"Hello World!"打印,但程序返回无限循环.只有在发出SIGQUIT信号Ctrl-\后,程序才真正退出.根据您的ulimit设置,它会转储核心或打印出收到的信号.

虽然循环将退出的解释是合理的,但它没有考虑信号处理的主要原因,即异步事件处理.这意味着信号处理程序的行为超出了程序控制的标准流程; 事实上,整个程序保存在一个上下文中,并且只为信号处理程序创建一个新的上下文来执行.一旦信号处理程序完成其动作,就会切换回上下文并开始正常的执行流程(即while(1)).

要回答你的问题,

结论证实,当bash在前台执行外部命令时,它不会处理在前台进程终止之前收到的任何信号

这里要注意的关键是外部命令部分.在第一种情况下,sleep外部进程在哪里,但在第二种情况下,read是shell本身的内置进程.因此,在这两种情况下,信号传播到这两者都不同

type read
read is a shell builtin
type sleep
sleep is /usr/bin/sleep
Run Code Online (Sandbox Code Playgroud)

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

是的,这种行为是预期的.因为此时只定义了函数,并且陷阱处理程序已注册到函数中,myCallback并且脚本中尚未收到信号.随着执行顺序的进行,read第一次抛出来自提示的消息.

2.打开一个名为termB并在pkill -USR1 -f callback.sh第一次运行的新终端,该信息会立即在termA中弹出

是的,当read命令正在等待字符串后跟Enter信号指示的键EOF,它接收SIGUSR1来自另一个终端的信号,保存当前执行上下文并且控制切换信号处理程序,该信号处理程序打印具有当前日期的字符串.

处理程序完成执行后,上下文将恢复到命令仍在等待输入字符串的while循环read.在read命令成功之前,所有后续信号陷阱都只会在信号处理程序内打印字符串.

在termB中继续,pkill -USR1 -f callback.sh第二次运行.

与前面解释的相同,read命令在while循环中没有完成一次,只有在成功读取字符串时,循环的下一次迭代才会启动,并且会抛出新的提示消息.


图像源:Michael KerrisK的Linux编程接口