什么时候处理信号以及为什么某些信息会冻结?
When is a signal handled and why does some info freeze up?
打开一个名为 "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" 的终端并 运行 使用 /bin/bash callback.sh
创建的文件 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
包含一个无限循环。这如何解释以下内容?
it does not handle any signals received until the foreground process terminates
在这种情况下,前台进程永远不会终止。
B期继续,运行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
仍然没有 please input something for foo:
显示在 termA 中。
为什么信息 please input something for foo:
冻结了?
termA 不会冻结。它只是在输入框中显示回调。只需按 Enter
键即可继续输入。
在解释您的问题之前,先了解一下 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!"
打印,但程序returns进入无限循环。只有在用 Ctrl-\
发出 SIGQUIT
信号后,程序才真正退出。根据您的 ulimit
设置,它会转储核心或打印出收到的信号编号。
虽然循环会退出的解释是合理的,但它没有考虑信号处理的主要原因,即异步事件处理。这意味着信号处理程序的行为超出了程序控制的标准流程;事实上,整个程序都保存在一个上下文中,并且会创建一个新的上下文来仅供信号处理程序在其中执行。一旦信号处理程序完成其操作,上下文就会切换回来并开始正常的执行流程(即while(1)
).
回答您的问题,
The conclusion verified that When bash is executing an external command in the foreground, it does not handle any signals received until the foreground process terminates
这里要注意的关键是external命令部分。在第一种情况下,sleep
是一个外部进程,但在第二种情况下,read
是 shell 本身的内置进程。因此,在这两种情况下,信号向这两者的传播都不同
type read
read is a shell builtin
type sleep
sleep is /usr/bin/sleep
1.Open a terminal,named termA ,and run the created file callback.sh with /bin/bash callback.sh for first time,the info pop up instantly.
是的,这种行为是预期的。因为此时只定义了函数并将陷阱处理程序注册到函数myCallback
,脚本中还没有接收到信号。随着执行顺序的进行,来自 read
提示的消息第一次被抛出。
2.Open a new terminal ,named termB and run pkill -USR1 -f callback.sh
first time,the info pop up instantly in termA
是的,当 read
命令等待字符串后跟 Enter 键时,它会收到一个信号 EOF
38=] 从另一个终端,当前执行上下文被保存并且控制切换到信号处理程序,该信号处理程序打印带有当前日期的字符串。
一旦处理程序完成执行,上下文就会恢复到 while
循环,其中 read
命令仍在等待输入字符串。在 read
命令成功之前,所有后续信号陷阱只会在信号处理程序中打印字符串。
Go on in termB,run pkill -USR1 -f callback.sh
the second time.
与前面解释的一样,read
命令在你的while循环中没有完成一次,只有当它成功读取一个字符串时,循环的下一次迭代才会开始,并且会出现新的提示信息被抛出。
图片来源:Michael KerrisK 的 Linux 编程接口
The conclusion verified that When bash is executing an external command in the foreground, it does not handle any signals received until the foreground process terminates.
bash
在 C
/ 实现级别处理信号,但不会 运行 使用 [=13= 设置的处理程序] 直到前台进程终止。这是 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
之类的 "utility",它不是在单独的进程中执行的,信号是在其 "context" 中还是在主 shell 中出现并不明显],以及使用 trap
设置的处理程序是否应该在 read
return 之前或之后 运行。
issue 1:callback.sh contains a infinite while loop,how to explain it does not handle any signals received until the foreground process terminates.,in this case the foreground process never terminates.
错了。 bash
没有在单独的进程中执行 while
循环。由于没有前台进程 bash
正在等待,它可以 运行 立即使用 trap
设置的任何处理程序。如果 read
是一个外部命令,那么 while
将是前台进程。
issue2: No please input something for foo: shown
in termA;
go to termB, run pkill -USR1 -f callback.sh
the third time.
The following info shown in termA again: callback function called at Mon Nov 19 09:07:24 HKT 2018
Still no please input something for foo:
shown in termA.
Why the info please input something for foo:
freeze up?
它不会冻结。只需按 Enter 它就会再次出现。
就是这样
a) bash
将在被信号中断时重新启动 read
内置 就地 -- 它不会 return 和再次通过 while
循环
b) 在这种情况下,它不会重新显示用 -p
设置的提示。
请注意,在处理了来自键盘的 SIGINT
的情况下,bash
甚至不会再次显示提示,即使它确实丢弃了到目前为止从用户那里读取的字符串:
$ 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'
如果信号是从另一个window用kill -INT <pid>
而不是用Ctrl-C从终端发送的,那将打印you entered 'foobar'
.
最后一部分的所有内容(read
如何被打断等)都非常 bash
具体。在其他 shell 中,如 ksh
或 dash
,甚至在 bash
中 运行 处于 POSIX 模式(bash --posix
)时,任何处理的信号实际上都会中断 read
内置函数。在上面的示例中,shell 将打印 you entered ''
并在第一个 ^C 后退出,如果从循环中调用 read
,循环将重新启动。
打开一个名为 "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" 的终端并 运行 使用
/bin/bash callback.sh
创建的文件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
包含一个无限循环。这如何解释以下内容?
it does not handle any signals received until the foreground process terminates
在这种情况下,前台进程永远不会终止。
B期继续,运行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
仍然没有 please input something for foo:
显示在 termA 中。
为什么信息 please input something for foo:
冻结了?
termA 不会冻结。它只是在输入框中显示回调。只需按 Enter
键即可继续输入。
在解释您的问题之前,先了解一下 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!"
打印,但程序returns进入无限循环。只有在用 Ctrl-\
发出 SIGQUIT
信号后,程序才真正退出。根据您的 ulimit
设置,它会转储核心或打印出收到的信号编号。
虽然循环会退出的解释是合理的,但它没有考虑信号处理的主要原因,即异步事件处理。这意味着信号处理程序的行为超出了程序控制的标准流程;事实上,整个程序都保存在一个上下文中,并且会创建一个新的上下文来仅供信号处理程序在其中执行。一旦信号处理程序完成其操作,上下文就会切换回来并开始正常的执行流程(即while(1)
).
回答您的问题,
The conclusion verified that When bash is executing an external command in the foreground, it does not handle any signals received until the foreground process terminates
这里要注意的关键是external命令部分。在第一种情况下,sleep
是一个外部进程,但在第二种情况下,read
是 shell 本身的内置进程。因此,在这两种情况下,信号向这两者的传播都不同
type read
read is a shell builtin
type sleep
sleep is /usr/bin/sleep
1.Open a terminal,named termA ,and run the created file callback.sh with /bin/bash callback.sh for first time,the info pop up instantly.
是的,这种行为是预期的。因为此时只定义了函数并将陷阱处理程序注册到函数myCallback
,脚本中还没有接收到信号。随着执行顺序的进行,来自 read
提示的消息第一次被抛出。
2.Open a new terminal ,named termB and run
pkill -USR1 -f callback.sh
first time,the info pop up instantly in termA
是的,当 read
命令等待字符串后跟 Enter 键时,它会收到一个信号 EOF
38=] 从另一个终端,当前执行上下文被保存并且控制切换到信号处理程序,该信号处理程序打印带有当前日期的字符串。
一旦处理程序完成执行,上下文就会恢复到 while
循环,其中 read
命令仍在等待输入字符串。在 read
命令成功之前,所有后续信号陷阱只会在信号处理程序中打印字符串。
Go on in termB,run
pkill -USR1 -f callback.sh
the second time.
与前面解释的一样,read
命令在你的while循环中没有完成一次,只有当它成功读取一个字符串时,循环的下一次迭代才会开始,并且会出现新的提示信息被抛出。
图片来源:Michael KerrisK 的 Linux 编程接口
The conclusion verified that When bash is executing an external command in the foreground, it does not handle any signals received until the foreground process terminates.
bash
在 C
/ 实现级别处理信号,但不会 运行 使用 [=13= 设置的处理程序] 直到前台进程终止。这是 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
之类的 "utility",它不是在单独的进程中执行的,信号是在其 "context" 中还是在主 shell 中出现并不明显],以及使用 trap
设置的处理程序是否应该在 read
return 之前或之后 运行。
issue 1:callback.sh contains a infinite while loop,how to explain it does not handle any signals received until the foreground process terminates.,in this case the foreground process never terminates.
错了。 bash
没有在单独的进程中执行 while
循环。由于没有前台进程 bash
正在等待,它可以 运行 立即使用 trap
设置的任何处理程序。如果 read
是一个外部命令,那么 while
将是前台进程。
issue2: No
please input something for foo: shown
in termA;go to termB, run
pkill -USR1 -f callback.sh
the third time.The following info shown in termA again:
callback function called at Mon Nov 19 09:07:24 HKT 2018
Still no
please input something for foo:
shown in termA. Why the infoplease input something for foo:
freeze up?
它不会冻结。只需按 Enter 它就会再次出现。
就是这样
a) bash
将在被信号中断时重新启动 read
内置 就地 -- 它不会 return 和再次通过 while
循环
b) 在这种情况下,它不会重新显示用 -p
设置的提示。
请注意,在处理了来自键盘的 SIGINT
的情况下,bash
甚至不会再次显示提示,即使它确实丢弃了到目前为止从用户那里读取的字符串:
$ 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'
如果信号是从另一个window用kill -INT <pid>
而不是用Ctrl-C从终端发送的,那将打印you entered 'foobar'
.
最后一部分的所有内容(read
如何被打断等)都非常 bash
具体。在其他 shell 中,如 ksh
或 dash
,甚至在 bash
中 运行 处于 POSIX 模式(bash --posix
)时,任何处理的信号实际上都会中断 read
内置函数。在上面的示例中,shell 将打印 you entered ''
并在第一个 ^C 后退出,如果从循环中调用 read
,循环将重新启动。