これについて説明してください:
#!/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」と出力します。これはどうやって起こりますか?
いくつかのトレースで:
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
はfork
(ここでは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
...
または、無視されずに、そのあと繰り返されるスリープコール処理に入る。はい。ええと。 scripta
とscriptb
の間にシムを挿入して、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
のコードは非常に複雑に見え、他の場所ではより多くの信号処理があります...
答えと問題の勉強に時間を割いてくれてありがとう。
要約して統合させてください(以下で明らかなことをお詫びします)。
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に同意し、私たちがここで見ているものはバグであると考えます。