プロセスが終了したときのデフォルトの終了コード?


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、 ...)] スクリプト内で、出口コアが何であるかをテストします。
オリビエデュラック


半質問についてのコメント:myScriptをバックグラウンドに置かないでください。(省略&)。別のシェルプロセス(別の端末)から信号を送信すると$?、myScriptの終了後に使用できます。
-MattBianco

回答:


61

プロセスは、整数引数を使用して_exit()システム呼び出し(Linuxの場合も参照exit_group())を呼び出して、終了コードを親に報告できます。整数ですが、親は最下位8ビットのみを使用できます(Linuxでなくても、親の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信号番号はどこですか、

  • 灰で、zshの、pdkshのは、bash、Bourneシェルは、$?あります128 + n。つまり、これらのシェルでは、$?of を取得した場合129、プロセスが終了したためであるのexit(129)か、シグナルによって終了したのか1HUPほとんどのシステムで)わかりません。しかし、理論的には、シェルは、終了するときに、デフォルトで最後に終了したコマンドの終了ステータスを返すということです。必ず$?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
    
  • yashyash妥協を提供します。を返します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

(移植性のために、シグナル番号は使用せず、シグナル名のみを使用してください)

非ボーンフロントでは:

  • csh/ tcsh及びfishステータスであることを除き、Bourneシェルと同様$statusの代わりに$?(ことに注意してくださいzshもセット$statusとの互換性のためcshに加えて($?))。
  • rc:終了ステータス$statusも同様ですが、シグナルによって強制終了された場合、その変数には数値の代わりにシグナルの名前(コアが生成された場合sigtermまたはsigill+coreコアが生成された場合)が含まれます。 。
  • es。終了ステータスは変数ではありません。気になる場合は、次のようにコマンドを実行します。

    status = <={cmd}
    

    その数を返すか、だろうsigtermsigsegv+coreのようにrc

たぶん完全を期すために、我々は言及すべきzshさん$pipestatusbash$PIPESTATUS最後のパイプラインの構成部品の終了ステータスが含まれている配列を。

また、完全を期すために、シェル関数とソースファイルについては、デフォルトでは関数は最後のコマンド実行の終了ステータスで戻りますが、return組み込み関数で明示的に戻りステータスを設定することもできます。ここにいくつかの違いがあります。

  • bashそしてmksh(R41ので、回帰^ Wchangeが明らかに意図的に導入は)8ビットに(正または負)の数を切り捨てます。だから、例えばreturn 1234設定されます$?210return -- -1設定します$?255。
  • zsh及びpdksh(以外の誘導体mksh)は、任意の符号付き32ビットの10進数を可能にする(-2 31 2 31 -1)(と32ビットに番号を切り捨て)。
  • ashyash0〜2 31 -1の正の整数を許可し、その中の任意の数のエラーを返します。
  • ksh93そのままreturn 0return 320設定$?しますが、それ以外の場合は8ビットに切り捨てます。すでに述べたように、256から320の間の数値を返すと、ksh終了時に自分自身を殺す可能性があることに注意してください。
  • rcそしてes何でもリストを返すことができます。

また、いくつかのシェルもの特殊な値を使用することに注意してください$?/ $status、のようなプロセスの終了ステータスではありませんいくつかのエラー状態を報告し127たり126するためにコマンドが見つからないか、実行可能ではない(ソースファイルまたは構文エラー)...


1
an exit code to their parentおよびto get the *status* of their child。「ステータス」に重点を置いています。であるexit code*status*同じ?はい、2つの名前を持つことの起源は何ですか?ケースが同じではない場合、ステータスの定義/参照を提供できますか?
n611x007 14年

2
ここには3つの数字があります。終了コード:に渡された番号exit()終了ステータス:た数waitpid()コアがあったかどうか、終了コードを含む、信号の数とがダンプ。そして、いくつかのシェルが特別な変数($?$status)の1つで利用可能にする数。これは、正常終了した場合の終了コードを含むように終了ステータスを変換しますが、プロセスが強制終了されました(一般に終了ステータスとも呼ばれます)。それはすべて私の答えで説明されています。
ステファンシャゼル14年

1
ありがとうございます!ここで明確に区別されている点に感謝します。出口に関するこれらの式は、いくつかの場所で交換可能に使用されるため、作成する価値があります。シェル変数バリアントには(一般的な)名前もありますか?そのため、シェルの詳細入るに、明示的クリアすることをお勧めします。最初の段落または2番目の段落の後に(コメントから)説明を挿入することをお勧めします。
n611x007 14年

