等待 bash-builtin 以 100% 的速度消耗 CPU

M89*_*M89 17 shell bash signals shell-script trap

至少在GNU bash 版本 4.3.42 x86_64 && GNU bash 版本 4.3.11 x86_64 上发生

我使用sleep & wait $!而不是简单的sleep来获得sleep信号的可中断性(如SIGUSR1)。但是wait当您运行以下命令时,bash-builtin 的行为似乎很奇怪。

1号航站楼:

cat <(
   trap 'echo SIGUSR1' SIGUSR1;
   echo $BASHPID;
   while :;do
       sleep 1 &
       wait $!;
       echo test;
   done
   )&
Run Code Online (Sandbox Code Playgroud)

2 号航站楼:

kill -10 /the pid of the subshell, printed by the previous command/
Run Code Online (Sandbox Code Playgroud)

1号航站楼:

^C (ctrl + C)
Run Code Online (Sandbox Code Playgroud)

然后,我得到了以 100% 消耗 CPU 的子外壳。

1号航站楼:

pkill -P $(pgrep -P $$)
Run Code Online (Sandbox Code Playgroud)

你知道为什么会发生这种行为吗?

注意cat <(/subshell/)不在后台时不会出现问题。


体验这种行为的另一种方式

1号航站楼:

(
   trap 'echo SIGUSR1' SIGUSR1;
   echo $BASHPID;
   while :;do
       sleep 1 &
       wait $!;
       echo test;
   done
)&
Run Code Online (Sandbox Code Playgroud)

2 号航站楼:

kill -10 /the pid of the subshell, printed by the previous command/
Run Code Online (Sandbox Code Playgroud)

1号航站楼:

fg
^C (ctrl + C)
Run Code Online (Sandbox Code Playgroud)

然后,得到一个冷冻的壳。


体验这种行为的第三种方式

1号航站楼:

(
   trap 'echo SIGUSR1' SIGUSR1;
   echo $BASHPID;
   while :;do
       sleep 1 &
       wait $!;
       echo test;
   done
)
Run Code Online (Sandbox Code Playgroud)

2 号航站楼:

kill -10 /the pid of the subshell, printed by the previous command/
Run Code Online (Sandbox Code Playgroud)

1号航站楼:

^C (ctrl + C)
Run Code Online (Sandbox Code Playgroud)

然后,得到一个冷冻的壳。

Nen*_*eni 1

观察结果

  • ctrl+c发送SIGINT到终端 1 中的 fg-process
  • 因此,在终端 2 中执行与在终端 1 中kill -2 <PID>执行相同ctrl+c
  • 在终端 2 中执行之前执行上述两点之一可以正确处理kill -10 <PID>SIGINT
  • 在终端 2 中执行(发送信号)执行此操作无法正确处理并导致有问题的行为kill -10 <PID>SIGUSR1SIGINT
  • kill -2 <PID>将端子 2 中的 ( SIGINT)替换为kill -15 <PID>( SIGTERM) 或kill -9 <PID>( SIGKILL) 始终会导致正确的信号处理。
  • kill -10 <PID>在终端 2 中执行会中断wait内置函数,但不会离开循环,因为test在信号SIGUSR1被捕获并且循环继续后立即 printet 。
  • 发送会SIGINT中断执行循环并冻结 shell,或者它永远不会中断wait并保持等待/冻结状态。

结论

SIGINT未正确处理或在手动陷印SIGUSR1或任何其他用户定义的陷印后被忽略。这意味着该进程仍然存在,这就是它消耗/加热 CPU 或冻结 shell 的原因。从终端 2执行kill -15 <PID>kill -9 <PID>会终止/终止进程,并让您重新获得对终端 1 的控制权并放松 CPU。

为什么会出现这个问题,仍然是个谜,但我希望有人能够准确解释幕后到底发生了什么。