考虑这个简短的脚本。它设置了两个信号处理程序;一个用于USR1
,另一个用于USR2
。然后它启动一个交互式 shell 会话。
#!/bin/sh
sigusr1_handler () {
variable=1
printf "SIGUSR1: variable is now %d\n" "$variable"
}
sigusr2_handler () {
variable=2
printf "SIGUSR2: variable is now %d\n" "$variable"
}
variable=0
printf "At beginning, variable is %d\n" "$variable"
trap 'sigusr1_handler' USR1
trap 'sigusr2_handler' USR2
/bin/sh
printf "At end, variable is %d\n" "$variable"
运行它:
$ ./script.sh
At beginning, variable is 0
$ kill -s USR1 $PPID
$ kill -s USR2 $PPID
$ kill -s USR1 $PPID
$ exit
SIGUSR1: variable is now 1
SIGUSR2: variable is now 2
At end, variable is 2
我注意到的第一件事是父 shell 在子 shell 终止之前不会处理信号。我发现这是由 POSIX 记录的,所以我有责任没有正确阅读文档。
我注意到的第二件事是信号没有排队。这可能是非常基础的知识,但我却忽略了它。我假设当子进程正在执行时,生成的信号处于挂起状态,并且如果生成挂起信号再次,它被简单地忽略。
我的问题是我需要在交互式 shell(子进程)运行时将信号传递到父进程的信号处理程序。我需要这个,因为我想根据$variable
子进程终止后的值做出决定,并且如果信号的生成顺序与上面的交互式会话中的顺序相同,$variable
必须脚本末尾的值为 1。
所以我的简单解决方案是将子进程作为异步后台作业启动:
/bin/sh &
wait
(我知道我需要检查信号传递情况下的返回值wait
,但我稍后会进行设置。)
这是行不通的。子进程立即退出:
$ sh -x script.sh
+ variable=0
+ printf At beginning, variable is %d\n 0
At beginning, variable is 0
+ trap sigusr1_handler USR1
+ trap sigusr2_handler USR2
+ wait
+ /bin/sh
+ printf At end, variable is %d\n 0
At end, variable is 0
wait
(在上面的跟踪输出中,看起来好像是在 shell 之前执行的,但我在脚本中按照正确的顺序执行。)
使用/bin/sh -i &
没有任何改变。我做了进一步的尝试,/bin/sh -s
并重定向/dev/stdin
到它,以及这些的组合(-i -s
),但无济于事。
所以现在我的问题是:如何从脚本启动交互式 shell 作为异步前景进程(即作为任何交互式外壳接受输入)以便其父进程可以在信号生成后立即传递信号?或者,是否可以有另一种设计?
答案1
佐藤桂的评论充分回答了我的问题:
信号是否排队取决于系统。无法保证信号按特定顺序传递。相同类型的信号可以按照它们生成的顺序传递,但这也取决于系统(在 Linux 上确实如此)。如果需要发送多种命令,请使用管道作为命令通道。