pthread_atfork -
fork(2)
の際に呼び出されるハンドラを登録する
int pthread_atfork(void (*prepare)(void), void
(*parent)(void), void (*child)(void));
pthread_atfork は
fork(2)
によって新しいプロセスが生成される際、その直前と直後に呼び出される
ハンドラ関数を登録する。
prepare
ハンドラは、新しいプロセスが生成される直前に親プロセスから
呼び出される。
parent
ハンドラは、
fork(2)
がリターンする直前に親プロセスから呼び出される。
child ハンドラは
fork(2)
が返る直前に子プロセスから呼び出される。
prepare,
parent および
child
の三つのハンドラのうちの一つまたは複数に
NULL
を与えることができるが、これは対応する時点でいかなるハンドラをも
呼び出す必要がないことを意味する。
pthread_atfork
は複数のハンドラの組合せを登録するために複数回
呼び出すことが可能である。
fork(2) の時点で複数の
prepare ハンドラは LIFO
順で呼び出される(
pthread_atfork
で最後に加えられたものが
fork
の前に最初に呼び出される)。
他方、
parent と
child は FIFO
順で呼び出される
(最初に加えられたものが最初に呼び出される)。
pthread_atfork
の目的を理解するために、
fork(2)
は、現在ロック状態にある
mutex
も含めて、呼び出したスレッドのみの
メモリ空間全体を複製することを思い出そう。つまり、他のスレッドは
子プロセスでは実行されていないのである。従って、
fork
を呼び出したスレッド以外のスレッドによって
mutex がロックされている
のならば、その mutex
は子プロセスの中で永遠にロックされたままであり、
子プロセスの実行をブロックする可能性がある。
これを避けるためには、
pthread_atfork
で次のようなハンドラを登録すれば良いだろう:
prepare
ハンドラが大域的な mutex
を(ロックする際の順序で)ロックし、
parent と
child
がそれらを(逆の順に)アンロックする。
または、
prepare と
parent
を
NULL に設定し、
child
を大域的な mutex
に対して
pthread_mutex_init
を呼び出す関数に設定しても良いだろう。
pthread_atfork は成功すれば 0
を返し、エラーがあれば非ゼロのエラーコードを返す。
- ENOMEM
- ハンドラを登録するのにメモリが足りない。
Xavier Leroy <
[email protected]>
fork(2),
pthread_mutex_lock(3),
pthread_mutex_unlock(3).
[訳注] glibc-linuxthreads
の最新のドキュメントは
Texinfo形式で提供されている。
以下は glibc-linuxthreads-2.3.1 の Texinfo
ファイルからの引用である。
pthread_atfork
の目的を理解するために、
fork
が現在ロック状態にある
mutex
も含めたメモリ空間全体を、
しかし呼び出しスレッドだけを複製することを思い出してほしい。
つまり、他のスレッドは子プロセスでは実行されない。
mutex は
fork
の後は使うことができず、子プロセスで
pthread_mutex_init
を使って初期化されなければならない。
これは現在の実装の制限で、将来のバージョンでも存在するかもしれないし、
存在しないかもしれない。
これを避けるためには、
pthread_atfork
で次のようなハンドラを登録すればよい:
prepare ハンドラで mutex を
(ロックする際の順序で)
ロックし、
parent
ハンドラで mutex
をロック解除する。
child ハンドラでは
pthread_mutex_init を使用して mutex
を初期化しなければならない。
条件変数などの他の同期オブジェクトについても同様である。
グローバル mutex を fork
の前にロックすると、
他のスレッドはすべて、それらのグローバル
mutex で保護される
コードのクリティカル領域から締め出される。したがって
fork
が親プロセスのアドレス空間のスナップショットを取ると、
そのスナップショットは有効で安定したデータをコピーする。
子プロセスで同期オブジェクトを初期化することで
親プロセスのスレッドサブシステムに由来するものが適切に清められることが保証される。
例えば、 mutex
は獲得を待つスレッドの待ちキューを引き継ぐが、
この待ちキューは子プロセスでは意味を持たない。
mutex
を初期化することでこのことに対処する。