シグナルハンドラからのforkするのは安全か? (1) マルチスレッドの場合

マルチスレッドのプログラムが、(シグナルハンドラ以外から)forkするときに注意事項があることは既に書きましたが、「forkをシグナルハンドラの中から行いたい」というケースでは、さらに追加の注意事項があります。


一般に、シグナルハンドラでforkしてよいのは次の2つのどちらかの場合だけです。どちらも満たさないケース、例えば「フォークハンドラ*1関数内でprintfを呼んでいる」ケースではシグナルハンドラでのforkは安全ではありません。

  1. pthread_atforkでフォークハンドラが登録されていない
  2. pthread_atforkでフォークハンドラが登録されており、その関数が非同期シグナルセーフ(async-signal-safe)である。つまり、規格で非同期シグナルセーフとされている数の関数しか呼んでいない関数である。


pthread_atforkとはPOSIXで定められている関数で、マルチスレッドのプログラムがforkシステムコールを呼んだ時に、fork処理の直前で実行させる関数を登録するための関数です。詳しくは以前の記事を参照してください。


問題は、pthread_atfork関数で登録したフォークハンドラ関数は、forkがシグナルハンドラ内で行われたかどうかに関係なく常に呼び出されるということです。ですから、フォークハンドラが非同期シグナルアンセーフに作られていると、実質的にあなたのプログラムは、シグナルハンドラから非同期シグナルアンセーフな関数を呼んでいることになってしまいます。


この件は、規格(SUSv3)にも記載があります。ちょっと引用してみましょう。fork(2)の解説 http://www.opengroup.org/onlinepubs/009695399/functions/fork.html の、

When the application calls fork() from a signal handler and any of the fork handlers registered by pthread_atfork() calls a function that is not asynch-signal-safe, the behavior is undefined.

および

While the fork() function is async-signal-safe, there is no way for an implementation to determine whether the fork handlers established by pthread_atfork() are async-signal-safe. The fork handlers may attempt to execute portions of the implementation that are not async-signal-safe, such as those that are protected by mutexes, leading to a deadlock condition. It is therefore undefined for the fork handlers to execute functions that are not async-signal-safe when fork() is called from a signal handler.

の部分です。


特に注意しなければいけないのは、「pthread_mutex_lock/unlock関数は非同期シグナルセーフではない」ことです。一方、pthread_atforkで登録したフォークハンドラで行いたい処理は、一般的にはmutex操作だと思います。ですから、pthread_atforkを使っているマルチスレッドプログラムはシグナルハンドラからforkすることができないケースが多いと思っておいたほうが良いでしょう。


なお、マルチスレッドのプログラムでシグナルハンドラからforkする時は、たとえ上のpthread_atfork関連の制限をうまくすり抜けたとしても、子プロセスがfork直後にexecするように作成しなくてはなりません。その理由はすでに述べた通りです。これを守らないとデッドロックの危険がありますのでご注意ください。


続き

*1:後述するpthread_atforkで登録する関数全般を、sigactionで登録するシグナルハンドラと対比するために、こう呼んでおきます。