트랩과 SIGINT의 이상한 문제

트랩과 SIGINT의 이상한 문제

설명 해주십시오:

#!/bin/bash
# This is scripta.sh
./scriptb.sh &
pid=$!
echo $pid started
sleep 3
while true
do
    kill -SIGINT $pid
    echo scripta.sh $$
    sleep 3
done 

-

#!/bin/bash
# This is scriptb.sh
trap "echo Ouch;" SIGINT

while true
do
 echo scriptb.sh $$
 sleep 1
done

트랩을 실행하면 ./scripta.sh인쇄되지 않습니다. SIGINT에서 다른 신호로 전환하면(SIGTERM, SIGUSR1을 시도했습니다) 트랩은 예상대로 "Ouch"를 인쇄합니다. 어떻게 이런 일이 일어날 수 있습니까?

답변1

SIGINT에서 다른 신호로 전환하면(SIGTERM, SIGUSR1을 시도했습니다) 트랩은 예상대로 "Ouch"를 인쇄합니다.

분명히 SIGQUIT를 시도하지 않았다면 SIGINT와 동일하게 동작한다는 것을 알 수 있을 것입니다.

문제는 업무 통제다.

Unix 초기에는 쉘이 프로세스나 파이프를 백그라운드에 넣을 때마다 해당 프로세스가 SIGINT 및 SIGQUIT를 무시하도록 설정했기 때문에 사용자가 Ctrl+ C(중단) 또는 Ctrl+ (종료)를 입력해도 \프로세스가 종료되지 않았습니다. . 포그라운드 작업. 작업 제어가 등장했을 때 프로세스 그룹도 함께 가져왔으므로 이제 셸에서 해야 할 일은 백그라운드 작업을 현재 터미널 프로세스 그룹이 아닌 한 새 프로세스 그룹에 넣는 것뿐입니다. 키보드의 입력( Ctrl+ C, Ctrl+ \Ctrl+ Z(SIGTSTP)). 셸을 사용하면 백그라운드 프로세스가 기본 신호 처리를 유지할 수 있습니다. 실제로 프로세스가 Ctrl포그라운드로 나올 때 +종료되어야 할 수도 있습니다.C

그러나 비대화형 쉘은 작업 제어를 사용하지 않습니다. 역사적인 이유로 비대화형 쉘은 백그라운드 프로세스에 대해 SIGINT 및 SIGQUIT를 무시하는 이전 동작으로 돌아가 키보드 유형 신호가 전송되더라도 백그라운드 프로세스가 계속 실행되도록 하는 것이 합리적입니다. 쉘 스크립트는 비대화형 쉘에서 실행됩니다.

trap그리고, 명령 아래 마지막 문단을 보면큰 타격(1), 당신은 볼 것이다

셸에 들어갈 때 무시되는 신호는 포착하거나 재설정할 수 없습니다.

./scriptb.sh & 그러니 당신이 당신 에게서 도망치면인터렉티브셸의 명령 프롬프트에서 해당 신호 구성은 변경되지 않고(백그라운드에 배치된 경우에도) 명령이 trap예상대로 작동합니다. 그러나 ./scripta.sh(사용 여부에 관계없이 ) 실행하면 &비대화형 셸에서 스크립트가 실행됩니다. 비대화형 쉘이 실행되면 인터럽트를 무시하고 종료하도록 프로세스를 설정합니다 ./scriptb.sh &. scriptb따라서 명령은 trap자동으로 scriptb.sh실패합니다 .

답변2

일부 추적을 통해:

strace -o aaa ./scripta

우리는 기본적으로 이를 관찰할 수 있습니다

read(255, "#!/bin/bash\n# this is scripta.sh"..., 153) = 153
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0
lseek(255, -108, SEEK_CUR)              = 45
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f1ee9b7ca10) = 2483

scripta차단 INT및 신호에도 동일하게 적용됩니다. 이러한 설정은 CHLD(여기서 호출됨)에 의해 상속됩니다. scriptb그리고 무슨 일이 일어나고 있습니까? 다음을 통해 실행한다면:forkclonescriptbscripta

