Linuxでさまざまなシグナルのシグナルハンドラーの数を登録したアプリケーションを作成しました。プロセスがシグナルを受信した後、制御は登録したシグナルハンドラーに移されます。このシグナルハンドラーでは、必要な作業を行ってから、デフォルトのシグナルハンドラー、つまりSIF_DFL
またはSIG_IGN
を呼び出します。ただし、SIG_DFL
とSIG_ING
はどちらもマクロであり、それぞれ数値0と1に展開されます。これらは、無効な関数アドレスです。
デフォルトのアクション、つまりSIG_DFL
またはSIG_IGN
を呼び出す方法はありますか?
SIG_DFL
またはSIG_ING
の効果を実現するには、exit(1)を呼び出し、それぞれ何もしません。しかし、SIGSEGV
のようなシグナルの場合、コアダンプも必要です。一般に、オペレーティングシステムと同じように、デフォルトの動作をSIG_DFL
と同じにし、同じSIG_IGN
の動作を無視したいと思います。
GNU Cライブラリリファレンスマニュアル には、信号処理に関するすべてを説明する章全体があります。
独自のハンドラーをインストールすると、常に以前に設定されたシグナルハンドラー(関数ポインター)を取得します(signal()
またはsigaction()
のマンページを参照)。
_previous_handler = signal(SIGINT, myhandler);
_
原則として、いつでも前のハンドラーにリセットして、シグナルを再びraise()
することができます。
_void myhandler(int sig) {
/* own stuff .. */
signal(sig, previous_handler);
raise(sig);
/* when it returns here .. set our signal handler again */
signal(sig, myhandler);
}
_
一般的な規則には1つの欠点があります。信号にマップされるハードウェア例外は通常、例外を引き起こした特定の命令に割り当てられます。したがって、信号を再度発生させると、関連する命令は元の命令と同じではありません。これは、他のシグナルハンドラーに害を及ぼす可能性がありますが、害を及ぼすべきではありません。
もう1つの欠点は、発生した信号ごとに多くの処理時間が発生することです。 raise()
の過度の使用を防ぐために、次の代替手段を使用できます。
_SIG_DFL
_の場合、関数ポインターはアドレス_0
_を指します(これは明らかに有効なアドレスではありません)。したがって、ハンドラーをリセットし、シグナルを再度raise()
する必要があります。
_if (previous_handler == SIG_DFL)
{
signal(sig, SIG_DFL);
raise(sig);
signal(sig, myhandler);
}
_
_SIG_IGN
_の値は_1
_(これも無効なアドレス)です。ここでは、戻ることができます(何もしません)。
_else if (previous_handler == SIG_IGN)
{
return;
}
_
それ以外の場合(_SIG_IGN
_でも_SIG_DFL
_でもない)、有効な関数ポインターを受け取り、ハンドラーを直接呼び出すことができます。
_else
{
previous_handler(sig);
}
_
もちろん、さまざまなAPIも考慮する必要があります(signal()
およびsigaction()
のマンページを参照してください)。
前のハンドラーを保存して、適切なタイミングで呼び出すことができます。
ハンドラーをインストールします。必ず古いハンドラーを保存してください
static struct sigaction new_sa, old_sa;
new_sa.sa_handler = my_handler;
sigemptyset(&new_handler.sa_mask);
if (sigaction(signo, &new_sa, &old_sa) == -1) {
/* handle sigaction error */
}
新しいハンドラーで、古いハンドラーを呼び出します
(*old_sa.sa_handler)(signo)
もう一度上げる必要も、面倒なことをする必要もありません。古いハンドラーを呼び出すだけです(もちろん、sigaction
を保存したので、古い処理にアクセスできます)。
通常のアプローチは、シグナルハンドラーをリセットしてから、シグナルを再びraise()
することです。
SIGINTハンドラーの例を次に示します。
void sigint_handler(int num)
{
/* handle SIGINT */
// call default handler
signal(SIGINT, SIG_DFL);
raise(SIGINT);
}
シグナルハンドラーがカーネルに実装されているとすると、私が見る唯一の方法は
raise()
再びシグナル