15

私のマルチスレッド GUI アプリケーションには、次のシグナル処理コードがあります。このコードを改善して、正しくスレッドセーフになるようにしたいのですが、シグナル処理で完全に理解できないことがいくつかあります。

  • シグナルはプロセスまたはスレッド レベルで処理されますか (スレッド固有のシグナル ハンドラーを使用できますか) ?
  • どのスレッドコンテキストで signal_handler 関数が実行されますか?
  • 短時間に多くの SIGTERM シグナルを送信できますか?
  • signal_handler の並列実行を防ぐためにミューテックスを使用することは理にかなっていますか?

void signal_handler(int sig)
{
        switch (sig)
        {
        case SIGTERM:
            ::wxLogMessage(wxT("SIGTERM signal received ..."));
            break;
        case SIGINT:
            ::wxLogMessage(wxT("SIGINT signal received ..."));
            break;
        case SIGUSR1:
            ::wxLogMessage(wxT("SIGUSR1 signal received ..."));
            break;
        default:
            ::wxLogMessage(wxT("Unknown signal received ..."));
        }

        // send wxCloseEvent to main application window
        ::wxGetApp().GetTopWindow()->Close(true);
}

init 関数にシグナル ハンドラを登録します。

// register signal handlers
signal(SIGTERM, signal_handler);
signal(SIGINT,  signal_handler);
signal(SIGUSR1, signal_handler);
4

3 に答える 3

25
  • シグナル ハンドラーはプロセスごとの状態です。つまり、プロセス内のすべてのスレッドは、インストールされているシグナル ハンドラー関数の同じセットを共有します。
  • シグナル マスクはスレッドごとの状態です。シグナルは、スレッドごとにブロックまたはブロック解除できます。
  • シグナルは、プロセスまたはスレッド向けにすることができます。シグナルがプロセス指向の場合、現在ブロックされているシグナル タイプを持たない任意のスレッドが選択されて処理されます。

マルチスレッド アプリケーションでシグナルを処理する簡単な方法は、1 つのスレッドを専用のシグナル処理スレッドとして作成することです。対象となるすべてのシグナルは、すべてのスレッドでブロックされます。シグナルハンドラは確立されていません。シグナル処理スレッドはsigwaitinfo()ループ内で呼び出し、受信したシグナルに基づいて動作します。

これは、呼び出したい関数がasync-signal-safe であるかどうかを心配する必要がないことを意味します。シグナルはシグナル ハンドラーで処理されず、専用のシグナル処理スレッドによって同期的に処理されるためです。好きな関数を呼び出すことができます (たとえば、通常の pthreads 同期関数を使用して別のスレッドを起動できます)。

于 2012-10-18T11:02:03.830 に答える
15

非常に注意してください。signal (7)ページに示されているように、シグナルハンドラー内で(直接的または間接的に)呼び出すことができる関数はごくわずかです(「非同期シグナルセーフ」関数。詳細はsignal-safety(7)を参照)。 。ミューテックス関連の関数は、おそらくシグナルハンドラーで呼び出されるべきではありません。pthreads(7)も参照してください

シグナルハンドラーで揮発性のsigatomic_t変数を設定し、そのフラグの値を時々テストすることを検討してください。C ++ 11(またはC11)アトミック(C ++ 11 std :: atomicやC11など)がある場合は、その意味でその変数もアトミックに<stdatomic.h>することができます。volatile次に、アトミックロード機能を使用してテストします。

Qtのドキュメントでは、次のトリックを提案しています。起動時に自分自身へのパイプ(2)を作成し、シグナルハンドラーにwrite(2)writesyscallは非同期シグナルセーフとして指定されています)を1バイト(またはそれ以上)にします。 ]同じプロセスへのパイプに接続し、GUIイベントループpoll(2)にそのパイプの読み取り端を設定します。

Qtでシグナルを処理するLinux固有の方法は、おそらくQSocketNotifierでsignalfd(2)を使用することかもしれません(名前かかわらず、ソケットだけでなく、ポーリング可能なファイル記述子で機能します)。他のGUIツールキットを使用すると、ポーリングするファイル記述子(またはからのもの)を追加することもできます。signalfdpipe

于 2012-10-18T10:18:34.530 に答える
5

この回答は、POSIX スレッド ( pthreads) を参照しています。

参照 1:

はい、シグナルはスレッドレベルで処理できます。プロセスの複数のスレッドがシグナルを処理し、シグナルがプロセスに送信されるが、特定のスレッドに送信される場合、どのスレッドのハンドラーがシグナルを処理するかは決定されません。(詳細はman pthread_kill()を参照)

参照 2:

シグナルハンドラは、それを設定したスレッドのコンテキストで実行されます。これにはメインスレッドが含まれます。

参照 3:

同じタイプの複数のシグナルが同じプロセスに送信された場合、それらはシグナル キューを離れる前に 1 つのシグナルに凝縮される可能性があります。これがスレッドレベルで区別できるかどうかはわかりませんが、認めざるを得ません。

参照 4:

共有リソースがゲームに関係している場合: はい、少なくともハンドラーのコードの一部がそれらのリソースに同時にアクセスしている場合。さらに、これは実装しようとするロジックにも依存します。

于 2012-10-18T10:25:59.010 に答える