대화형 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
$
이 두 예제에서 서로 다른 종료 코드가 생성되는 이유는 무엇입니까?
답변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 프로세스 그룹(shell을 사용하여 배치됨)에 있기 때문에 종료됩니다 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
옵션을 추가하면 문제가 방지됩니다.