pthread_sigmask(3THR) は、スレッドのシグナルマスクを設定するための関数です。つまり、sigprocmask(2) システムコールがプロセスに対して行うのと同じ操作をスレッドに対して行います。新しいスレッドが生成されると、その初期状態のシグナルマスクは生成元から継承されます。
マルチスレッドプロセス内で sigprocmask() を呼び出すのは、pthread_sigsetmask() を呼び出すのと同等です。詳細は、sigprocmask(2) のマニュアルページを参照してください。
pthread_kill(3THR) は、特定のスレッドにシグナルを送るための関数で、スレッド用の kill(2) と考えることができます。これは、プロセスにシグナルを送るものではありません。プロセスに送られたシグナルは、プロセス内のどのスレッドでも処理できます。pthread_kill() で送られたシグナルは、指定されたスレッドだけが処理できます。
pthread_kill() でシグナルを送ることができるのは、現在のプロセス内のスレッドに限られることに注意してください。スレッド識別子 (thread_t
型) の有効範囲が局所的であるため、現在のプロセス以外のプロセス内のスレッドを指定できないからです。
宛先のスレッドでシグナルの受信時に行われる処置 (ハンドラ、SIG_DFL、SIG_IGN) は通常どおり広域的です。この意味は、たとえば、あるスレッドに SIGXXX を送信する場合、そのプロセスにとっての SIGXXX シグナル処置がそのプロセスを終了させることであれば、宛先スレッドがこのシグナルを受け取ったとき、そのプロセス全体が終了するということです。
マルチスレッドプログラムでは、sigwait(2) が好まれるインタフェースです。これは、非同期的に生成されるシグナルを非常にうまく扱えるからです。
sigwait() は、set 引数に指定したシグナルが呼び出しスレッドに送られてくるまで、そのスレッドを待ち状態にします。スレッドが待っている間は、set 引数で指定したシグナルのマスクが解除され、復帰時に元のシグナルマスクが設定し直されます。
set 引数で識別されるすべてのシグナルは、呼び出しスレッドを含むすべてのスレッドでブロックする必要があります。そうしないと、sigwait() は正確に動作しません。
非同期シグナルからプロセス内のスレッドを隔離したい場合は、sigwait() を使用します。非同期シグナルを待つスレッドを 1 つ生成しておき、他のスレッドは、現在のプロセスに送られてくる可能性のある非同期シグナルをすべてブロックするように生成します。
Solaris オペレーティング環境 2.5 以降のリリースでは、Solaris オペレーティング環境 2.5 バージョンの新しい sigwait() と POSIX.1c バージョンの sigwait() の 2 種類を使用できます。新しいアプリケーションとライブラリでは、できるだけ POSIX 規格インタフェースを使用してください。Solaris オペレーティング環境バージョンは、将来のリリースではサポートされない可能性があるからです。
Solaris オペレーティング環境 2.5 バージョンの新しい sigwait() は、シグナルの無視という処置を無効にしません。以前の sigwait(2) の動作に依存しているアプリケーションは、ダミーのシグナルハンドラをインストールして、その処置を SIG_IGN からハンドラをもつように変更しない限りブレークする可能性があるため、このシグナルに対する sigwait() 呼び出しでこのシグナルは捕捉されます。
これら 2 つのバージョンの sigwait() の構文は下記のとおりです。
#include <signal.h> /* Solaris 2.5 バージョン */ int sigwait(sigset_t *set); /* POSIX.1c バージョン */ int sigwait(const sigset_t *set, int *sig); |
指定のシグナルが送られてくると、POSIX.1c sigwait() は保留されているそのシグナルを削除し、sig にそのシグナルの番号を入れます。同時に複数のスレッドから sigwait() を呼び出すこともできますが、受け取るシグナルごとに 1 つのスレッドだけの sigwait() だけが返ってきます。
sigwait() を使うと、非同期シグナルを同期的に扱うことができます。つまり、非同期シグナルを扱うスレッドから sigwait() だけを呼び出すと、シグナルが到着しだい戻ります。sigwait() の呼び出し側も含むすべてのスレッドで非同期シグナルをマスクすることによって、非同期シグナルを特定のシグナルハンドラだけに処理させることができます。非同期シグナルを安全に処理することが可能です。
すべてのスレッドですべてのシグナルを常にマスクし、必要なときだけ sigwait() を呼び出すようにすれば、アプリケーションはシグナルに依存するスレッドに対してはるかに安全になります。
通常、sigwait() を使うときは、シグナルを待つスレッドを 1 つまたは複数生成します。sigwait() はマスクされているシグナルであっても受け取るため、それ以外のスレッドでは誤ってシグナルを受け取ることがないように、対象となるシグナルをすべてブロックしてください。
シグナルが送られてくると、スレッドは sigwait() から復帰し、シグナルを処理し、さらに次のシグナルを待ちます。このシグナル処理スレッドでは、非同期保護関数以外の関数も使用でき、他のスレッドとも通常の方法で同期をとることができます (非同期保護カテゴリについては、「マルチスレッドインタフェースの安全レベル」を参照してください)。
同期シグナルに対しては、sigwait() を決して使わないでください。
sigtimedwait(2) は、指定時間が経過してもシグナルが送られてこなかったときにエラーで復帰する点を除いて、sigwait(2) と似ています。