Linux アプリケーション開発とシグナル処理

Linux アプリケーション開発とシグナル処理

現在、アプリケーションが終了することについてユーザーから苦情が寄せられており、問題を抱えています。一部の (一見恣意的な) 条件とデスクトップ環境では、アプリケーションが終了せず、再起動時に設定が保存されません。関連する IRC チャネルで質問したところ、ほとんどの場合、シグナルを正しく処理するように言われます。ターミナルで Ctrl-C の場合は SIGINT、"通常の" 終了の場合は SIGTERM は知っています。しかし、SIGHUP も重要だと教えられました。そこで、私の質問は次のとおりです。

適切に動作するアプリケーションを構築するには、どのような信号を処理する必要がありますか?

答え1

https://en.wikipedia.org/wiki/Unix_signal

標準シグナルとそのデフォルトのアクションのリストがあります。十分な権限を持つプロセスはいつでも任意のシグナルを送信できますが、プロセス (またはプロセス経由のあなた) はこれを実行すべきではありません。

あなたはkill、あなたのプロセスkill学期また、ターゲットが送信したい特別なシグナルを処理することがわかっていない限り、環境 (シェル、ターミナル ドライバー) によって生成される可能性のある一連のシグナルも含まれます。

基本的な信号をアクションごとに並べ替えることができます。

コアダンプ信号

ABRT TRAP SYS BUS FPE ILL SEGV   XFSZ XCPU QUIT

は、特殊な関数を使用してユーザーによって生成されるか (TRAP、ABRT、SYS)、またはハード エラー状態 (BUS、FPE、ILL、SEGV) になることによって生成されます。 は、 QUITコア ダンプ () を希望する端末のユーザーによって生成されますC+\

これらをデフォルトの設定のままにしておくとよいでしょう。

の外へ終了信号:

HUP INT PIPE TERM ALRM POLL PROF USR1 USR2

期待すべき

HUP INT PIPE TERM

さまざまな状況における環境から:

HUP -- when the terminal hungs up or you become a stopped orphaned process
INT -- when the user at the terminal interrupts you with C-c
PIPE -- when a PIPE or socket you write into closes, e.g. by
        exiting before you finished writing to it 
       (`yourprogram | (exit)` will give you a `PIPE` 
        if yourprogram attempts to write to its STDOUT) 
TERM -- when a process ends you a normal termination request

残りの終了信号は、自分で設定した場合にのみ受信されるはずです。

殺すそして停止あなたには何もできません。

生成された端末を傍受したい場合があります停止信号:

TTIN -- you read from the terminal and you're a backgrounded process
TTOU -- you write the terminal when you're a backgrounded process
        and the terminal is set to put you to sleep when you do
TSTP -- you're being put to sleep with `C-Z`

しかし、最悪の場合、状態が破壊されることなく、プロセスが停止するだけです。

CHLD、、CONTまたは を処理する必要があることがわかっていない限りURG、処理する必要はありません。

要約:

基本的に、通常、終了前のクリーンアップを実行する場合は、HUP、INT、PIPE、および TERM を処理 (または無視) する必要があると思います。プログラムがそれらのシグナルを使用しない限り、またはすべての状況でクリーンアップが絶対に必要な場合を除き、残りはそのままにしておくことができます。

後者の場合、処理されないシグナルをすべて空白でブロックできますが、シグナル ブロッキング マスクはフォークや execve 呼び出し間で継承され、ILL などのシグナルがプロセスの実行から発生し、kill または sigqueue によって送信されていない場合にシグナルを無視またはブロックすると、未定義の動作が発生することに注意してください。

さらに詳しく知りたい場合は、マニュアルページと標準を調べてください。シグナルは Unix では非常に大きなトピックであり、その処理は非常に難しい場合があります。

答え2

そのようにすると、シグナルの 1 つがプログラムを終了させるというだけの理由で、かなりの数のシグナルを無視することになります。コメントで siblynx が示唆しているように、最善の策はこれらのシグナルを分析して、その発生源を理解することです。結局のところ、プログラムが強制終了されるのには、それなりの理由があるかもしれません。

