31

多くのスレッドを作成し、組み込みコンピューターの電源がシャットダウンされるか、ユーザーがkillまたはを使用ctrlcしてプロセスを終了するまで実行するプログラムがあります。

ここにいくつかのコードと main() がどのように見えるかを示します。

static int terminate = 0;  // does this need to be volatile?

static void sighandler(int signum) { terminate = 1; }

int main() {
  signal(SIGINT, sighandler);
  // ...
  // create objects, spawn threads + allocate dynamic memory
  // ...
  while (!terminate) sleep(2);
  // ...
  // clean up memory, close threads, etc.
  // ...
  signal(SIGINT, SIG_DFL);  // is this necessary?
}

私はいくつかのことを考えています:

  1. シグナル処理は必要ですか?
    このスレッド「Linux C catch kill signal for graceful terminate」を読みましたが、どうやら OS がクリーンアップを処理してくれるようです。したがって、シグナルハンドラーを無限ループに置き換えて、OS がスレッドを正常に終了したり、メモリの割り当てを解除したりすることはできますか?

  2. クリーン ターミネーションに関して考慮する必要がある他のシグナルはありますか? このスレッド「SIGINT は他の終了シグナルとどのように関連していますか?」、私が関係している可能性のあるすべてのシグナルをリストするのに役立ちましたが、実際に処理が必要なシグナルはいくつですか?

  3. 私の例の終了変数は揮発性でなければなりませんか? この変数が揮発性である多くの例と、そうでない他の例を見てきました。

  4. 私はそれsignal()が現在非推奨であり、使用することを読みましたsigaction(). signal()以前の呼び出しから変換する方法を示す本当に良い例はありますか? 作成/渡す必要がある新しい構造と、それらがどのように適合するかについて問題があります。

  5. 2 番目の呼び出しはsignal()必要ですか?
    私が心配する必要がある同様のものはありsigaction()ますか?

明確にするために、私が達成しようとしているのは、ctrlc電源が切断されるか、何か本当に悪いことが起こるまで、 my: main ループを実行することだけです。

4

5 に答える 5

28

[Q-3]私の例のterminate変数は でなければなりvolatileませんか? この変数が揮発性である多くの例と、そうでない他の例を見てきました。

フラグterminatevolatile sig_atomic_t次のとおりです。

ハンドラー関数は非同期で呼び出すことができるためです。つまり、ハンドラはプログラムの任意の時点で予期せず呼び出される可能性があります。非常に短い間隔で 2 つのシグナルが到着した場合、1 つのハンドラーが別のハンドラー内で実行できます。また、 を宣言することをvolatile sig_atomic_tお勧めします。この型は常にアトミックにアクセスされ、変数へのアクセスの中断に関する不確実性を回避します。volatile最適化しないようにコンパイラーに指示し、それをレジスターに入れます。(読み取り:詳細な有効期限のためのアトミック データ アクセスとシグナル処理)。
もう 1 つの参照: 24.4.7 Atomic Data Access and Signal Handling。さらに、7.14.1.1-5 の C11 標準はvolatile sig_atomic_t、シグナル ハンドラからは のオブジェクトのみにアクセスできることを示しています (他のオブジェクトへのアクセスは未定義の動作をします)。

[Q-4]signal()が非推奨になり、使用することを読みましたsigaction()signal()以前の呼び出しから変換する方法を示す本当に良い例はありますか? 作成/渡す必要がある新しい構造と、それらがどのように適合するかについて問題があります。

以下の例 (およびコメント内のリンク) が役立ちます。

// 1. Prepare struct 
struct sigaction sa;
sa.sa_handler =  sighandler;

// 2. To restart functions if interrupted by handler (as handlers called asynchronously)
sa.sa_flags = SA_RESTART; 

// 3. Set zero 
sigemptyset(&sa.sa_mask);

/* 3b. 
 // uncomment if you wants to block 
 // some signals while one is executing. 
sigaddset( &sa.sa_mask, SIGINT );
*/ 

// 4. Register signals 
sigaction( SIGINT, &sa, NULL );

参照:

  1. Beginning Linux Programming, 4th Edition : この本ではsigaction()、「Chapter 11: Processes and Signals」で正確にコードが説明されています。
  2. 例を含む sigaction ドキュメント(クイック ラーニング)。
  3. The GNU C Library: Signal Handling
    *1 から始めて、現在3 つの GNU-libraryを読んでいます

[Q-5]への2回目の呼び出しはsignal()必要ですか? 私が心配する必要がある同様のものはありsigaction()ますか?

プログラムの終了前に default-action に設定した理由は、私にはわかりません。次の段落が答えになると思います。

シグナルの処理

signal の呼び出しは、シグナルの1 回だけの発生に対するシグナル処理を確立します。シグナル処理関数が呼び出される前に、ライブラリはシグナルをリセットして、同じシグナルが再び発生した場合にデフォルトのアクションが実行されるようにします。シグナル処理をリセットすると、たとえば、シグナル ハンドラーで実行されたアクションが同じシグナルを再び発生させた場合に、無限ループを防ぐのに役立ちます。ハンドラーをシグナルが発生するたびに使用する場合は、ハンドラー内でシグナルを呼び出して元に戻す必要があります。シグナル処理を元に戻す際には注意が必要です。たとえば、継続的にSIGINT処理を再開すると、プログラムを中断して終了することができなくなる可能性があります。

