web-dev-qa-db-ja.com

bash-builtinがCPUを100%で燃やすのを待つ

少なくともGNU bashバージョン4.3.42 x86_64で発生&&GNU bashバージョン4.3.11 x86_64

シグナルによって割り込み可能なsleepを取得するために、単純なsleepではなく_sleep & wait $!_を使用します(SIGUSR1として)。しかし、以下を実行すると、wait bash-builtinが奇妙な動作をするようです。

ターミナル1:

_cat <(
   trap 'echo SIGUSR1' SIGUSR1;
   echo $BASHPID;
   while :;do
       sleep 1 &
       wait $!;
       echo test;
   done
   )&
_

ターミナル2:

_kill -10 /the pid of the subshell, printed by the previous command/
_

ターミナル1:

_^C (ctrl + C)
_

次に、CPUを100%燃焼させるサブシェルを取得します。

ターミナル1:

_pkill -P $(pgrep -P $$)
_

この動作が発生する理由について何か考えがありますか?

[〜#〜] nb [〜#〜]cat <(/subshell/)がバックグラウンドにない場合、問題は発生しません。


この動作を体験する別の方法

ターミナル1:

_(
   trap 'echo SIGUSR1' SIGUSR1;
   echo $BASHPID;
   while :;do
       sleep 1 &
       wait $!;
       echo test;
   done
)&
_

ターミナル2:

_kill -10 /the pid of the subshell, printed by the previous command/
_

ターミナル1:

_fg
^C (ctrl + C)
_

次に、凍結したシェルを入手します。


この動作を体験する3番目の方法

ターミナル1:

_(
   trap 'echo SIGUSR1' SIGUSR1;
   echo $BASHPID;
   while :;do
       sleep 1 &
       wait $!;
       echo test;
   done
)
_

ターミナル2:

_kill -10 /the pid of the subshell, printed by the previous command/
_

ターミナル1:

_^C (ctrl + C)
_

次に、凍結したシェルを入手します。

16
M89

観測

  • ctrl+cは、SIGINTをターミナル1のfgプロセスに送信します
  • したがって、ターミナル2でkill -2 <PID>を実行することは、ターミナル1でctrl+cを押すことと同じです。
  • ターミナル2でkill -10 <PID>を実行する前にbeforeを実行すると、SIGINTが正しく処理されます
  • それを行うターミナル2でkill -10 <PID>を実行すると(信号SIGUSR1を送信)、SIGINTが正しく処理されず、問題が発生します動作
  • ターミナル2(SIGINT)のkill -2 <PID>kill -15 <PID>SIGTERM)またはkill -9 <PID>SIGKILL)に置き換えると、常に正しい信号処理が行われます。
  • ターミナル2でkill -10 <PID>を実行すると、wait組み込みが中断されますが、testは、信号SIGUSR1がトラップされてループが継続した直後に印刷されるため、ループを終了しません。
  • SIGINTを送信すると、実行中のループが抜けてシェルがフリーズするか、waitが中断されずに待機/フリーズされたままになります。

結論

SIGINTが取得されず、正しく処理されないか、手動でSIGUSR1をトラップしたり、他のユーザー定義のトラップを実行した後に無視されます。これは、プロセスがまだ存在していることを意味し、CPUを消費または加熱したり、シェルを凍結したりします。ターミナル2からkill -15 <PID>またはkill -9 <PID>を実行すると、プロセスが終了/終了し、ターミナル1の制御が戻され、CPUが解放されます。

なぜこの問題が発生するのか、それでも謎のままですが、誰かがカーテンの裏側で実際に何が起こっているのかを正確に説明できれば幸いです。

1
Neni