SIGINT가 상위 프로세스로 전파되는 것을 방지

SIGINT가 상위 프로세스로 전파되는 것을 방지

상위 프로그램(C++ 프로그램 또는 셸 스크립트일 수 있음)이 하위 셸 스크립트를 실행하는 시나리오를 생각해 보세요. 하위 셸 스크립트가 실행되는 동안 Ctrl+C(또는 INTR 문자로 구성된 문자)를 누르면 SIGINT는 다음과 같습니다. 의 모든 프로세스를 포그라운드 프로세스 그룹으로 보냅니다. 여기에는 상위 프로세스가 포함됩니다.

원천:POSIX.1-2008 XBD 섹션 11.1.9

이 기본 동작을 재정의하는 방법이 있습니까? 하위 프로세스가 신호를 상위 프로세스에 전파하지 않고 단독으로 처리합니까?

인용하다:스택 오버플로 포스트 - 하위 프로세스가 중단되면 상위 프로세스가 완료되지 않음(TRAP INT)

답변1

(Giles의 답변에서 영감을 얻었습니다)

플래그가 설정된 경우 스크립트가 상위 프로세스를 가져오지 않고 가져오는 유일한 방법은 ISIG자체 프로세스 그룹에 스크립트를 두는 것입니다. 이는 옵션을 통해 수행할 수 있습니다.ChildSIGINTSIGINTset -m

-m쉘 스크립트에서 이 옵션이 켜져 있으면 Child상호 작용 없이 작업 제어를 수행합니다. 이로 인해 별도의 프로세스 그룹에서 작업이 실행되어 부모 프로세스가 SIGINT읽을 때 문자를 수신하지 못하게 됩니다.INTR

여기있어-mPOSIX 옵션 설명:

-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"
    

대안적인 아이디어

  1. SIGINT기간 동안 무시 되도록 포그라운드 프로세스 그룹의 다른 프로세스를 수정합니다 Child. 이렇게 해도 문제가 해결되지는 않지만 원하는 것을 얻을 수 있습니다.
  2. 로 변경 Child:
    1. stty -g현재 터미널 설정을 백업하는 데 사용됩니다 .
    2. , 및 문자가 포함된 신호는 런타임 stty -isig시 생성되지 않습니다 .INTRQUITSUSP
    3. 백그라운드에서 터미널 입력을 읽고 필요에 따라 신호 자체를 보냅니다(예: kill -QUIT 0읽기+에서 실행, 읽기+에서 실행). 이는 사소한 문제가 아니며 스크립트나 실행되는 모든 항목이 대화형인 경우 원활하게 작동하지 않을 수 있습니다.Control\kill -INT $$ControlCChild
    4. 종료하기 전에 터미널 설정을 복원하십시오(바람직하게는 위에서 EXIT).
  3. 를 실행하는 대신 를 죽이기 전에 stty -isig사용자가 Enter누르거나 다른 비특수 키를 누를 때 까지 기다리는 점을 제외하면 #2와 동일합니다 Child.
  4. setpgidsetpgid()다음은 대략적인 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 문자를 처리에서 버려야 합니다"라고 명확하게 명시되어 있습니다.

두 가지 옵션이 있습니다. 세 번째 방법은 자체 프로세스 그룹에서 하위 프로세스를 실행하는 것입니다.

관련 정보