SIGINT가 스크립트의 백그라운드 프로세스에서 작동하지 않는 이유는 무엇입니까?

SIGINT가 스크립트의 백그라운드 프로세스에서 작동하지 않는 이유는 무엇입니까?

내 스크립트에는 다음이 있습니다.

yes >/dev/null &
pid=$!
echo $pid
sleep 2
kill -INT $pid
sleep 2
ps aux | grep yes

yes이를 실행하면 스크립트가 종료될 때에도 여전히 실행 중이라고 출력됩니다 . 그러나 명령을 대화형으로 실행하면 프로세스가 다음과 같이 성공적으로 종료됩니다.

> yes >/dev/null &
[1] 9967
> kill -INT 9967
> ps aux | grep yes
sean ... 0:00 grep yes

SIGINT가 대화형 인스턴스에서는 프로세스를 종료하지만 스크립트 인스턴스에서는 프로세스를 종료하지 않는 이유는 무엇입니까?

편집하다

다음은 문제 진단에 도움이 될 수 있는 몇 가지 추가 정보입니다. 위 스크립트를 시뮬레이션하기 위해 다음 Go 프로그램을 작성했습니다.

package main

import (
    "fmt"
    "os"
    "os/exec"
    "time"
)

func main() {
    yes := exec.Command("yes")
    if err := yes.Start(); err != nil {
        die("%v", err)
    }

    time.Sleep(time.Second*2)

    kill := exec.Command("kill", "-INT", fmt.Sprintf("%d", yes.Process.Pid))
    if err := kill.Run(); err != nil {
        die("%v", err)
    }

    time.Sleep(time.Second*2)

    out, err := exec.Command("bash", "-c", "ps aux | grep yes").CombinedOutput()
    if err != nil {
        die("%v", err)
    }
    fmt.Println(string(out))
}

func die(msg string, args ...interface{}) {
    fmt.Fprintf(os.Stderr, msg+"\n", args...)
    os.Exit(1)
}

나는 그것을 스크립트로 구축하고 스크립트 내에서 실행했으며 main대화식으로 실행하면 다음과 같은 결과가 나왔습니다../main./main./main &

sean ... 0:01 [yes] <defunct>
sean ... 0:00 bash -c ps aux | grep yes
sean ... 0:00 grep yes

그러나 ./main &스크립트 내에서 실행하면 다음이 발생합니다.

sean ... 0:03 yes
sean ... 0:00 bash -c ps aux | grep yes
sean ... 0:00 grep yes

이로 인해 나는 이 모든 것을 Bash 셸에서 실행하고 있지만 Bash 자체 작업 제어에는 차이가 거의 없다고 믿게 되었습니다.

답변1

어떤 쉘을 사용할지는 의문입니다. 서로 다른 쉘이 작업 제어를 다르게 처리하기 때문입니다(그리고 작업 제어는 복잡합니다. C에 따르면 현재 C의 무게는 3,300줄입니다 job.c). 예를 들어 Mac OS X 10.11의 5.2.14와 3.2는 다음과 같이 표시됩니다.bashclocpdkshbash

$ cat code
pkill yes
yes >/dev/null &
pid=$!
echo $pid
sleep 2
kill -INT $pid
sleep 2
pgrep yes
$ bash code
38643
38643
$ ksh code
38650
$ 

여기서 중요한 점은 yes신호 처리가 수행되지 않으므로 부모 쉘 프로세스에서 상속된 모든 항목이 상속된다는 것입니다.

$ cat sighandlingcode 
perl -e '$SIG{INT} = sub { die "ouch\n" }; sleep 5' &
pid=$!
sleep 2
kill -INT $pid
$ bash sighandlingcode 
ouch
$ ksh sighandlingcode 
ouch
$ 

- 신호 처리가 변경된 perl것과 같지 않기 때문에 상위 쉘이 무엇이든 SIGINT가 트리거됩니다 . DTrace를 통해 또는 여기 Linux에서 관찰 yes할 수 있는 신호 처리와 관련된 일부 시스템 호출이 있습니다 .strace

