Bash 5.1でバックグラウンドジョブを作成してすぐにシグナルを送信すると、シグナルが失われたようです。短いデモ:
$ cat simple.cc
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
static void handler(int, siginfo_t *, void *)
{
write(2, "Got SIGINT\n", 11);
exit(0);
}
int main()
{
struct sigaction act;
act.sa_flags = SA_SIGINFO | SA_RESTART;
act.sa_sigaction = handler;
if (sigaction(SIGINT, &act, nullptr)) { exit(1); }
while (true) {}
}
$ g++ -Wall -Wextra simple.cc -o simple
$ ./simple & kill -SIGINT $!
[1] 540434
$ # nothing happens
$
$ kill -SIGINT 540434
Got SIGINT
私の前提は、信号が到達したときにフォークされたバックグラウンドプロセスがまだBashを実行していることです。 BashはSIGINTをフォアグラウンドプロセスに渡そうとしますが、残念ながらそうではないため、SIGINTは削除されます。
私の質問:
- そうですか?実際にこのようなことが起こっていることをどのように確認できますか?
- バックグラウンドで実行するようにプログラムを変更できないとしましょう。すでに有効になっていて信号を処理するかどうかを確認できますか?簡単なアプローチは
sleep 1
役に立ちますが、正しい同期を探しています。予想されるシグナルハンドラが設定されているかどうかは気にせず、正しいバイナリが実行されていることを確認してください。
ベストアンサー1
注:この回答にはが必要です/proc
。
[...] SIGINTは破棄されます。 [...] そうなんですか?
基本的にそうです。あなたの説明が正しいかどうかはわかりません(「SIGINT転送」?)。メイン(インタラクティブなど)が分岐してbash
ライン実行を超えると、&
PIDを持つプロセスがあり、$!
信号が到達します。問題は、このプロセスが最初にもう一つbash
がexec
来る./simple
。確かに相手がbash
信号を受けた。ただし、シャットダウンの代わりに置き換えられますが、./simple
信号は「使用」されています。
私はプログラマーでもあり、*nixの専門家でもありません。私の説明が正しいかどうかわかりません。完全に正確ではない場合でも、この回答の残りの部分が役に立ちます。
実際にこのようなことが起こっていることをどのように確認できますか?
問題が発生する期間は非常に狭いです。多くの場合kill
、問題の「解決」を遅らせるだけで十分です。理論的には、どんなに大きな遅延でも、特定の試みでは十分に大きくないかもしれません。 「適切な同期」について書くと、この点を理解しているようです。
kill
問題を引き起こすのに十分速いBashの組み込み機能です。外部kill
(例:)を使用できます。/bin/kill
これは時間のかかる別々のプロセスで作成され、テストでは問題は発生しませんでした。
bash
他のもの;に置き換えられる./simple
前にキャッチしようとしています。私は確認しようとしました/proc/$!/exe
。残念ながら、遅すぎたり内蔵ls -l
さreadlink
れていません。
便利な組み込み関数はtest
または同義語です[
。私は次のようにするbash
こと/bin/bash
ができます。
./simple & [ "/proc/$!/exe" -ef /bin/bash ] && echo gotcha
(テスト中にこれを実行することを忘れないでくださいkillall simple
。)これが何をするのかhelp test
については、Bashを参照してください。-ef
bash
次に、切り替える前に実行できるテストの数を見てみましょうsimple
。
./simple & while [ "/proc/$!/exe" -ef /bin/bash ]; do echo a; done
私がテストした結果、エコー文字列の数は約5個でした。最初はパイプで数を計算しようとしましたが、wc -l
結果として遅延が発生しました0
。
バックグラウンドプロセスがまだ実行されていない間、基本コードが実行され続ける期間は./simple
実際には狭いが存在する。
バックグラウンドで実行するようにプログラムを変更できないとしましょう。すでに有効になっていて信号を処理するかどうかを確認できますか? [...] 予想されるシグナルハンドラが設定されているかどうかは気にせず、正しいバイナリが実行されますが、確認してください。
/proc/$!/exe
ループテスト。bash
になったらループを終了しますsimple
。テストは非常に高速である必要はありません。次の点に注意してください。
while [ "/proc/$!/exe" -ef /bin/bash ]; do :; done
ハードコード/bin/bash
。until [ "/proc/$!/exe" -ef ./simple ]; do :; done
simple
十分に速くシャットダウンまたは実行されない場合(たとえば、いくつかのエラーのため)、無限ループが発生します。
私はこれが合理的なアプローチだと思います。
./simple & while [ "/proc/$!/exe" -ef "/proc/$$/exe" ]; do :; done; kill -s INT "$!"
私のテストでは、ハンドラが設定される前にシグナルはsimple
到着せず、後でメインシェルから通知を送信するたびに操作が中断されました。 「予想される信号ハンドラが設定されているかどうかは重要ではなく、正しいバイナリが実行されることだけが重要です」と書いています。はい、それはあなたができることです。