このsignal()関数は、次に受信したシグナルのハンドラーのみを定義し、その後、デフォルトのハンドラーが復元されます。したがってsignal() 、プログラムがデフォルト以外のハンドラーを使用してシグナルの処理を続行する必要がある場合は、シグナル ハンドラーを呼び出す必要があります。

詳細については、ディスカッションを参照してください:シグナル ハンドラーを再度有効にする場合

【Q-1a】シグナルハンドリングは必要ですか?

はい、Linux がクリーンアップを行います。たとえば、ファイルまたはソケットを閉じない場合、Linux はプロセスの終了後にクリーンアップを行います。ただし、Linux はすぐにクリーンアップを実行する必要はなく、時間がかかる場合があります (システム パフォーマンスを高く維持するため、またはその他の問題が原因である可能性があります)。たとえば、tcp ソケットを閉じずにプログラムが終了した場合、カーネルはソケットをすぐに閉じず、すべてのデータが送信されたことを確認します。可能であれば、TCP が配信を保証します。

[Q-1b]したがって、シグナル ハンドラを無限ループだけに置き換えて、OS にスレッドを正常に終了させたり、メモリの割り当てを解除させたりすることはできますか?

いいえ、オペレーティング システムは、プログラムの終了後にのみクリーンアップを実行します。プロセスの実行中、そのプロセスに割り当てられたリソースは OS によって要求されません。(OS は、プロセスが無限ループにあるかどうかを認識できません。これは解決できない問題です)。プロセスの終了後に OS がクリーンアップ操作を実行するようにしたい場合は、シグナルを処理する必要はありません (プロセスがシグナルによって異常終了した場合でも)。

[Q]私が達成しようとしているのは、ctrlc電源が切断されるか、何か本当に悪いことが起こるまで、 my: メイン ループを実行することだけです。

いいえ、制限があります!すべての信号をキャッチすることはできません。一部のシグナルはキャッチできません。たとえばSIGKILL、and とSIGSTOPand の両方が終了シグナルです。1つ引用:

— マクロ: intSIGKILL

このSIGKILL信号は、プログラムを即時終了させるために使用されます。これは処理または無視できないため、常に致命的です。この信号をブロックすること もできません。

したがって、中断できないプログラム (中断されないプログラム) を作成することはできません

確かではありませんが、Windows システムでこのようなことができるかもしれません: TSR (ある種のカーネル モード フック) を書くことによって。論文の時から、一部のウイルスはタスク マネージャーからでも終了できないことを覚えていますが、それらは管理者権限によってユーザーを騙していると考えています。

この回答がお役に立てば幸いです。

于 2013-07-30T08:42:48.497 に答える
6

代わりに使用sigactionするには、次のような関数を使用できます。

/*
 * New implementation of signal(2), using sigaction(2).
 * Taken from the book ``Advanced Programming in the UNIX Environment''
 * (first edition) by W. Richard Stevens.
 */
sighandler_t my_signal(int signo, sighandler_t func)
{
    struct sigaction nact, oact;

    nact.sa_handler = func;
    nact.sa_flags = 0;
    # ifdef SA_INTERRUPT
    nact.sa_flags |= SA_INTERRUPT;
    # endif
    sigemptyset(&nact.sa_mask);

    if (sigaction(signo, &nact, &oact) < 0)
        return SIG_ERR;

    return oact.sa_handler;
}
于 2013-07-30T08:43:19.047 に答える
5

1. シグナル処理は必要ですか?

  • あなたが投稿したリンクの特定のケースでは、はい。ネットワークに関係するソフトウェアの実行には、その実行を妨げないようにするために、ソケットのクローズをクライアントに警告するなどの特定の操作が必要です。
  • 特定のケースでは、プロセスを正常にクリアするためにシグナルを処理する必要はありません。OSが処理します。

2. クリーン ターミネーションに関して、他に考慮する必要がある信号はありますか?

  • まず、彼のページを見てみましょう: The GNU Library Signals 終了シグナルはあなたが気にするものです。ただし、デバッグ目的を除いて、どのソフトウェアでもそれらを見つけることができなくても、SIGUSR1 と SIGUSR2 を見てください。

  • ソフトを突然終了させたくない場合は、この終了信号をすべて処理する必要があります。

3. この例の終了変数は揮発性でなければなりませんか?

  • 絶対違う。

4.私はそれsignal()が現在非推奨であり、使用することを読みましたsigaction()

  • Sigaction()signal は C 標準ですが、POSIX です。

  • Signal()私にとっては問題なく動作しますが、例が必要な場合: IBM Example

于 2013-07-30T08:53:36.217 に答える
0

まず第一に、シグナルを処理する必要があるかどうかがわからない場合は、おそらく処理しないでしょう。シグナルは、ソケットを閉じたり、終了する前に他の接続されたプロセスにメッセージを送信したり、write() からの SIGPIPE シグナルを処理したりするなど、特定のケースで処理する必要があります (おそらくそれ以上)。

第 2 に、これwhile (!terminate) sleep(2);はあまり良くありません。最悪の場合、ユーザー (またはシステム) が 2 秒間待機し、処理できない SIGKILL をプログラムに送信する必要があるため、イライラする可能性があります。

ここでの私見の最善の解決策は、 signalfd と select を使用することです。そのため、2秒待たずにプログラムを終了できます。

于 2013-08-04T19:54:33.230 に答える