트랩 및 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을 시도했습니다) 트랩은 예상대로 "아야"를 인쇄합니다.

분명히 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그 사이에 심을 넣으면 기본 상태로 scriptb재설정됩니다 .SIGINT

#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의 의견에 동의하고 우리가 여기서 보고 있는 것이 버그라고 생각하는 경향이 있습니다.

관련 정보