web-dev-qa-db-ja.com

トラップと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」と出力します。これはどうやって起こりますか?

5
diciotto

いくつかのトレースで:

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

そのため、scriptaINTおよびCHLD信号をブロックしました。 scriptbfork(ここではclone)を介してこれらの設定を継承します。そして、scriptbは何をしていますか? scriptaから実行する場合:

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
...

または、無視されずに、そのあと繰り返されるスリープコール処理に入る。はい。ええと。 scriptascriptbの間にシムを挿入して、SIGINTをデフォルトの状態にリセットします...

#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
thrig

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

答えと問題の勉強に時間を割いてくれてありがとう。

要約して統合させてください(以下で明らかなことをお詫びします)。

1)質問に追加するのを忘れたので、SIGQUITも試しましたが、SIGINTとして動作しました。

2)それから、私はすでに問題がこれらの2つの信号のインタラクティブbashのデフォルトの処理に関連していると疑っていました。

3)bashと対話するときにデフォルトのアクションが発生しないのは、プロンプトが唯一の場合に、終了したり中断したりする意味がないためです。シェルを終了する場合は、exitと入力します。

4)SIGQUITとSIGINTがジョブ制御で特別な役割を果たすことはありません(SIGTSTP、SIGTTOU、SIGTTINとは異なります)。

5)これら2つの信号のインタラクティブbashのデフォルトの配置が、バックグラウンド(非インタラクティブ)シェル(この場合、scriptb.shを実行するシェル)に継承されることは、私には意味がありません。

6)実際、フォアグラウンドプロセスグループがSIGQUITとSIGINTの後処理を(それを開始したシェルから)継承しないのと同様に、IMHOは、バックグラウンドプロセスグループで同じことが発生することに意味があるはずです。

7)さらに、継承された性質が何であれ、トラップはそれを変更する必要があります。

8)全体として、私はthrigに同意し、私たちがここで見ているものはバグであると考えます。

0
diciotto