프로세스 종료시 기본 종료 코드?


54

처리 할 수있는 신호와 같이 SIGINT또는 처리 할 수없는 신호로 프로세스가 SIGTERM종료되면 프로세스의 종료 코드는 무엇입니까?

처리 할 수없는 신호는 SIGKILL어떻습니까?

내가 알 수 있듯이 프로세스를 SIGINT종료하면 종료 코드 가 발생할 수 130있지만 커널 또는 셸 구현에 따라 다를 수 있습니까?

$ cat myScript
#!/bin/bash
sleep 5
$ ./myScript
<ctrl-c here>
$ echo $?
130

다른 신호를 어떻게 테스트할지 모르겠습니다.

$ ./myScript &
$ killall myScript
$ echo $?
0  # duh, that's the exit code of killall
$ killall -9 myScript
$ echo $?
0  # same problem

1
당신의 killall myScript작품, 따라서 killall 의 반환 (그리고 스크립트가 아닙니다!)은 0입니다. kill -x $$[x는 신호 번호이며 $$는 일반적으로 쉘에 의해 스크립트의 PID로 확장됩니다 (sh, bash, ...)] 스크립트 에서 종료 코어가 무엇인지 테스트하십시오.
Olivier Dulac


세미 질문에 대한 의견 : myScript를 백그라운드에 두지 마십시오. (생략 &). 다른 터미널에서 다른 쉘 프로세스의 신호를 보내면 $?myScript가 끝난 후에 사용할 수 있습니다 .
MattBianco

답변:


61

프로세스는 정수 인수로 _exit()시스템 호출 (Linux의 경우 참조 exit_group())을 호출 하여 종료 코드를 상위에보고 할 수 있습니다. 정수이지만 부모는 8 개의 최하위 비트 만 사용할 수 있습니다 ( 리눅스에서는 아니지만 부모에서 SIGCHLD에서 또는 처리기를 사용하여 waitid()해당 코드를 검색 할 때 제외 ).

부모는 일반적으로 wait()또는 자녀 waitpid()상태 를 정수로 가져 오거나 ( waitid()시맨틱이 약간 다른 의미를 가질 수도 있지만)

Linux 및 대부분의 Unices에서 프로세스가 정상적으로 종료되면 해당 상태 번호 의 8에서 15까지 비트에 전달 된 종료 코드가 포함됩니다 exit(). 그렇지 않으면 7 개의 최하위 비트 (0-6)에 신호 번호가 포함되고 코어가 덤프 된 경우 비트 7이 설정됩니다.

perl$?가 설정 한 예를 들어 해당 번호를 포함 waitpid():

$ perl -e 'system q(kill $$); printf "%04x\n", $?'
000f # killed by signal 15
$ perl -e 'system q(kill -ILL $$); printf "%04x\n", $?'
0084 # killed by signal 4 and core dumped
$ perl -e 'system q(exit $((0xabc))); printf "%04x\n", $?'
bc00 # terminated normally, 0xbc the lowest 8 bits of the status

Bourne과 같은 쉘은 또한 자신의 $?변수 에서 마지막 실행 명령의 종료 상태를 만듭니다 . 그러나에 의해 반환 된 숫자는 직접 포함하지 않지만 waitpid()변환은 쉘마다 다릅니다.

프로세스가 정상적으로 종료 된 경우 모든 쉘에서 공통적 $?인 것은 종료 코드의 가장 낮은 8 비트 (에 전달 된 수 exit())를 포함한다는 것입니다.

