インタラクティブsh
シェルセッション:
$ sh
$ timeout 1 yes | sed -n s/a/b/p ; echo $?
Terminated
143
$
以下の非対話型スクリプトsh -c
:
$ sh -c 'timeout 1 yes | sed -n s/a/b/p ; echo $?'
0
$
この2つの例で異なる終了コードが生成されるのはなぜですか?
答え1
timeout
(timeout
少なくともGNU)デフォルトでは、新しいプロセスグループでコマンドを実行しようとし、タイムアウト時にSIGTERMを使用してプロセスグループを終了します。
これにより、コマンドがより多くのプロセスを生成すると、そのプロセスもタイムアウト時に終了します。
~$ strace -fze '/[sg]etpg|exec|kill|exit' sh -c 'timeout 1 sleep 2 | sleep 3'
execve("/usr/bin/sh", ["sh", "-c", "timeout 1 sleep 2 | sleep 3"], 0x7fffe2349230 /* 69 vars */) = 0
strace: Process 316058 attached
strace: Process 316059 attached
[pid 316058] execve("/usr/bin/timeout", ["timeout", "1", "sleep", "2"], 0x561e384e43a8 /* 69 vars */) = 0
[pid 316059] execve("/usr/bin/sleep", ["sleep", "3"], 0x561e384e41c8 /* 69 vars */) = 0
[pid 316058] setpgid(0, 0) = 0
strace: Process 316060 attached
[pid 316060] execve("/usr/bin/sleep", ["sleep", "2"], 0x7ffc3ef29910 /* 69 vars */) = 0
[pid 316058] --- SIGALRM {si_signo=SIGALRM, si_code=SI_TIMER, si_timerid=0, si_overrun=0, si_int=0, si_ptr=NULL} ---
[pid 316058] kill(316060, SIGTERM) = 0
[pid 316060] --- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=316058, si_uid=1000} ---
[pid 316058] kill(0, SIGTERM) = 0
[pid 316058] --- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=316058, si_uid=1000} ---
[pid 316060] +++ killed by SIGTERM +++
[pid 316058] kill(316060, SIGCONT <unfinished ...>
) = 0
[pid 316058] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_KILLED, si_pid=316060, si_uid=1000, si_status=SIGTERM, si_utime=0, si_stime=0} ---
[pid 316058] kill(0, SIGCONT) = 0
[pid 316058] --- SIGCONT {si_signo=SIGCONT, si_code=SI_USER, si_pid=316058, si_uid=1000} ---
[pid 316058] +++ exited with 124 +++
[pid 316057] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=316058, si_uid=1000, si_status=124, si_utime=0, si_stime=0} ---
[pid 316059] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=316059, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
新しいプロセスグループを作成するためにtimeout
a setpgid(0, 0)
(と同じ)を実行するを参照してください。 Killを実行すると同時に、独自のプロセスグループ(以前に作成した)も終了し、processによって生成されたグループも終了します(存在する場合)。この場合は何もありません)。setpgrp()
kill(316060, SIGTERM)
sleep 2
kill(0, SIGTERM)
sleep 2
これで、シェルでジョブ制御が有効になると(たとえば、対話的にまたは-m
/-o monitor
オプションを使用して呼び出される場合)、次のようになります。
timeout 1 sleep 2 | sleep 3
シェルが開始され、timeout
新しいsleep 3
プロセスグループに属し、ほとんどのシェルでは、プロセスグループリーダーは実行中のプロセスグループになりますtimeout
。
したがってtimeout
、実行すると、setpgrp()
同じプロセスと同様に、setpgid(0, 0)
新しいプロセスグループを作成せず、何もしません。したがって、プロセスグループには実行用に作成され、シェルによって事前に配置されたプロセスが含まれますtimeout
。sleep 2
sleep 3
~$ strace -fze '/[sg]etpg|exec|kill|exit' sh -o monitor -c 'timeout 1 sleep 2 | sleep 3'
execve("/usr/bin/sh", ["sh", "-o", "monitor", "-c", "timeout 1 sleep 2 | sleep 3"], 0x7ffeb8e13640 /* 69 vars */) = 0
getpgrp() = 318590
setpgid(0, 318593) = 0
strace: Process 318594 attached
[pid 318593] setpgid(318594, 318594) = 0
[pid 318594] setpgid(0, 318594) = 0
strace: Process 318595 attached
[pid 318595] setpgid(0, 318594) = 0
[pid 318593] setpgid(318595, 318594) = 0
[pid 318595] execve("/usr/bin/sleep", ["sleep", "3"], 0x557fde7871c8 /* 69 vars */) = 0
[pid 318594] execve("/usr/bin/timeout", ["timeout", "1", "sleep", "2"], 0x557fde7873a8 /* 69 vars */) = 0
[pid 318594] setpgid(0, 0) = 0
strace: Process 318596 attached
[pid 318596] execve("/usr/bin/sleep", ["sleep", "2"], 0x7ffc60f34c60 /* 69 vars */) = 0
[pid 318594] --- SIGALRM {si_signo=SIGALRM, si_code=SI_TIMER, si_timerid=0, si_overrun=0, si_int=0, si_ptr=NULL} ---
[pid 318594] kill(318596, SIGTERM) = 0
[pid 318596] --- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=318594, si_uid=1000} ---
[pid 318596] +++ killed by SIGTERM +++
[pid 318594] kill(0, SIGTERM <unfinished ...>
) = 0
[pid 318595] --- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=318594, si_uid=1000} ---
[pid 318594] --- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=318594, si_uid=1000} ---
[pid 318595] +++ killed by SIGTERM +++
[pid 318594] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_KILLED, si_pid=318596, si_uid=1000, si_status=SIGTERM, si_utime=0, si_stime=0} ---
[pid 318593] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_KILLED, si_pid=318595, si_uid=1000, si_status=SIGTERM, si_utime=0, si_stime=0} ---
Terminated
[pid 318594] kill(318596, SIGCONT) = 0
[pid 318594] kill(0, SIGCONT) = 0
[pid 318594] --- SIGCONT {si_signo=SIGCONT, si_code=SI_USER, si_pid=318594, si_uid=1000} ---
[pid 318594] +++ exited with 124 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=318594, si_uid=1000, si_status=124, si_utime=0, si_stime=0} ---
setpgid(0, 318590) = 0
+++ exited with 143 +++
今回は、kill(0, SIGTERM)
実行中のプロセスがsleep 3
318594プロセスグループ(シェルを使用して配置されている)にあるため終了しますsetpgid(318595, 318594)
。
存在する:
sleep 2 | timeout 1 sleep 3 | sleep 4
パイプは4秒間持続し、sleep 3
1秒後にのみ終了します。なぜなら、この時点でシェルによって生成されたプロセスグループは実行中のプロセスによってブートストラップされ、それ自体sleep 2
でtimeout
新しいプロセスを作成できるからです。 children)group)これは、パイプラインの3つのコマンドに対してシェルが生成するプロセスグループとは異なります(したがって、+または+がフォアグラウンドプロセスグループで実行されないため、実行時に正しく機能しないことがsleep 3
わかります)。CtrlCCtrlZtimeout
timeout
ランタイムを使用すると、追加の--foreground
プロセスtimeout
グループの作成をスキップしてkill(0, SIGTERM)
自分のプロセスグループを終了する操作は行われないため、動作は一貫していますが、孫プロセスは終了しません。
$ strace -fze '/[sg]etpg|ioctl|exec|kill|exit' sh -o monitor -c 'timeout --foreground 1 sh -c "sleep 2; exit" | sleep 3'
execve("/usr/bin/sh", ["sh", "-o", "monitor", "-c", "timeout --foreground 1 sh -c \"sl"...], 0x7ffe289c2910 /* 69 vars */) = 0
ioctl(10, TIOCGPGRP, [331754]) = 0
getpgrp() = 331754
setpgid(0, 331757) = 0
ioctl(10, TIOCSPGRP, [331757]) = 0
strace: Process 331758 attached
[pid 331757] setpgid(331758, 331758) = 0
[pid 331758] setpgid(0, 331758) = 0
[pid 331758] ioctl(10, TIOCSPGRP, [331758]) = 0
strace: Process 331759 attached
[pid 331757] setpgid(331759, 331758) = 0
[pid 331759] setpgid(0, 331758) = 0
[pid 331759] ioctl(10, TIOCSPGRP, [331758]) = 0
[pid 331759] execve("/usr/bin/sleep", ["sleep", "3"], 0x55dc52f76418 /* 69 vars */) = 0
[pid 331758] execve("/usr/bin/timeout", ["timeout", "--foreground", "1", "sh", "-c", "sleep 2; exit"], 0x55dc52f763a8 /* 69 vars */) = 0
strace: Process 331760 attached
[pid 331760] execve("/usr/bin/sh", ["sh", "-c", "sleep 2; exit"], 0x7fff756826c0 /* 69 vars */) = 0
strace: Process 331761 attached
[pid 331761] execve("/usr/bin/sleep", ["sleep", "2"], 0x557447790168 /* 69 vars */) = 0
[pid 331758] --- SIGALRM {si_signo=SIGALRM, si_code=SI_TIMER, si_timerid=0, si_overrun=0, si_int=0, si_ptr=NULL} ---
[pid 331758] kill(331760, SIGTERM) = 0
[pid 331760] --- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=331758, si_uid=1000} ---
[pid 331760] +++ killed by SIGTERM +++
[pid 331758] +++ exited with 124 +++
[pid 331757] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=331758, si_uid=1000, si_status=124, si_utime=0, si_stime=0} ---
[pid 331761] +++ exited with 0 +++
[pid 331759] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=331759, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
ioctl(10, TIOCSPGRP, [331757]) = 0
ioctl(10, TIOCSPGRP, [331754]) = 0
setpgid(0, 331754) = 0
+++ exited with 0 +++
sh
殺されたけどsleep 2
。
これは、端末の対話型シェルの理由も説明します。
sh -c 'timeout 10 cat; exit'
または:
sleep 10 | timeout 10 cat /dev/tty
cat
端末から読み取れません。新しいプロセスグループにあるため、端末のフォアグラウンドプロセスグループに属さなくなったため、試みると停止します。
繰り返しますが、この--foreground
オプションを追加すると問題が回避されます。