Linuxにおけるシグナルセーフとは、シグナルハンドラ内で安全に使用できる関数の特性を指します。シグナルは、特定のイベントが発生した際にプロセスに通知を送るための仕組みであり、その性質上、シグナルハンドラはプログラムの任意の時点で実行される可能性があります。そのため、シグナルハンドラ内では、予期しない動作を防ぐために特定の制約が課されます。
一般的な関数の多くは、シグナルが発生する可能性のある状況で使用すると予測不可能な動作を引き起こすことがあります。たとえば、グローバル変数や静的データを操作する関数、内部でメモリ確保やロックを行う関数は、シグナルによる中断が発生するとデータ競合やデッドロックの原因となります。こうした問題を避けるため、Linuxでは「シグナルセーフ」と呼ばれる関数群が明確に定義されています。
シグナルセーフな関数とは、シグナルハンドラ内で実行されたとしても、不整合や予測不可能な動作を起こさないように設計された関数です。これらの関数は、非同期に実行される可能性がある状況でも問題なく動作するよう配慮されています。たとえば、標準出力への低レベル書き込みを行うwrite関数や、プロセスの終了を即座に行う_exit関数などがこれに該当します。一方で、mallocやprintfなどの関数は、内部で状態を保持するため、シグナルハンドラ内で使用するとスタック破壊やデータ競合を引き起こす可能性があるため避けるべきです。
例えばこんなコードはシグナルセーフではない。
#include <stdio.h> #include <signal.h> #include <unistd.h> void signal_handler(int signum) { // シグナルハンドラ内でprintfを使用(シグナルセーフではない) printf("Caught signal %d\n", signum); } int main() { signal(SIGINT, signal_handler); while (1) { printf("Running...\n"); sleep(1); } return 0; }
シグナルセーフな設計の重要性は、信頼性が求められるシステムプログラミングにおいて特に顕著です。たとえば、サーバープログラムやリアルタイムシステムでは、シグナル処理中の不整合がシステム全体の動作に重大な影響を及ぼす可能性があります。そのため、シグナルハンドラを実装する際には、シグナルセーフな関数のみを利用し、必要最小限の処理にとどめることが推奨されています。これにより、システムの安定性を確保しつつ、シグナルの非同期性によるリスクを最小限に抑えることが可能になります。
最近だとsshdが話題になっていたりしました。