프로세스가 신호에 의해 종료되는 시점이 다릅니다. 모든 경우에 POSIX에서 요구하는 숫자는 128보다 큽니다. POSIX는 값이 무엇인지 지정하지 않습니다. 실제로, 내가 알고있는 모든 Bourne과 같은 쉘에서 가장 낮은 7 비트 $?에는 신호 번호가 포함됩니다. 그러나 n신호 번호는 어디에 있습니까?

  • ash, zsh, pdksh, bash, Bourne 쉘 $?128 + n입니다. 무슨 의미하는 것은 당신이 얻을 경우 그 껍질에,이다 $?129, 당신은 프로세스로 종료 있기 때문에인지 모르는 exit(129)또는이 신호에 의해 살해되었는지 여부 1( HUP대부분의 시스템에서). 그러나 이론적 근거는 쉘이 스스로 종료 될 때 기본적으로 마지막 종료 된 명령의 종료 상태를 반환한다는 것입니다. $?절대 255보다 크지 않도록 하여 일관된 종료 상태를 유지할 수 있습니다.

    $ bash -c 'sh -c "kill \$\$"; printf "%x\n" "$?"'
    bash: line 1: 16720 Terminated              sh -c "kill \$\$"
    8f # 128 + 15
    $ bash -c 'sh -c "kill \$\$"; exit'; printf '%x\n' "$?"
    bash: line 1: 16726 Terminated              sh -c "kill \$\$"
    8f # here that 0x8f is from a exit(143) done by bash. Though it's
       # not from a killed process, that does tell us that probably
       # something was killed by a SIGTERM
    
  • ksh93, $?이다 256 + n. 즉, $?사용자 의 가치에서 종료 프로세스와 종료되지 않은 프로세스를 구별 할 수 있습니다. ksh종료시 $?255보다 큰 경우 최신 버전의 에서는 동일한 종료 상태를 상위에보고 할 수 있도록 동일한 신호로 자체를 종료합니다. 그것은 좋은 생각처럼 들리지만 ksh, 코어 생성 신호에 의해 프로세스가 종료되면 여분의 코어 덤프를 생성합니다 (다른 잠재적으로 다른 것을 덮어 쓸 것).

    $ ksh -c 'sh -c "kill \$\$"; printf "%x\n" "$?"'
    ksh: 16828: Terminated
    10f # 256 + 15
    $ ksh -c 'sh -c "kill -ILL \$\$"; exit'; printf '%x\n' "$?"
    ksh: 16816: Illegal instruction(coredump)
    Illegal instruction(coredump)
    104 # 256 + 15, ksh did indeed kill itself so as to report the same
        # exit status as sh. Older versions of `ksh93` would have returned
        # 4 instead.
    

    함수 가 수행 한 ksh93경우에도 자체 를 죽이는 버그가 있다고 말할 수도 있습니다.$?return 257

    $ ksh -c 'f() { return "$1"; }; f 257; exit'
    zsh: hangup     ksh -c 'f() { return "$1"; }; f 257; exit'
    # ksh kills itself with a SIGHUP so as to report a 257 exit status
    # to its parent
    
  • yash. yash타협을 제공합니다. 를 반환합니다 256 + 128 + n. 즉, 종료 된 프로세스와 올바르게 종료 된 프로세스를 구별 할 수도 있습니다. 그리고 나가면 128 + n자살하지 않고 부작용을 보고 하지 않습니다.

    $ yash -c 'sh -c "kill \$\$"; printf "%x\n" "$?"'
    18f # 256 + 128 + 15
    $ yash -c 'sh -c "kill \$\$"; exit'; printf '%x\n' "$?"
    8f  # that's from a exit(143), yash was not killed
    

의 값에서 신호를 얻으려면 $?휴대용 방법을 사용하십시오 kill -l.

$ /bin/kill 0
Terminated
$ kill -l "$?"
TERM

(이동성을 위해 신호 번호를 사용하지 말고 신호 이름 만 사용해야합니다)

