web-dev-qa-db-ja.com

POSIXスレッドとシグナル

POSIXスレッドとPOSIXシグナルの相互作用の複雑さを理解しようとしています。特に、私は興味があります:

  • 信号が配信されるスレッドを制御する最良の方法は何ですか(そもそも致命的ではないと仮定して)。
  • 信号が到着したことを別のスレッド(実際にはビジーである可能性があります)に伝える最良の方法は何ですか? (シグナルハンドラからpthread条件変数を使用するのは悪い考えであることは既に知っています。)
  • シグナルが発生したという情報を他のスレッドに渡すことを安全に処理するにはどうすればよいですか?これはシグナルハンドラで発生する必要がありますか? (私は一般に他のスレッドを殺したくありません。もっと微妙なアプローチが必要です。)

これが必要な理由については、スレッドをサポートするために TclX パッケージを変換する方法、または分割して少なくともいくつかの有用な部分がスレッドをサポートするようにする方法を調査しています。信号は、特に重要な部分の1つです。

76
Donal Fellows
  • 信号が配信されるスレッドを制御する最良の方法は何ですか?

@ zoli2kが示したように、単一のスレッドを明示的に指定して、処理するすべてのシグナル(または特定のシグナルを処理する一連のスレッド)を処理することは、優れた手法です。

  • 信号が到着したことを別のスレッド(実際にはビジーである可能性があります)に伝える最良の方法は何ですか?[...]
  • シグナルが発生したという情報を他のスレッドに渡すことを安全に処理するにはどうすればよいですか?これはシグナルハンドラで発生する必要がありますか?

「ベスト」とは言いませんが、私のおすすめは次のとおりです。

すべてのスレッドがそのシグナルマスクを継承するように、mainで必要なすべてのシグナルをブロックします。次に、特別なシグナル受信スレッドをシグナル駆動イベントループとして作成し、新しく到着したシグナルを他のスレッド内通信としてディスパッチします。

これを行う最も簡単な方法は、スレッドに sigwaitinfoまたはsigtimedwait を使用してループ内のシグナルを許可させることです。その後、スレッドは何らかの方法で信号を変換し、おそらくpthread_cond_tをブロードキャストし、他のスレッドをより多くのI/Oで起動し、アプリケーション固有のスレッドセーフキューにコマンドをキューに入れます。

あるいは、特別なスレッドを使用して、シグナルをシグナルハンドラに配信し、シグナルを処理する準備ができたときにのみ配信のマスクを解除することもできます。 (ただし、ハンドラーを介したシグナル配信は、sigwaitファミリーを介したシグナル受け入れよりもエラーが発生しやすい傾向があります。)この場合、レシーバーのシグナルハンドラーは、いくつかの単純で非同期シグナルセーフなアクションを実行します:sig_atomic_tフラグの設定、sigaddset(&signals_i_have_seen_recently, latest_sig)write()を呼び出して、非ブロッキング self-pipe などのバイトにします。その後、スレッドはマスクされたメインループに戻り、通信します上記のように他のスレッドへのシグナルの受信。

[〜#〜] updated [〜#〜]@cafは、sigwaitアプローチが優れていることを正しく指摘しています。)

46
pilcrow

POSIX規格によれば、すべてのスレッドはシステム上で同じPIDで表示され、pthread_sigmask()を使用して、すべてのスレッドのシグナルブロックマスクを定義できます。

PIDごとに定義できるシグナルハンドラは1つだけなので、1つのスレッドですべてのシグナルを処理し、実行中のスレッドをキャンセルする必要がある場合はpthread_cancel()を送信します。 pthread_kill()に対して推奨される方法です。スレッドのクリーンアップ関数を定義できるためです。

一部の古いシステムでは、適切なカーネルサポートがないため、実行中のスレッドのPIDが親スレッドのPIDと異なる場合があります。 Linux 2.4上のlinuxThreads を使用した信号処理については、FAQを参照してください。

13
zoli2k

私見、Unix Vシグナルとposixスレッドはうまく混ざりません。 Unix Vは1970です。POSIXは1980です;)

キャンセルポイントがあり、1つのアプリケーションでシグナルとpthreadを許可すると、最終的に各呼び出しの周りにループを作成することになり、驚くほどEINTRを返す可能性があります。

そのため、LinuxまたはQNXでマルチスレッドをプログラムしなければならない(ごく少数の)ケースで私がしたことは、すべての(ただし1つの)スレッドのすべてのシグナルをマスクすることでした。

Unix Vシグナルが到着すると、プロセスはスタックを切り替えます(プロセス内で取得できるのと同程度のUnix Vの並行性)。

ここの他の投稿が示唆しているように、どのposixスレッドがそのスタックスイッチングの犠牲になるかをシステムに伝えることが可能になるかもしれません。

シグナルハンドラスレッドを機能させることができたら、問題は残ります。シグナル情報を他のスレッドが使用できる文明的なものに変換する方法です。スレッド間通信のインフラストラクチャが必要です。有用なパターンの1つに、各スレッドがインプロセスメッセージングメカニズムのターゲットとなるアクターパターンがあります。

そのため、他のスレッドをキャンセルまたは殺す(または他の奇妙なもの)の代わりに、SignalコンテキストからSignalハンドラースレッドにSignalをマーシャルしてから、アクターパターン通信メカニズムを使用して意味的に有用なメッセージをそれらのアクターに送信する必要があります、信号関連情報が必要な人。

4
user2173833

これまでのところ:

  • シグナルにはさまざまな主要なクラスがあり、通常はプロセスを強制終了するものもあれば(SIGILL)、何も実行する必要がないものもあります(SIGIO; async IO rightとにかく正しい)これらの2つのクラスにはアクションは不要です。
  • 一部の信号はすぐに処理する必要はありません。 SIGWINCHのようなものは、便利になるまで(X11からのイベントのように)キューに入れることができます。
  • トリッキーなのは、スレッドを完全に消去することなく、実行中の操作を中断することで応答することです。特に、対話モードのSIGINTでは、応答性を維持する必要があります。

signalsigactionpselectsigwaitsigaltstack、およびその他のビットをソートする必要がありますPOSIX(および非POSIX)APIの一部。

3
Donal Fellows