トラップとSIGINTの奇妙な問題

トラップとSIGINTの奇妙な問題

説明してください:

#!/bin/bash
# This is scripta.sh
./scriptb.sh &
pid=$!
echo $pid started
sleep 3
while true
do
    kill -SIGINT $pid
    echo scripta.sh $$
    sleep 3
done 

-

#!/bin/bash
# This is scriptb.sh
trap "echo Ouch;" SIGINT

while true
do
 echo scriptb.sh $$
 sleep 1
done

トラップを実行すると./scripta.sh印刷されません。 SIGINTから別の信号に切り替えると(SIGTERM、SIGUSR1を試しました)、トラップは期待どおりに「Ouch」を印刷します。どうすればこれが起こりますか?

答え1

SIGINTから別の信号に切り替えると(SIGTERM、SIGUSR1を試しました)、トラップは期待どおりに「Ouch」を印刷します。

明らかにSIGQUITを試していない場合は、SIGINTと同じように動作することがわかります。

問題は業務管理だ。

Unixの初期には、シェルがプロセスまたはパイプをバックグラウンドに入れるたびに、そのプロセスがSIGINTとSIGQUITを無視するように設定したため、ユーザーがCtrl+ C(中断)またはCtrl+(終了)を入力しても\プロセスは終了しませんでした。 。前景の仕事。タスク制御が登場したときにプロセスグループも一緒にインポートされたので、シェルでやるべきことは、バックグラウンドタスクを現在のターミナルプロセスグループでない限り新しいプロセスグループに入れることです。キーボードの入力(Ctrl+ CCtrl+ \Ctrl+ Z(SIGTSTP))。シェルを使用すると、バックグラウンドプロセスが基本的な信号処理を維持できます。実際には、プロセスが前景Ctrlに出てくるときに+終了する必要があるかもしれません。C

ただし、非対話型シェルはジョブ制御を使用しません。歴史的な理由から、非対話型シェルはバックグラウンドプロセスに対してSIGINTとSIGQUITを無視する以前の動作に戻り、キーボードタイプの信号が送信されても​​バックグラウンドプロセスを実行し続けることが合理的です。シェルスクリプトは非対話型シェルで実行されます。

trapそして、命令の下の最後の段落を見ると大きな打撃(1)、あなたは見るでしょう

シェルに入ると無視される信号はキャプチャまたはリセットできません。

./scriptb.sh & だからあなたから逃げたらインタラクティブシェルのコマンドプロンプトでは、対応する信号設定は変更されず(バックグラウンドに配置されていても)コマンドは期待どおりにtrap機能します。ただし./scripta.sh、(使用するかどうかにかかわらず)実行すると、&非対話型シェルでスクリプトが実行されます。非対話型シェルが実行されると、割り込みを無視して終了するようにプロセスを設定します./scriptb.sh &scriptbしたがって、コマンドはtrap自動的にscriptb.sh失敗します。

答え2

いくつかの追跡を通じて:

strace -o aaa ./scripta

私たちは基本的にこれを観察することができます

read(255, "#!/bin/bash\n# this is scripta.sh"..., 153) = 153
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0
lseek(255, -108, SEEK_CUR)              = 45
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f1ee9b7ca10) = 2483

scripta遮断INTと信号にも同様に適用されます。これらの設定はCHLD(ここで呼び出されます)によって継承されます。scriptbそして何が起こっていますか?次のように実行する場合:forkclonescriptbscripta

strace -o bbb ./scriptb &

その後、シグナル関連の項目を検索しながら、以下を発見しました。

rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7fb1b2e75250}, {SIG_IGN, [], 0}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x7fb1b2e75250}, {SIG_DFL, [], SA_RESTORER, 0x7fb1b2e75250}, 8) = 0

これは何もブロックされておらず、INT信号に最初に基本処理が与えられ、次に無視されることを示します。scriptbシェルで直接比較を実行すると、次のようにstraceなります。

rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0
rt_sigaction(SIGINT, {0x45fbf0, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1], [INT CHLD], 8) = 0
...

あるいは、睡眠呼出し処理を入力し、繰り返す前に決して無視されない。わかりました。デフォルトにリセットするには、scriptaとの間にスペースを入れてください...scriptbSIGINT

#include <getopt.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int ch;
    while ((ch = getopt(argc, argv, "h?")) != -1) {
        switch (ch) {
        case 'h':
        case '?':
        default:
            abort();
        }
    }
    argc -= optind;
    argv += optind;
    if (argc < 1) abort();

    signal(SIGINT, SIG_DFL);

    execvp(*argv, argv);
    abort();
    return 1;
}

使用方法:

$ make defaultsig
cc     defaultsig.c   -o defaultsig
$ grep defaultsig scripta.sh
./defaultsig ./scriptb.sh &
$ ./scripta.sh 
12510 started
scriptb.sh 12510
scriptb.sh 12510
scriptb.sh 12510
scripta.sh 12509
Ouch
scriptb.sh 12510
scriptb.sh 12510
scriptb.sh 12510
scripta.sh 12509
Ouch
...

はい、今はうまくいきます。しかし、なぜこれが起こるのかわかりませんbash。バグを報告できますか?のコードはsig.c非常に複雑に見え、他の場所ではより多くの信号処理があります。

答え3

トラップとSIGINTの奇妙な問題

回答いただき、時間をかけてこの問題を調査してくださった皆様に感謝します。

もう一度説明して統合します(下記で明らかになることについてお詫び申し上げます)。

1)SIGQUITも試してみましたが、SIGINTと同じように機能するという質問を追加することを忘れました。

2)これから、私は問題がこれら2つのシグナルの対話型bashの基本構成に関連しているとすでに疑っています。

3)bashと対話するときにプロンプ​​トがあるときは何も終了または中断する必要がないため、基本的な操作は発生しません。シェルを終了するには、単に終了を入力します。

4)SIGQUITとSIGINTがジョブ制御で特別な役割を果たしているとは思わない(SIGTSTP、SIGTTOU、SIGTTINとは反対)。

5)これら2つのシグナルの対話型bashの基本構成は、バックグラウンド(非対話型)シェル(私たちの場合はscriptb.shを実行するシェル)によって継承されていることは私にはわかりません。

6) 実際、フォアグラウンドプロセスグループが SIGQUIT と SIGINT の設定を(実行したシェルから)継承しないように、IMHO はバックグラウンドプロセスグループでも同じことが起こるのが合理的です.

7)また、受け取ったキャラクターが何であれ、トラップはこれを変更しなければなりません。

8) だいたい私はthrigの意見に同意し、ここで見ているのがバグだと思う傾向があります。

関連情報