Bourne 이외의 전선에서 :

  • csh/ tcshfish상태는 것을 제외하고는 본 쉘 동일 $status대신 $?(참고 zsh또한 설정 $status과 호환성 csh(추가하여 $?)).
  • rc: 종료 상태도 $status있지만 신호에 의해 종료 되면 해당 변수 에 숫자 대신 신호 이름 ( 코어 생성 sigterm또는 sigill+core생성 된 경우)이 포함됩니다. 이는 해당 쉘의 우수한 설계에 대한 또 다른 증거입니다 .
  • es. 종료 상태는 변수가 아닙니다. 관심이 있다면 다음과 같이 명령을 실행하십시오.

    status = <={cmd}
    

    이는 숫자 또는 반환 sigterm또는 sigsegv+core처럼 rc.

어쩌면 완전성을 위해, 우리는 언급해야 zsh'의들 $pipestatusbash$PIPESTATUS마지막 파이프 라인의 구성 요소의 종료 상태를 포함하는 배열.

또한 쉘 함수 및 소스 파일에 관해서는 기본적으로 함수는 마지막 명령 실행의 종료 상태로 리턴되지만 return내장 상태로 명시 적으로 리턴 상태를 설정할 수도 있습니다 . 그리고 우리는 여기에 몇 가지 차이점이 있습니다.

  • bashmksh(R41 때문에, 회귀 ^ Wchange 명백하게는 의도적으로 도입 ) 8 비트 (양 또는 음) 수를 잘라야한다. 그래서 예를 들어 return 1234설정합니다 $?210, return -- -1설정합니다 $?255.
  • zshpdksh(및 유도체 이외의 mksh) 임의의 비트 32 진수 정수 (-2 부호 수 31 2 행 (31) -1) (및 32 비트의 수를 절단).
  • ashyash0 내지 2 양의 정수를 허용 (31) -1, 그 중 임의의 수의 오류를 반환.
  • ksh93대한이 return 0하는 return 320설정 $?그대로,하지만 다른 용도로, 8 개 비트를 잘라. 이미 언급했듯이 256에서 320 사이의 숫자를 반환하면 ksh종료시 자체 종료 될 수 있습니다 .
  • rc그리고 es심지어는 목록 아무것도 반환 할 수 있습니다.

또한 일부 포탄도 특수 값을 사용할 수 있습니다 $?/ $status와 같은 프로세스의 종료 상태없는 몇 가지 오류 상태보고 127126에 대한 발견되지 명령 또는 하지 실행 (전래 파일이나 구문 오류가) ...


1
an exit code to their parent그리고 to get the *status* of their child. "상태"에 중점을 두었습니다. 인가 exit code*status*같은? 네, 그렇습니다. 두 이름의 기원은 무엇입니까? 동일하지 않은 경우, 상태의 정의 / 참조를 줄 수 있습니까?
n611x007

2
여기에 3 개의 숫자가 있습니다. 종료 코드 :에 전달 된 숫자 exit(). 종료 상태 : 의해 얻어진 수 waitpid()있는 출구 코드 신호들을 포함하고 있었다 여부 코어 덤프. 그리고 일부 쉘이 특수 변수 ( $?, $status) 중 하나에서 사용 가능한 숫자 는 정상적인 종료가있는 경우 종료 코드 를 포함 하지만 종료 정보가있는 경우 신호 정보를 전달 하는 방식으로 종료 상태 를 변환 하는 것입니다 프로세스가 종료되었습니다 ( 일반적으로 종료 상태 라고도 함 ). 그것은 모두 내 대답에 설명되어 있습니다.
Stéphane Chazelas

1
고마워요! 나는 분명히이 구별에 대한이 명백한 메모에 감사한다. 출구에 관한 이러한 표현은 그것을 만들 가치가있는 곳에서 상호 교환 가능하게 사용됩니다. 쉘 변수 변형에도 (일반) 이름이 있습니까? 따라서 쉘에 대해 자세히 설명 하기 전에 명시 적으로 정리하는 것이 좋습니다 . 첫 번째 또는 두 번째 단락 다음에 (설명에서) 설명을 삽입하는 것이 좋습니다.
n611x007

