web-dev-qa-db-ja.com

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

プロセスがSIGINTSIGTERMなどの処理可能なシグナルで強制終了されたが、シグナルを処理しない場合、プロセスの終了コードは何になりますか?

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
60
Cory Klein

プロセスは、整数引数を使用して_exit()システムコール(Linuxでは、exit_group()も参照)を呼び出して、親に終了コードを報告できます。これは整数ですが、親が使用できるのは最下位8ビットのみです(ただし、例外は 親のSIGCHLDでwaitid()またはハンドラーを使用してそのコードを取得する場合 Linuxではありません)。

親は通常、wait()またはwaitpid()を実行して、子のstatusを整数(ただし、セマンティクスが多少異なるwaitid()も使用できます)。

LinuxおよびほとんどのUnicesでは、プロセスが正常に終了した場合、そのstatus番号のビット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 Shellでは、_$?_は_128 + n_です。つまり、これらのシェルでは、_$?_の_129_を取得した場合、そのプロセスがexit(129)で終了したためか、それによってkillされたかがわかりません。信号_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
    _
  • 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ステータスが_$status_ではなく_$?_にあることを除いて、Bourne Shellと同じ(zshcshとの互換性のために_$status_も設定します(_$?_)に加えて)。
  • rc:終了ステータスも_$status_にありますが、シグナルによって強制終了された場合、その変数にはシグナルの名前が含まれます(sigtermまたは_sigill+core_ ifコアが生成された)数の代わりに、これはそのシェルの優れたデザインのもう1つの証拠です。
  • es。終了ステータスは変数ではありません。気になる場合は、次のようにコマンドを実行します。

    _status = <={cmd}
    _

    sigtermのように、数値またはrcまたは_sigsegv+core_を返します。

完全を期すために、最後のパイプラインのコンポーネントの終了ステータスを含むzshの_$pipestatus_およびbashの_$PIPESTATUS_配列について言及する必要があります。

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

  • bashおよびmksh(R41以降、 回帰^ Wchangeは明らかに意図的に導入された )は、数値(正または負)を8ビットに切り捨てます。たとえば、_return 1234_は_$?_を_210_に設定し、_return -- -1_は_$?_を255に設定します。
  • zshおよびpdksh(およびmksh以外の導関数)は、任意の符号付き32ビット10進整数(-231 2に31-1)(および数値を32ビットに切り捨てます)。
  • ashおよびyashは0〜2の任意の正の整数を許可します31-1で、その中の任意の数のエラーを返します。
  • _ksh93_ for _return 0_ to _return 320_は_$?_をそのまま設定しますが、それ以外の場合は8ビットに切り捨てます。すでに述べたように、256〜320の数値を返すと、終了時にkshが強制終了する可能性があることに注意してください。
  • rcおよびesを使用すると、リストも返すことができます。

また、一部のシェルは_$?_/_$status_の特別な値を使用して、_127_または_126_ forコマンドが見つかりませんまたは実行可能ではありません(またはソースファイルの構文エラー) ...

64

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

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

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

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

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

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

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

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

ほとんどのUNIXバリアントではSIGINTはシグナル2であるため、SIGINTによって強制終了されたプロセスの$?は128 + 2 = 130です。 SIGHUPには129、SIGKILLには137などが表示されます。

それはあなたのシェルに依存します。 bash(1)のマニュアルページから、Shell GRAMMARセクション、Simple Commandsサブセクション:

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

システムのSIGINTはシグナル番号2であるため、Bashで実行した場合の戻り値は130です。

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

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

Waitid()インターフェースは、1980年からのUNOSからのcwait()呼び出しを除いて、以前のwait()実装よりもはるかにクリーンです。

次の場所にあるmanページをご覧ください。

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

そして、現在8ページにある「パラメータ置換」のセクションをチェックしてください。

新しい変数.sh。*がwaitid()インターフェースに導入されました。このインターフェイスは、$で知られている数値に対して曖昧な意味を持たなくなりました。インターフェースがはるかに簡単になります。

この機能を使用するにはPOSIX準拠のwaitid()が必要なので、Mac OS XとLinuxは現在この機能を提供していませんが、waitid()はwaitpid()呼び出しでエミュレートされるため、非POSIXプラットフォームでは、終了コードから8ビットしか取得できません。

つまり、.sh.statusは数値の終了コードであり、.sh.codeは数値の終了理由です。

移植性を高めるために、次のものが用意されています。終了理由のテキストバージョン用の.sh.codename。 "DUMPED"と.sh.termsigは、プロセスを終了させたシグナルの信号名です。

使いやすくするために、exitに関連しない2つの.sh.codename値があります。「NOEXEC」と「NOTFOUND」は、プログラムをまったく起動できないときに使用されます。

FreeBSDは私の報告から20時間以内にwaitid()カーネルバグを修正しましたが、Linuxはまだ修正を開始していませんでした。この機能をPOSIXに導入してから26年後に、すべてのOSがすぐにそれをサポートすることを願っています。

3
schily