web-dev-qa-db-ja.com

「exit 130を実行することがSIGINTを終了することと同じではない」のはなぜですか?

https://unix.stackexchange.com/a/230568 でのStéphaneChazelasの返信から

理想的には、SIGINTで亡くなったことを親に報告したいです(たとえば、それが別のbashスクリプトである場合、そのbashスクリプトも中断されます)。 出口130を実行することは、SIGINTを終了することと同じではありません(ただし、シェルによっては、両方のケースで$?を同じ値に設定します)。 SIGINTによる死を報告する(SIGINTが2であるシステムで最も多い)。

ただし、bash、ksh93、またはFreeBSD shでは機能しません。その130の終了ステータスはSIGINTによる終了とは見なされず、親スクリプトはそこで中止しません。

  1. 「出口130をすることはSIGINTの死と同じではない」に関して、それらの違いは何ですか?
  2. 「bash、ksh93、またはFreeBSD shの場合、動作しません。130の終了ステータスはSIGINTによる終了とは見なされない」のはなぜですか?

バッシュのマニュアルから:

番号がNの致命的な信号でコマンドが終了すると、Bashは終了ステータスとして値128 + Nを使用します

SIGINTのシグナル番号は2であるため、SIGINTで終了するコマンドの終了ステータスは130です。したがって、130を終了することはSIGINTの終了と同じであり、130の終了ステータスは、 SIGINT。

ありがとう。

3
Tim

最後のコマンドがSIGINTで終了した後に$?に表示される130(128 + SIGINT)は、bashなどの一部のシェルによって作成された終了ステータスの簡略化された表現です。他のシェルは異なる表現を使用します(ksh93の256 + signum、yashの128 + 256 + signum、sigintまたはrc/essigquit+coreなどのテキスト表現)。詳細は プロセス終了時のデフォルトの終了コード? を参照してください。

プロセスは子プロセスを待機し、そのステータスを照会できます。

  • 停止した場合(どの信号で)
  • 再開された場合
  • 殺された場合(どのシグナルで)
  • トラップptrace dプロセスの場合)
  • coreをダンプした場合
  • _exit()システムコール(終了コード)で正常に終了した場合

これを行うには、wait()waitpid()waitid()(廃止されたwait3()wait4()も参照)またはSIGCHLDシステムコールのハンドラーのいずれかを使用します。 。

これらのシステムコールは、上記のすべての情報を返します。 (ただし、一部のシステムではwaitid()を除き、正常に終了する子の_exit()に渡される数値の最下位8ビットのみが使用可能です)。

しかし、bash(およびほとんどのBourne-likeおよびcsh-likeシェル)は、すべての情報を$?$?は、正常に終了するプロセスの終了コードの最低8ビットであり、128 + signumでそれが殺された)の8ビット数にバンドルします。または一時停止またはトラップされ、他のすべての情報は利用できません)。したがって、明らかに、失われている情報があります。特に、$?だけでは、プロセスが_exit(130)を実行したのか、SIGINTで終了したのかを判別できません。

bashは、プロセスが明らかに強制終了されていることを認識しています。たとえば、バックグラウンドプロセスが強制終了されると、次のように表示されます。

[1]+  Interrupt               sleep 20

しかし、$?では、SIGINTによって強制終了されたのか、それとも_exit(130)を呼び出したのかを知るのに十分な情報が得られません。

ほとんどのシェルはその変換を行うので、アプリケーションは_exit(number_greater_than_127)を何よりも行うことよりもよく知っていますが、シグナルによる死を報告します。

それでもプロセスが_exit(130)を実行する場合、そのプロセスを待機しているプロセスは、そのプロセスが正常に終了したことを検出します。シグナルによって強制終了されたのではありません。 Cでは、WIFEXITED()trueを返し、WIFSIGNALED()はfalseを返します。

bash自体は、プロセスがSIGINTで終了したと見なしません(SIGINTで終了した場合と同じ値を含む$?を介してプロセスが終了したと考えても)。

したがって、それはbashが行うSIGINTの特別な処理をトリガーしません。スクリプトでは、bashとスクリプトで現在実行中のコマンドの両方が^CでSIGINTを受け取ります(どちらも同じプロセスグループにあるため)。

bashは、SIGINTを受け取ったときに、それが待機しているコマンドもSIGINTで停止した場合にのみ終了します(たとえば、スクリプト内でvi以下を実行し、^ Cを使用してvi /にならないものを中止するとします。 lessが死ぬと、スクリプトはvi/lessを終了して戻ったときに死ぬことはありません)。

そのコマンドbashがSIGINTのハンドラーで_exit(130)を待機している場合、bashはそのSIGINTで終了しません(子が中断されたとは考えられないため、中断されたとは見なされません)。

そのため、death by SIGINTを報告する場合、そのハンドラでシグナルを受信したときに実際に追加の処理を行っていても、実際に中断されています_exit(130)を実行すべきではありませんが、実際にはSIGINTを使用して自分自身を強制終了します(SIGINTのデフォルトハンドラーを復元した後)。シェルでは、それは:

trap '
  extra processing
  trap - INT # restore SIGINT handler
  kill -s INT "$$" # report to the parent that we have indeed been
                   # interrupted
  ' INT
7