-bash-4.2$ cat code
pkill yes
yes >/dev/null &
pid=$!
echo $pid
sleep 2
kill -INT $pid
sleep 2
pgrep yes
pkill yes
-bash-4.2$ rm foo*; strace -o foo -ff bash code
21899
21899
code: line 9: 21899 Terminated              yes > /dev/null
-bash-4.2$ 

우리는 yes프로세스가 무시되는 것을 발견했습니다 SIGINT.

-bash-4.2$ egrep 'exec.*yes' foo.21*
foo.21898:execve("/usr/bin/pkill", ["pkill", "yes"], [/* 24 vars */]) = 0
foo.21899:execve("/usr/bin/yes", ["yes"], [/* 24 vars */]) = 0
foo.21903:execve("/usr/bin/pgrep", ["pgrep", "yes"], [/* 24 vars */]) = 0
foo.21904:execve("/usr/bin/pkill", ["pkill", "yes"], [/* 24 vars */]) = 0
-bash-4.2$ grep INT foo.21899
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x7f18ebee0250}, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, 8) = 0
--- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=21897, si_uid=1000} ---
-bash-4.2$ 

코드로 이 테스트를 반복하면 무시되지 않거나 무시가 에서와 같이 설정되지 않았 음을 perl확인할 수 있습니다 . "모니터 모드"가 켜져 있으면 대화형 모드와 마찬가지로 죽 습니다.SIGINTpdkshbashbashyes

-bash-4.2$ cat monitorcode 
#!/bin/bash
set -m
pkill yes
yes >/dev/null &
pid=$!
echo $pid
sleep 2
kill -INT $pid
sleep 2
pgrep yes
pkill yes
-bash-4.2$ ./monitorcode 
22117
[1]+  Interrupt               yes > /dev/null
-bash-4.2$ 

답변2

백그라운드 작업이 있습니다해서는 안 된다그들을 시작한 껍질에 묶여 있습니다. 쉘을 종료하면 계속 실행됩니다. 따라서 SIGINT기본적으로 중단되어서는 안 됩니다. 작업 제어가 활성화되면 백그라운드 작업이 별도의 프로세스 그룹에서 실행되기 때문에 이 작업이 자동으로 수행됩니다. 작업 제어가 비활성화되면(일반적으로 비대화형 쉘에서) bash비동기 명령이 무시됩니다 SIGINT.

문서의 관련 부분:

Bash가 실행한 비내장 명령은 신호 처리기를 쉘이 상위로부터 상속받은 값으로 설정합니다.작업 제어가 비활성화되면 비동기 명령이 무시 SIGINT되고SIGQUIT이러한 상속된 핸들러 외에도 명령 대체의 결과로 실행되는 명령은 키보드에서 생성된 작업 제어 신호 및 를 무시 합니다 SIGTTIN.SIGTTOUSIGTSTP

https://www.gnu.org/software/bash/manual/html_node/Signals.html

작업 제어 사용자 인터페이스의 구현을 용이하게 하기 위해 운영 체제는 현재 터미널 프로세스 그룹 ID의 개념을 유지합니다. 이 프로세스 그룹의 구성원(현재 터미널의 프로세스 그룹 ID와 동일한 프로세스 그룹 ID를 가진 프로세스)은 키보드에서 생성된 신호를 수신합니다 SIGINT. 이러한 프로세스를 포그라운드에 있다고 합니다.백그라운드 프로세스는 프로세스 그룹 ID가 터미널 프로세스 그룹 ID와 다른 프로세스입니다. 이러한 프로세스는 키보드에서 생성된 신호의 영향을 받지 않습니다.포그라운드 프로세스만 터미널을 읽거나 쓸 수 있습니다(사용자가 stty tostop을 사용하여 지정한 경우). 터미널에서 읽기를 시도하는(그리고 stty tostop이 적용될 때 쓰기를 시도하는) 백그라운드 프로세스는 SIGTTIN커널의 터미널 드라이버에 의해 ( ) 신호를 받게 되며 잡히지 않는 한 정지됩니다.SIGTTOU

https://www.gnu.org/software/bash/manual/html_node/Job-Control-Basics.html

관련 콘텐츠 더보기여기.

관련 정보