1
신호 인 첫 7 비트에 대한 POSIX 견적을 가리킬 수 있습니까? 내가 찾은 것은 > 128"신호를 수신하여 종료 된 명령의 종료 상태가 128보다 큰 것으로보고된다" 는 부분 이었습니다. pubs.opengroup.org/onlinepubs/9699919799/utilities/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

1
@ cuonglm, HTTP를 통해 다른 곳에서 공개적으로 사용할 수 있다고 생각하지 않지만 NNTP를 통해 gmane에서 얻을 수 있습니다. 메시지 ID efe764d811849b34eef24bfb14106f61@austingroupbugs.net(2015-05-06부터) 또는Xref: news.gmane.org gmane.comp.standards.posix.austin.general:10726
Stéphane Chazelas

23

프로세스가 종료되면 운영 체제에 정수 값을 리턴합니다. 대부분의 유닉스 변형 에서이 값은 모듈로 256으로 간주됩니다. 하위 프로세스의 상태는 16 비트 정수를 통해 상위 프로세스로 리턴됩니다.

  • 비트 0-6 (7 하위 비트)은 프로세스를 종료하는 데 사용 된 신호 번호이거나 프로세스가 정상적으로 종료 된 경우 0입니다.
  • 프로세스가 신호 및 덤프 된 코어에 의해 종료 된 경우 비트 7이 설정됩니다.
  • 비트 8–15는 프로세스가 정상적으로 종료 된 경우 프로세스의 종료 코드이고 프로세스가 신호에 의해 종료 된 경우 0입니다.

상태는 wait시스템 호출 또는 해당 형제 중 하나에 의해 반환됩니다 . POSIX는 종료 상태 및 신호 번호의 정확한 인코딩을 지정하지 않습니다. 그것은 단지 제공합니다

  • 출구 상태가 신호 또는 정상 출구에 해당 하는지를 알려주는 방법;
  • 프로세스가 정상적으로 종료 된 경우 종료 코드에 액세스하는 방법;
  • 프로세스가 신호에 의해 종료 된 경우 신호 번호에 액세스하는 방법.

엄밀히 말하면 신호에 의해 프로세스가 종료 될 때 종료 코드 가 없습니다 . 대신 종료 상태가 있습니다.

쉘 스크립트에서 명령종료 상태 는 특수 변수를 통해보고됩니다 $?. 이 변수는 종료 상태를 모호한 방식으로 인코딩합니다.

  • 프로세스가 정상적으로 종료되면 $?종료 상태입니다.
  • 프로세스가 신호에 의해 종료되면 $?대부분의 시스템에서 신호에 128을 더한 신호 수입니다. $?이 경우 POSIX 는 128보다 큰 명령 만 요구합니다 . ksh93은 128 대신 256을 추가합니다. 신호 번호에 상수를 추가하는 것 외에는 유닉스 변형을 본 적이 없습니다.

따라서 쉘 스크립트에서는 ksh93을 제외하고 명령이 신호에 의해 종료되었거나 128보다 큰 상태 코드로 종료되었는지 여부를 결정적으로 알 수 없습니다. 프로그래머가 $?모호함 으로 인해이를 피하기 때문에 상태 코드가 128보다 큰 상태에서 프로그램을 종료하는 것은 매우 드 rare니다 .

SIGINT는 대부분의 유닉스 변형에서 신호 2이므로 $?SIGINT에 의해 종료 된 프로세스의 경우 128 + 2 = 130입니다. SIGHUP의 경우 129, SIGKILL의 경우 137 등이 표시됩니다.


