상위 프로그램(C++ 프로그램 또는 셸 스크립트일 수 있음)이 하위 셸 스크립트를 실행하는 시나리오를 생각해 보세요. 하위 셸 스크립트가 실행되는 동안 Ctrl+C(또는 INTR 문자로 구성된 문자)를 누르면 SIGINT는 다음과 같습니다. 의 모든 프로세스를 포그라운드 프로세스 그룹으로 보냅니다. 여기에는 상위 프로세스가 포함됩니다.
이 기본 동작을 재정의하는 방법이 있습니까? 하위 프로세스가 신호를 상위 프로세스에 전파하지 않고 단독으로 처리합니까?
답변1
(Giles의 답변에서 영감을 얻었습니다)
플래그가 설정된 경우 스크립트가 상위 프로세스를 가져오지 않고 가져오는 유일한 방법은 ISIG
자체 프로세스 그룹에 스크립트를 두는 것입니다. 이는 옵션을 통해 수행할 수 있습니다.Child
SIGINT
SIGINT
set -m
-m
쉘 스크립트에서 이 옵션이 켜져 있으면 Child
상호 작용 없이 작업 제어를 수행합니다. 이로 인해 별도의 프로세스 그룹에서 작업이 실행되어 부모 프로세스가 SIGINT
읽을 때 문자를 수신하지 못하게 됩니다.INTR
여기있어-m
POSIX 옵션 설명:
-m
구현이 사용자 이식성 유틸리티 옵션을 지원하는 경우 이 옵션을 지원해야 합니다. 모든 작업은 자체 프로세스 그룹에서 실행되어야 합니다. 백그라운드 작업이 완료된 후 즉시 쉘 프롬프트가 표시되기 전에 백그라운드 작업의 종료 상태를 보고하는 메시지가 표준 오류에 기록되어야 합니다. 포그라운드 작업이 중지되면 쉘은 작업 유틸리티에서 설명하는 형식으로 표준 오류에 대한 메시지를 작성해야 합니다. 또한 작업이 종료되지 않고 상태가 변경되는 경우(예: 입력 또는 출력이 중지되거나 SIGSTOP 신호에 의해 중지되는 경우) 쉘은 다음 프롬프트를 작성하기 직전에 유사한 메시지를 작성해야 합니다. 기본적으로 대화형 셸에는 이 옵션이 활성화되어 있습니다.
이 -m
옵션은 유사 -i
하지만 쉘의 동작을 거의 크게 변경하지 않습니다 -i
.
예:
스크립트
Parent
:#!/bin/sh trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT echo "PARENT: pid=$$" echo "PARENT: Spawning child..." ./Child echo "PARENT: child returned" echo "PARENT: exiting normally"
스크립트
Child
:#!/bin/sh -m # ^^ # notice the -m option above! trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT echo "CHILD: pid=$$" echo "CHILD: hit enter to exit" read foo echo "CHILD: exiting normally"
입력을 기다리는 동안 +를 누르면 다음과 같은 Control일이 발생합니다 .CChild
$ ./Parent
PARENT: pid=12233
PARENT: Spawning child...
CHILD: pid=12234
CHILD: hit enter to exit
^CCHILD: caught SIGINT; exiting
PARENT: child returned
PARENT: exiting normally
SIGINT
부모 핸들러가 어떻게 실행되지 않는지 확인하세요 .
Parent
또는 대신 수정하려면 Child
다음을 수행할 수 있습니다.
스크립트
Parent
:#!/bin/sh trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT echo "PARENT: pid=$$" echo "PARENT: Spawning child..." sh -m ./Child # or 'sh -m -c ./Child' if Child isn't a shell script echo "PARENT: child returned" echo "PARENT: exiting normally"
스크립트
Child
(일반, 필수는 아님-m
):#!/bin/sh trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT echo "CHILD: pid=$$" echo "CHILD: hit enter to exit" read foo echo "CHILD: exiting normally"
대안적인 아이디어
SIGINT
기간 동안 무시 되도록 포그라운드 프로세스 그룹의 다른 프로세스를 수정합니다Child
. 이렇게 해도 문제가 해결되지는 않지만 원하는 것을 얻을 수 있습니다.- 로 변경
Child
:stty -g
현재 터미널 설정을 백업하는 데 사용됩니다 .- , 및 문자가 포함된 신호는 런타임
stty -isig
시 생성되지 않습니다 .INTR
QUIT
SUSP
- 백그라운드에서 터미널 입력을 읽고 필요에 따라 신호 자체를 보냅니다(예:
kill -QUIT 0
읽기+에서 실행, 읽기+에서 실행). 이는 사소한 문제가 아니며 스크립트나 실행되는 모든 항목이 대화형인 경우 원활하게 작동하지 않을 수 있습니다.Control\kill -INT $$
ControlCChild
- 종료하기 전에 터미널 설정을 복원하십시오(바람직하게는 위에서
EXIT
).
- 를 실행하는 대신 를 죽이기 전에
stty -isig
사용자가 Enter누르거나 다른 비특수 키를 누를 때 까지 기다리는 점을 제외하면 #2와 동일합니다Child
. setpgid
setpgid()
다음은 대략적인 C 구현입니다 .#define _XOPEN_SOURCE 700 #include <unistd.h> #include <signal.h> int main(int argc, char *argv[]) { // todo: add error checking void (*backup)(int); setpgid(0, 0); backup = signal(SIGTTOU, SIG_IGN); tcsetpgrp(0, getpid()); signal(SIGTTOU, backup); execvp(argv[1], argv + 1); return 1; }
사용 예
Child
:#!/bin/sh [ "${DID_SETPGID}" = true ] || { # restart self after calling setpgid(0, 0) exec env DID_SETPGID=true setpgid "$0" "$@" # exec failed if control reached this point exit 1 } unset DID_SETPGID # do stuff here
답변2
POSIX에서 인용한 장에서 설명했듯이 SIGINT는 전체 포그라운드 프로세스 그룹으로 전송됩니다. 따라서 프로그램의 상위 프로세스가 종료되는 것을 방지하려면 자체 프로세스 그룹에서 실행되도록 예약하십시오.
셸은 setpgrp
내장 또는 구문 구성을 통해 액세스할 수 없지만 셸을 대화식으로 실행하는 간접적인 방법이 있습니다. (감사해요스티븐 히메네즈기술을 위해. )
ksh -ic '
… the part that needs to be interruptible without bothering the parent …
'
답변3
글쎄, 당신이 참조한 스택 오버플로 질문에서 신호를 처리하도록 상위 항목을 구성해야 함을 분명히 나타냅니다.
둘째, POSIX 참조에는 "ISIG가 설정된 경우 INTR 문자를 처리에서 버려야 합니다"라고 명확하게 명시되어 있습니다.
두 가지 옵션이 있습니다. 세 번째 방법은 자체 프로세스 그룹에서 하위 프로세스를 실행하는 것입니다.