
私のスクリプトには次のものがあります。
yes >/dev/null &
pid=$!
echo $pid
sleep 2
kill -INT $pid
sleep 2
ps aux | grep yes
yes
これを実行すると、スクリプトが終了してもまだ実行中であると出力されます。ただし、コマンドを対話的に実行すると、プロセスは次のように正常に終了します。
> yes >/dev/null &
[1] 9967
> kill -INT 9967
> ps aux | grep yes
sean ... 0:00 grep yes
SIGINTが対話型インスタンスではプロセスを終了しますが、スクリプトインスタンスではプロセスを終了しないのはなぜですか?
編集する
以下は、問題の診断に役立ついくつかの追加情報です。上記のスクリプトをシミュレートするために、次のGoプログラムを作成しました。
package main
import (
"fmt"
"os"
"os/exec"
"time"
)
func main() {
yes := exec.Command("yes")
if err := yes.Start(); err != nil {
die("%v", err)
}
time.Sleep(time.Second*2)
kill := exec.Command("kill", "-INT", fmt.Sprintf("%d", yes.Process.Pid))
if err := kill.Run(); err != nil {
die("%v", err)
}
time.Sleep(time.Second*2)
out, err := exec.Command("bash", "-c", "ps aux | grep yes").CombinedOutput()
if err != nil {
die("%v", err)
}
fmt.Println(string(out))
}
func die(msg string, args ...interface{}) {
fmt.Fprintf(os.Stderr, msg+"\n", args...)
os.Exit(1)
}
私はそれをスクリプトとして構築し、スクリプト内で実行し、main
インタラクティブに実行すると、次のような結果が出ました。./main
./main
./main &
sean ... 0:01 [yes] <defunct>
sean ... 0:00 bash -c ps aux | grep yes
sean ... 0:00 grep yes
ただし、./main &
スクリプト内で実行すると、次のようになります。
sean ... 0:03 yes
sean ... 0:00 bash -c ps aux | grep yes
sean ... 0:00 grep yes
これにより、私はこれらすべてをBashシェルで実行していますが、Bash独自のジョブ制御にはほとんど差がないと思いました。
答え1
どのシェルを使うかは疑問です。異なるシェルがジョブ制御を異なる方法で処理するためです(そして、ジョブ制御は複雑です。Cによると、現在のCの重量は3,300行ですjob.c
)。たとえば、Mac OS X 10.11の5.2.14と3.2は次のように表示されます。bash
cloc
pdksh
bash
$ cat code
pkill yes
yes >/dev/null &
pid=$!
echo $pid
sleep 2
kill -INT $pid
sleep 2
pgrep yes
$ bash code
38643
38643
$ ksh code
38650
$
ここで重要なのは、yes
信号処理が実行されないため、親シェルプロセスから継承されたすべてのエントリが継承されることです。
$ cat sighandlingcode
perl -e '$SIG{INT} = sub { die "ouch\n" }; sleep 5' &
pid=$!
sleep 2
kill -INT $pid
$ bash sighandlingcode
ouch
$ ksh sighandlingcode
ouch
$
- 信号処理が変更されたperl
のと同じではないため、親シェルが何であれ、SIGINTがトリガされます。 DTrace経由またはここでLinuxで観察yes
できる信号処理に関連するいくつかのシステムコールがあります。strace
-bash-4.2$ cat code
pkill yes
yes >/dev/null &
pid=$!
echo $pid
sleep 2
kill -INT $pid
sleep 2
pgrep yes
pkill yes
-bash-4.2$ rm foo*; strace -o foo -ff bash code
21899
21899
code: line 9: 21899 Terminated yes > /dev/null
-bash-4.2$
私たちはyes
プロセスが無視されることを発見しましたSIGINT
。
-bash-4.2$ egrep 'exec.*yes' foo.21*
foo.21898:execve("/usr/bin/pkill", ["pkill", "yes"], [/* 24 vars */]) = 0
foo.21899:execve("/usr/bin/yes", ["yes"], [/* 24 vars */]) = 0
foo.21903:execve("/usr/bin/pgrep", ["pgrep", "yes"], [/* 24 vars */]) = 0
foo.21904:execve("/usr/bin/pkill", ["pkill", "yes"], [/* 24 vars */]) = 0
-bash-4.2$ grep INT foo.21899
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x7f18ebee0250}, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, 8) = 0
--- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=21897, si_uid=1000} ---
-bash-4.2$
コードでこのテストを繰り返すと、無視されないか、無視がのように設定されていないことをperl
確認できます。 「モニターモード」がオンの場合は、インタラクティブモードと同じように死にます。SIGINT
pdksh
bash
bash
yes
-bash-4.2$ cat monitorcode
#!/bin/bash
set -m
pkill yes
yes >/dev/null &
pid=$!
echo $pid
sleep 2
kill -INT $pid
sleep 2
pgrep yes
pkill yes
-bash-4.2$ ./monitorcode
22117
[1]+ Interrupt yes > /dev/null
-bash-4.2$
答え2
バックグラウンドタスクがありますしてはいけないそれらを始めた殻に縛られています。シェルを終了すると、引き続き実行されます。したがって、SIGINT
基本的に中断してはいけません。ジョブ制御が有効になると、バックグラウンドジョブは別のプロセスグループで実行されるため、このジョブは自動的に実行されます。ジョブ制御が無効になると(通常は非対話型シェルでは)、非同期bash
コマンドは無視されますSIGINT
。
文書の関連部分:
Bashによって実行される非組み込みコマンドは、シグナルハンドラをシェルが親から継承した値に設定します。ジョブ制御が無効になると、非同期コマンドは無視され
SIGINT
ます。SIGQUIT
これらの継承されたハンドラに加えて、コマンドの置き換えの結果として実行されるコマンドは、キーボードから生成されたジョブ制御信号とを無視しますSIGTTIN
。SIGTTOU
SIGTSTP
https://www.gnu.org/software/bash/manual/html_node/Signals.html
タスク制御ユーザーインターフェースの実装を容易にするために、オペレーティングシステムは現在の端末プロセスグループIDの概念を維持しています。このプロセスグループのメンバー(現在の端末のプロセスグループIDと同じプロセスグループIDを持つプロセス)は、キーボードから生成された信号を受け取ります
SIGINT
。これらのプロセスは前景にあると言われています。バックグラウンドプロセスは、プロセスグループIDが端末プロセスグループIDと異なるプロセスである。これらのプロセスはキーボードから生成された信号の影響を受けません。フォアグラウンドプロセスのみが端末を読み書きできます(ユーザーがstty tostopを使用して指定した場合)。ターミナルから読み込みを試みる(そしてstty tostopが適用されたときに書き込みを試みる)バックグラウンドプロセスは、カーネルSIGTTIN
のターミナルドライバによって()信号を受け取り、キャッチされない限り停止します。SIGTTOU
https://www.gnu.org/software/bash/manual/html_node/Job-Control-Basics.html
関連コンテンツをもっと見るここ。