strace -o bbb ./scriptb &

그런 다음 신호 관련 항목을 검색하면서 다음을 발견했습니다.

rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7fb1b2e75250}, {SIG_IGN, [], 0}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x7fb1b2e75250}, {SIG_DFL, [], SA_RESTORER, 0x7fb1b2e75250}, 8) = 0

이는 아무것도 차단되지 않았으며 INT신호에 먼저 기본 처리가 부여된 다음 무시됨을 나타냅니다. scriptb셸에서 직접 비교를 실행하면 다음이 strace표시됩니다.

rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0
rt_sigaction(SIGINT, {0x45fbf0, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1], [INT CHLD], 8) = 0
...

또는 수면 호출 처리를 입력하고 반복하기 전에 결코 무시되지 않습니다. 좋아요 잘. 기본값으로 재설정하려면 scripta와 사이에 공백을 넣으 세요 ...scriptbSIGINT

#include <getopt.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int ch;
    while ((ch = getopt(argc, argv, "h?")) != -1) {
        switch (ch) {
        case 'h':
        case '?':
        default:
            abort();
        }
    }
    argc -= optind;
    argv += optind;
    if (argc < 1) abort();

    signal(SIGINT, SIG_DFL);

    execvp(*argv, argv);
    abort();
    return 1;
}

사용 방법:

$ make defaultsig
cc     defaultsig.c   -o defaultsig
$ grep defaultsig scripta.sh
./defaultsig ./scriptb.sh &
$ ./scripta.sh 
12510 started
scriptb.sh 12510
scriptb.sh 12510
scriptb.sh 12510
scripta.sh 12509
Ouch
scriptb.sh 12510
scriptb.sh 12510
scriptb.sh 12510
scripta.sh 12509
Ouch
...

예, 지금은 잘 작동합니다. 하지만 왜 이런 일이 발생하는지 모르겠습니다 bash. 버그를 신고할 수 있을까요? 의 코드는 sig.c매우 복잡해 보이며 다른 곳에서는 더 많은 신호 처리가 있습니다...

답변3

트랩과 SIGINT의 이상한 문제

답변해 주시고 시간을 내어 이 문제를 조사해 주신 모든 분들께 감사드립니다.

다시 설명하고 통합하겠습니다(아래에서 명백하게 드러나는 내용에 대해 사과드립니다).

1) SIGQUIT도 시도했는데 SIGINT와 동일하게 작동한다는 질문을 추가하는 것을 잊었습니다.

2) 이것으로부터 나는 문제가 이 두 신호에 대한 대화형 bash의 기본 구성과 관련이 있다고 이미 의심합니다.

3) bash와 상호 작용할 때 프롬프트만 있을 때 아무것도 종료하거나 중단할 필요가 없기 때문에 기본 작업이 발생하지 않습니다. 쉘을 종료하려면 그냥 종료를 입력하십시오.

4) SIGQUIT 및 SIGINT가 작업 제어에서 특별한 역할을 한다고 생각하지 않습니다(SIGTSTP, SIGTTOU 및 SIGTTIN과 반대).

5) 이 두 신호에 대한 대화형 bash의 기본 구성이 배경(비대화형) 셸(우리의 경우 scriptb.sh를 실행하는 셸)에 의해 상속된다는 것이 나에게는 이해가 되지 않습니다.

6) 실제로 포그라운드 프로세스 그룹이 SIGQUIT 및 SIGINT의 구성을 (실행한 셸에서) 상속하지 않는 것처럼 IMHO는 백그라운드 프로세스 그룹에서도 동일한 일이 발생하는 것이 합리적입니다.

7) 또한, 물려받은 캐릭터가 무엇이든 트랩은 이를 변경해야 합니다.

8) 대체로 나는 thrig의 의견에 동의하고 여기서 보고 있는 것이 버그라고 생각하는 경향이 있습니다.

관련 정보