そうは言っても、signals(7)プロセスの終了を引き起こす可能性のあるシグナルのリストを取得するには:

SIGHUP、SIGINT、SIGQUIT、SIGILL、SIGABRT、SIGFPE、SIGKILL、SIGSEGV、SIGPIPE、SIGALRM、SIGTERM、SIGUSR1、SIGUSR2。

アプリケーションがターミナルに接続されている時間が長い場合は、処理するSIGINTことをお勧めします ( Ctrl+ はC結局かなり有名です)。SIGTERMも、基本的な終了シグナルのままであり、カーネルによって使用されるため (たとえば、システムのシャットダウン時)、お勧めです。 は、SIGQUITコア ダンプを発生させるため、おそらく前の 2 つの「厳しい」バージョンです。 このシグナルを受信すると、プログラムはすぐにクラッシュすることが予想されます。 他のシグナルを受信すると、プログラムは「混乱を解消して終了する」(設定を保存し、適切に終了する) ことが予想されます。

SIGILL、、SIGFPEおよびSIGSEGVSIGPIPE、プログラムの不正動作に関連する信号です。セグメンテーション違反おそらく最も頻繁に発生する状況ですが、結局のところ、これらすべてのケースはバグとして扱うことができ、シグナルをキャッチすることがこれらの状況から回復するための最善の方法であるとは考えていません。

  • ほとんどの場合、コンパイラはSIGILLそれを簡単には許可しません(詳細はこちら) ですので、その件は脇に置いておいてもいいと思います。
  • ゼロで割らないようにすれば、ほとんどのSIGFPE問題を回避できます。それ以外は、プログラムが数学に優れ、数字を扱い過ぎないことを確認してください。
  • SIGSEGVいくつかの原因 (不正なメモリ アクセス、不正な参照解除) が考えられますが、その場合は、 を使用してgdbプログラムの実行を監視し、gdbのスタックを使用して問題のある命令をトレースバックすることをお勧めします。
  • 最後に、SIGPIPEは主にデータ ストリーム (ファイルの読み取り、接続など) に関連しています。プログラム I/O ストリームのいずれかが実行時に終了する可能性がある場合には、プログラムがそれらを使用する前にその記述子をチェックし、そこで発生するすべての I/O イベントを処理するようにする必要があります (たとえば、selectまたはを使用する場合)。もここで役立つ可能性があります。pollgdb

SIGABRT、、SIGALRMは基本的にユーザーシグナルです。これらを受信して​​いる場合は、実際にそれを実行した可能性がありますSIGUSR1SIGUSR2

SIGHUPこれにより、と が残りますがSIGKILL、これらはキャッチされない可能性があります。

  • SIGHUPたとえば、ターミナルからプロセスを開始し、その後プロセスをデタッチせずにターミナルを閉じると ( disown、または を使用nohup)、このエラーが発生します。プログラムがターミナルから開始され、デーモンとして実行される場合は、ダブルフォークなどによってシェルからデタッチすることを忘れないでください。プログラムが GUI から開始できる場合、またはブート時に制御ターミナルが「閉じられる」可能性が低い場合は、この特定のシグナルはあまり関係ありません。

  • SIGKILLご存知のとおり、シグナルの万能者です。これはカーネルが「くそったれ!」と言っているようなものです。これを受け取った場合は、プログラムでそれをどう処理するかではなく、シグナルを発しているものに注意を集中する必要があります。

結局のところ、シグナルを分析してデバッグ ツールを使用するのが、おそらくこれらの問題に対する最も簡単なアプローチです。プログラムが誤動作したときにカーネルによってシグナルがトリガーされた場合は、gdb問題を特定するのに役立ちます。その場合は、シグナルを忘れて、シグナルが伝えているバグに集中してください。

信号が「外部」から来ている場合は、発信元を見つけることがおそらく最善の策です。(本当に見つからない場合は、トレースkill(2)システムコールは最後の手段としては良いかもしれない). その後、それを無視するか ( SIG_IGN)、考慮に入れるかを決めることができます。 発信プログラムを無音にすることも、適切な代替手段です...

関連情報