본질적으로 같은 말을하더라도 내 말보다 훨씬 더 나은 말로 표현합니다. $?Bourne과 같은 쉘 전용 임을 명확히 할 수 있습니다 . yash다른 (그러나 여전히 POSIX) 동작을 참조하십시오 . 또한 POSIX + XSI (Unix)에 따라 a kill -2 "$pid"는 프로세스에 SIGINT를 보내지 만 실제 신호 번호는 2가 아닐 수 있으므로 $? 128 + 2 (또는 256 + 2 또는 384 + 2) 일 필요는 없지만 kill -l "$?"return을 반환 INT하므로 숫자 자체를 참조하지 않는 이식성을 조언합니다.
Stéphane Chazelas

8

껍질에 따라 다릅니다. 로부터 bash(1)man 페이지, SHELL 문법 섹션, 간단한 명령은 서브 섹션 :

(A)의 반환 값 간단한 명령 이다 [...] 128+ N 이 명령 신호에 의해 종료되는 경우 , N .

이후 SIGINT시스템에서 신호 번호 2, 반환 값은이 배쉬에서 실행 130입니다.


1
세상에서 이걸 어떻게 찾거나 어디서 볼지 아십니까? 나는 당신의 천재 앞에 절합니다.
코리 클라인

1
@CoryKlein : 대부분 경험. 아, 그리고 signal(7)맨 페이지도 원할 것입니다.
Ignacio Vazquez-Abrams

좋은 것; 우연히 그 상수를 가진 파일을 C에 포함했는지 알고 있습니까? +1
Rui F Ribeiro

@CoryKlein 왜 이것을 정답으로 선택하지 않았습니까?
Rui F Ribeiro

3

SVr4가 1989 년에 waitid ()를 도입했다고 언급하는 것이 옳은 것 같습니다. 그러나 지금까지 중요한 프로그램은 사용하지 않는 것 같습니다. waitid ()는 exit () 코드에서 전체 32 비트를 검색 할 수 있습니다.

약 2 개월 전, Bourne Shell의 wait / job 컨트롤 부분을 다시 작성하여 waitpid () 대신 waitid ()를 사용했습니다. 종료 코드를 0xFF로 마스크하는 제한을 제거하기 위해 수행되었습니다.

waitid () 인터페이스는 1980 년부터 UNOS의 cwait () 호출을 제외하고 이전 wait () 구현보다 훨씬 깨끗합니다.

다음 페이지에서 매뉴얼 페이지를 읽으십시오.

http://schillix.sourceforge.net/man/man1/bosh.1.html

8 페이지에서 현재보고있는 "매개 변수 대체"섹션을 확인하십시오.

waitid () 인터페이스에 새로운 변수 .sh. *가 도입되었습니다. 이 인터페이스는 더 이상 $?로 알려진 숫자에 대해 모호한 의미를 갖지 않습니다. 인터페이스가 훨씬 쉬워졌습니다.

이 기능을 사용하려면 POSIX 호환 waitid ()가 있어야하므로 Mac OS X 및 Linux는 현재이 기능을 제공하지 않지만 waitid ()는 waitpid () 호출에서 에뮬레이트되므로 비 POSIX 플랫폼에서는 여전히 종료 코드에서 8 비트 만 가져옵니다.

간단히 말해 : .sh.status는 숫자 종료 코드이고 .sh.code는 숫자 종료 이유입니다.

더 나은 이식성을 위해 종료 이유의 텍스트 버전에 대한 .sh.codename (예 : "DUMPED"및 .sh.termsig, 프로세스를 종료 한 신호의 단일 이름).

더 나은 사용법을 위해 프로그램을 전혀 시작할 수 없을 때 사용되는 "NOEXEC"및 "NOTFOUND"라는 두 가지 종료 관련이없는 .sh.codename 값이 있습니다.

FreeBSD는 보고서 이후 20 시간 이내에 waitid () 커널 버그를 수정했지만 Linux는 아직 수정으로 시작하지 않았습니다. POSIX에이 기능을 도입한지 26 년 만에 모든 OS가 곧 지원할 수 있기를 바랍니다.


관련 답변은 unix.stackexchange.com/a/453432/5132 입니다.
JdeBP
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.