1
最初の7ビットがシグナルであるというPOSIXの引用を指摘できますか?私が見つけることができたのは、> 128「信号を受信したために終了したコマンドの終了ステータスは、128を超えると報告される」という部分だけでした。pubs.opengroup.org/onlinepubs/9699919799/utilities/...
チロSantilli新疆改造中心法轮功六四事件

1
@ cuonglm、HTTPを介して他の場所で公開されているとは思いませんが、NNTPを介してgmaneから入手することができます。メッセージIDを探しますefe764d811849b34eef24bfb14106f61@austingroupbugs.net(2015年5月6日から)またはXref: news.gmane.org gmane.comp.standards.posix.austin.general:10726
ステファンChazelas

23

プロセスが終了すると、オペレーティングシステムに整数値を返します。ほとんどのUNIXバリアントでは、この値は256を法として取得されます。下位ビット以外はすべて無視されます。子プロセスのステータスは、16ビット整数を介して親に返されます。

  • ビット0〜6(下位7ビット)は、プロセスを強制終了するために使用されたシグナル番号、またはプロセスが正常に終了した場合は0です。
  • プロセスがシグナルによって強制終了され、コアがダンプされた場合、ビット7が設定されます。
  • ビット8〜15は、プロセスが正常に終了した場合のプロセスの終了コード、またはプロセスがシグナルによって強制終了された場合は0です。

ステータスは、waitシステムコールまたはその兄弟のいずれかによって返されます。POSIXは、終了ステータスとシグナル番号の正確なエンコーディングを指定しません。提供するだけ

  • 終了ステータスがシグナルまたは通常の終了に対応するかどうかを判断する方法。
  • プロセスが正常に終了した場合、終了コードにアクセスする方法。
  • プロセスがシグナルによって強制終了された場合、シグナル番号にアクセスする方法。

厳密に言えば、プロセスがシグナルによって強制終了された場合、終了コードはありません。代わりに終了ステータスがあります

シェルスクリプトでは、コマンドの終了ステータスは特別な変数を介して報告されます$?。この変数は、終了ステータスをあいまいな方法でエンコードします。

  • プロセスが正常に終了した場合、$?その終了ステータスです。
  • プロセスがシグナルによって強制終了された場合、$?ほとんどのシステムで128にシグナル番号を加えたものになります。$?この場合、POSIX は128を超える権限のみを要求します。ksh93は、128の代わりに256を追加します。シグナル番号に定数を追加する以外のことを行ったUNIXバリアントは見たことがありません。

したがって、シェルスクリプトでは、コマンドがシグナルによって強制終了されたのか、ksh93を除き、128を超えるステータスコードで終了したのかを最終的に判断することはできません。プログラムが128を超えるステータスコードで終了することは非常にまれです。これは、$?あいまいさのためにプログラマが回避するためです。

SIGINTは、ほとんどのUNIXバリアントでシグナル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 "$?"返すINTので、移植性のために数字自体を参照しないことをお勧めします。
ステファンシャゼル

8

それはシェルに依存します。以下からのbash(1)manページ、シェルの文法のセクション、単純なコマンドのサブセクション:

単純なコマンドの戻り値は、コマンドがシグナルnで終了する場合、[...] 128+ nです。

以来SIGINT、システム上の信号の数2であることがバッシュの下で実行されている場合、戻り値は130です。


1
世界ではどのようにこれを見つけますか、さらにはどこを見るべきか知っていますか?あなたの天才の前でお辞儀をします。
コリークライン

1
@CoryKlein:主に経験。ああ、おそらくsignal(7)manページも必要になるでしょう。
イグナシオバスケス-アブラムス

クール; 私は偶然にそれらの定数を持つCのファイルをインクルードしているかどうか知っていますか?+1
ルイFリベイロ

@CoryKleinなぜこれを正解として選択しないのですか?
ルイFリベイロ

3

SVr4が1989年にwaitid()を導入したことを言及するのに適切な場所のようですが、今のところ重要なプログラムはそれを使用していないようです。waitid()を使用すると、exit()コードから完全な32ビットを取得できます。

約2か月前、私はBourneシェルの待機/ジョブ制御部分を書き直して、waitpid()の代わりにwaitid()を使用しました。これは、終了コードを0xFFでマスクする制限を取り除くために行われました。

waitid()インターフェースは、1980年からのUNOSからのcwait()呼び出しを除き、以前のwait()実装よりもずっときれいです。

以下のmanページをご覧ください。

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」という2つの非出口関連の.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.