シグナルの基本概念
シグナルはLinuxにおけるプロセス間通信の一種であり、非同期イベント通知を実現します。プロセスはシグナルを受信することで、特定の動作を実行したり状態を変更したりします。
シグナルの種類と特性
kill -lコマンドで確認できるシグナルは62種類あり、1〜31番が通常シグナル、32〜64番がリアルタイムシグナルです。通常シグナルは到着の有無のみを記録しますが、リアルタイムシグナルはキューイングが可能です。
# シグナル一覧表示
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
...
シグナルの動作変更
シグナルハンドラはsignal()関数で登録可能ですが、SIGKILL(9)とSIGSTOP(19)は変更不可です。
#include <signal.h>
#include <iostream>
void custom_handler(int sig_num) {
std::cout << "受信シグナル: " << sig_num << std::endl;
}
int main() {
signal(SIGINT, custom_handler);
while(1) pause();
return 0;
}
シグナル発生の方式
キーボード入力
Ctrl+CはSIGINT(2)、Ctrl+\はSIGQUIT(3)を送信します。これらのシグナルは端末ドライバが検出し、フォアグラウンドプロセスに配送されます。
システムコール
kill(), raise(), abort()などの関数で明示的にシグナルを送信できます。
#include <sys/types.h>
#include <signal.h>
// プロセス終了デモ
int main() {
pid_t current_pid = getpid();
kill(current_pid, SIGTERM);
return 0;
}
カスタムkillコマンド実装
#include <cstdlib>
#include <iostream>
int main(int argc, char* argv[]) {
if(argc != 3) {
std::cerr << "使用方法: " << argv[0] << " [シグナル番号] [PID]" << std::endl;
return 1;
}
int sig_num = std::atoi(argv[1]);
int target_pid = std::atoi(argv[2]);
kill(target_pid, sig_num);
return 0;
}
ハードウェア例外
ゼロ除算や不正メモリアクセスなどのハードウェア例外は、CPUのステータスレジスタを設定し、OSが対応するシグナル(SIGFPE, SIGSEGVなど)を送信します。
#include <iostream>
int main() {
int val = 10;
int result = val / 0; // SIGFPE発生
return 0;
}
コアダンプ機能
特定のシグナル受信時にはコアダンプファイルが生成されます。デバッグ時に有用です。
# コアダンプ有効化
$ ulimit -c unlimited
# デバッグ方法
$ gdb ./program core.pid
(gdb) bt
コアダンプ関連シグナル
- SIGQUIT (3) - コアダンプ生成後終了
- SIGABRT (6) - 異常終了
- SIGFPE (8) - 算術例外
- SIGSEGV (11) - メモリ違反