Bashバックグラウンドプロセス信号が失われました。

Bashバックグラウンドプロセス信号が失われました。

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は削除されます。

私の質問:

  1. そうですか?実際にこのようなことが起こっていることをどのように確認できますか?
  2. バックグラウンドで実行するようにプログラムを変更できないとしましょう。すでに有効になっていて信号を処理するかどうかを確認できますか?簡単なアプローチはsleep 1役に立ちますが、正しい同期を探しています。予想されるシグナルハンドラが設定されているかどうかは気にせず、正しいバイナリが実行されていることを確認してください。

ベストアンサー1

注:この回答にはが必要です/proc


[...] SIGINTは破棄されます。 [...] そうなんですか?

基本的にそうです。あなたの説明が正しいかどうかはわかりません(「SIGINT転送」?)。メイン(インタラクティブなど)が分岐してbashライン実行を超えると、&PIDを持つプロセスがあり、$!信号が到達します。問題は、このプロセスが最初にもう一つbashexec来る./simple確かに相手がbash信号を受けた。ただし、シャットダウンの代わりに置き換えられますが、./simple信号は「使用」されています。

私はプログラマーでもあり、*nixの専門家でもありません。私の説明が正しいかどうかわかりません。完全に正確ではない場合でも、この回答の残りの部分が役に立ちます。


実際にこのようなことが起こっていることをどのように確認できますか?

問題が発生する期間は非常に狭いです。多くの場合kill、問題の「解決」を遅らせるだけで十分です。理論的には、どんなに大きな遅延でも、特定の試みでは十分に大きくないかもしれません。 「適切な同期」について書くと、この点を理解しているようです。

kill問題を引き起こすのに十分速いBashの組み込み機能です。外部kill(例:)を使用できます。/bin/killこれは時間のかかる別々のプロセスで作成され、テストでは問題は発生しませんでした。

bash他のもの;に置き換えられる./simple前にキャッチしようとしています。私は確認しようとしました/proc/$!/exe。残念ながら、遅すぎたり内蔵ls -lreadlinkれていません。

便利な組み込み関数は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 :; donesimple十分に速くシャットダウンまたは実行されない場合(たとえば、いくつかのエラーのため)、無限ループが発生します。

私はこれが合理的なアプローチだと思います。

./simple & while [ "/proc/$!/exe" -ef "/proc/$$/exe" ]; do :; done; kill -s INT "$!"

私のテストでは、ハンドラが設定される前にシグナルはsimple到着せず、後でメインシェルから通知を送信するたびに操作が中断されました。 「予想される信号ハンドラが設定されているかどうかは重要ではなく、正しいバイナリが実行されることだけが重要です」と書いています。はい、それはあなたができることです。

おすすめ記事