第3章: POSIX シグナル標準と sigaction
3.1 POSIX 標準化の歴史
3.1.1 UNIX の分裂と標準化への道
1980年代、UNIX は多くの派生版に分裂し、深刻な互換性問題を引き起こしていました:
【1980年代の UNIX 混乱期】
AT&T UNIX (System V)
├── AIX (IBM)
├── HP-UX (HP)
└── Solaris (Sun)
BSD UNIX (Berkeley)
├── SunOS (Sun)
├── FreeBSD
└── NetBSD
問題: 同じプログラムが異なる UNIX で動かない!
特にシグナル処理の動作が大きく異なっていた
例: signal() の動作
- System V: ハンドラ呼び出し後、デフォルトにリセット
- BSD: ハンドラを維持(リセットしない)
→ 移植可能なプログラムを書くことが困難
3.1.2 IEEE POSIX の誕生(1988年)
POSIX(Portable Operating System Interface)は、IEEE が策定した UNIX 標準です:
> "POSIX is a family of standards specified by the IEEE Computer Society for maintaining compatibility between operating systems." > — IEEE Std 1003.1
主要な POSIX 標準:
POSIX.1 (1988) - 基本OS インターフェース
├── ファイルシステム
├── プロセス管理
├── シグナル処理 ← sigaction() の標準化
└── 基本I/O
POSIX.1b (1993) - リアルタイム拡張
├── リアルタイムシグナル (SIGRTMIN-SIGRTMAX)
├── メッセージキュー
└── セマフォ
POSIX.1c (1995) - スレッド (Pthreads)
├── スレッド管理
└── スレッドとシグナルの相互作用
POSIX.1-2001/2008/2017 - 統合標準
└── Single UNIX Specification (SUS) との統合
3.1.3 signal() の問題点
古い signal() 関数には、POSIX で是正された多くの問題がありました:
/*
* signal() の問題点
*/
/* 問題1: 動作が実装依存 */
void handler(int sig)
{
/*
* System V: この関数の呼び出し後、ハンドラはリセットされる
* → 次のシグナルでプロセスが終了する可能性
*
* BSD: ハンドラは維持される
* → 期待通りの動作
*/
}
/* 問題2: 競合状態 */
void unreliable_handler(int sig)
{
/* System V で再登録が必要 */
signal(SIGUSR1, unreliable_handler);
/* ↑ この再登録の前に次のシグナルが来たら?
* → シグナルがロストまたはデフォルト動作で処理
*/
/* 実際の処理 */
}
/* 問題3: システムコールの中断 */
/*
* read() 実行中にシグナルを受信:
* - 一部の実装: read() は EINTR で失敗
* - 他の実装: read() は自動的に再開
*
* → 移植可能なエラー処理が困難
*/
3.2 sigaction() の設計思想
3.2.1 信頼性の高いシグナル処理
sigaction() は、signal() の問題を解決するために設計されました:
#include <signal.h>
int sigaction(int signum,
const struct sigaction *act,
struct sigaction *oldact);
設計原則:
1. 一貫性(Consistency)
- すべての POSIX システムで同じ動作を保証
- 実装依存の動作を排除
2. 原子性(Atomicity)
- ハンドラの登録と設定が原子的に行われる
- 競合状態を防止
3. 制御性(Controllability)
- フラグによる細かい動作制御
- シグナルマスクの明示的な管理
4. 拡張性(Extensibility)
- siginfo_t による追加情報の取得
- 将来の拡張に対応可能な構造
3.2.2 struct sigaction の構造
struct sigaction {
/* ハンドラ関数(2種類から選択) */
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
/* ハンドラ実行中にブロックするシグナル */
sigset_t sa_mask;
/* 動作フラグ */
int sa_flags;
/* 内部使用(設定不要) */
void (*sa_restorer)(void);
};
各フィールドの詳細:
┌─────────────────────────────────────────────────────────────┐
│ struct sigaction のフィールド │
├─────────────────────────────────────────────────────────────┤
│ │
│ sa_handler または sa_sigaction │
│ ├── sa_handler: void (*)(int) │
│ │ シグナル番号のみを受け取る簡易ハンドラ │
│ │ │
│ └── sa_sigaction: void (*)(int, siginfo_t *, void *) │
│ 詳細情報を受け取る拡張ハンドラ │
│ (SA_SIGINFO フラグが必要) │
│ │
│ sa_mask │
│ └── ハンドラ実行中に追加でブロックするシグナル │
│ (処理中のシグナルは自動的にブロック) │
│ │
│ sa_flags │
│ └── ハンドラの動作を制御するビットフラグ │
│ │
└─────────────────────────────────────────────────────────────┘
3.2.3 sa_flags の詳細
POSIX で定義されている主要なフラグ:
/*
* SA_SIGINFO
*
* sa_sigaction ハンドラを使用し、siginfo_t で
* 詳細情報を受け取る
*/
sa.sa_flags = SA_SIGINFO;
/* → sa_sigaction(int, siginfo_t *, void *) を使用 */
/*
* SA_RESTART
*
* シグナルによって中断されたシステムコールを
* 自動的に再開する
*/
sa.sa_flags = SA_RESTART;
/*
* 効果:
* read(), write(), etc. がシグナルで中断されても
* EINTR を返さずに自動再開
*/
/*
* SA_NODEFER (SA_NOMASK)
*
* ハンドラ実行中に同じシグナルをブロックしない
* 危険: 再帰的なハンドラ呼び出しの可能性
*/
sa.sa_flags = SA_NODEFER;
/* 通常は使用しない(デフォルトでブロックされる) */
/*
* SA_RESETHAND (SA_ONESHOT)
*
* ハンドラ呼び出し後、デフォルト動作に戻す
* (signal() の System V 動作を再現)
*/
sa.sa_flags = SA_RESETHAND;
/* 通常は使用しない */
/*
* SA_NOCLDSTOP
*
* 子プロセスが停止した時に SIGCHLD を受け取らない
* (終了時のみ通知)
*/
sa.sa_flags = SA_NOCLDSTOP;
/* SIGCHLD ハンドラでのみ意味がある */
3.3 siginfo_t 構造体の詳細
3.3.1 シグナルの詳細情報
SA_SIGINFO フラグを使用すると、シグナルに関する詳細情報を取得できます:
typedef struct {
int si_signo; /* シグナル番号 */
int si_errno; /* エラー番号(ある場合) */
int si_code; /* シグナルコード(詳細理由) */
pid_t si_pid; /* 送信元プロセスID */
uid_t si_uid; /* 送信元ユーザーID */
void *si_addr; /* フォールトアドレス(SIGSEGV等) */
int si_status; /* 終了コード/シグナル(SIGCHLD) */
long si_band; /* バンドイベント(SIGPOLL) */
union sigval si_value; /* シグナル付随データ */
} siginfo_t;
3.3.2 si_code の意味
si_code フィールドは、シグナルが発生した理由を示します:
/*
* 汎用コード(すべてのシグナルで使用可能)
*/
SI_USER /* kill() または raise() で送信 */
SI_KERNEL /* カーネルが送信 */
SI_QUEUE /* sigqueue() で送信 */
SI_TIMER /* タイマー期限切れ */
/*
* SIGILL (不正命令) の場合
*/
ILL_ILLOPC /* 不正なオペコード */
ILL_ILLTRP /* 不正なトラップ */
ILL_PRVOPC /* 特権命令 */
/*
* SIGSEGV (セグメンテーション違反) の場合
*/
SEGV_MAPERR /* マッピングされていないアドレス */
SEGV_ACCERR /* マッピングの権限違反 */
/*
* Minitalk での主要な使用例
*/
void handler(int sig, siginfo_t *info, void *ctx)
{
if (info->si_code == SI_USER)
{
/* kill() で送信されたシグナル */
pid_t sender = info->si_pid;
/* sender のPID を使用して応答可能 */
}
}
3.3.3 Minitalk での siginfo_t 活用
/*
* si_pid を使用してクライアントを識別
*/
void minitalk_handler(int sig, siginfo_t *info, void *ctx)
{
static pid_t current_client = 0;
(void)ctx; /* 未使用パラメータ */
/*
* info->si_pid で送信元を特定
* これにより:
* 1. クライアントの切り替えを検出
* 2. ACK をクライアントに返送
* 3. 複数クライアントの区別(ボーナス)
*/
if (info->si_pid != current_client)
{
/* 新しいクライアントからの通信開始 */
current_client = info->si_pid;
/* 状態をリセット... */
}
/* ビット処理... */
/* ACK を送信 */
kill(info->si_pid, SIGUSR1);
}
3.4 シグナルマスクとブロッキング
3.4.1 シグナルセット(sigset_t)
シグナルセットは、複数のシグナルをグループ化するためのデータ型です:
#include <signal.h>
sigset_t set;
/* 操作関数 */
int sigemptyset(sigset_t *set); /* 空に初期化 */
int sigfillset(sigset_t *set); /* 全シグナルを追加 */
int sigaddset(sigset_t *set, int sig); /* シグナルを追加 */
int sigdelset(sigset_t *set, int sig); /* シグナルを削除 */
int sigismember(const sigset_t *set, int sig); /* 含まれるか確認 */
ビットマスクとしての内部表現:
sigset_t の概念的な構造:
ビット: 31 30 29 ... 12 11 10 9 8 7 6 5 4 3 2 1 0
シグナル: ... USR2 SEGV USR1 KILL FPE EMT IOT TRAP ILL QUIT INT HUP --
例: SIGUSR1 と SIGUSR2 をセット
set = 0b...0101000000000...
↑ ↑
USR2 USR1
3.4.2 プロセスのシグナルマスク
各プロセスは「シグナルマスク」を持ち、どのシグナルがブロックされているかを管理します:
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
操作モード:
/*
* SIG_BLOCK: 指定シグナルをマスクに追加
*/
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
sigprocmask(SIG_BLOCK, &set, NULL);
/* → SIGUSR1 は配送されなくなる(保留される) */
/*
* SIG_UNBLOCK: 指定シグナルをマスクから削除
*/
sigprocmask(SIG_UNBLOCK, &set, NULL);
/* → SIGUSR1 が再び配送される */
/* → 保留されていた SIGUSR1 があればここで配送 */
/*
* SIG_SETMASK: マスクを指定された値に置き換え
*/
sigset_t newmask, oldmask;
sigemptyset(&newmask);
sigaddset(&newmask, SIGUSR1);
sigaddset(&newmask, SIGUSR2);
sigprocmask(SIG_SETMASK, &newmask, &oldmask);
/* → マスクが newmask に置き換わる */
/* → 古いマスクは oldmask に保存される */
3.4.3 クリティカルセクションの保護
シグナルマスクを使用してクリティカルセクションを保護:
/*
* Dijkstra のクリティカルセクション概念をシグナルに適用
*/
void critical_operation(void)
{
sigset_t block_set, old_set;
/* ブロックするシグナルを準備 */
sigemptyset(&block_set);
sigaddset(&block_set, SIGUSR1);
sigaddset(&block_set, SIGUSR2);
/* クリティカルセクション開始: シグナルをブロック */
sigprocmask(SIG_BLOCK, &block_set, &old_set);
/*
* ===== クリティカルセクション =====
* この間、SIGUSR1/SIGUSR2 は配送されない
* 共有データを安全に操作可能
*/
/* ... クリティカルな処理 ... */
/* クリティカルセクション終了: マスクを復元 */
sigprocmask(SIG_SETMASK, &old_set, NULL);
/* ブロック中に到着したシグナルはここで配送される */
}
3.4.4 sa_mask の役割
sigaction の sa_mask フィールドは、ハンドラ実行中に追加でブロックするシグナルを指定します:
void setup_handler(void)
{
struct sigaction sa;
sigemptyset(&sa.sa_mask);
/*
* ハンドラ実行中に追加でブロックするシグナル
*
* 注意: 処理中のシグナル自体は自動的にブロックされる
* (SA_NODEFER を指定しない限り)
*/
sigaddset(&sa.sa_mask, SIGUSR1);
sigaddset(&sa.sa_mask, SIGUSR2);
sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO | SA_RESTART;
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
}
/*
* この設定により:
*
* SIGUSR1 ハンドラ実行中:
* - SIGUSR1 がブロック(自動)
* - SIGUSR2 がブロック(sa_mask で指定)
*
* → ハンドラの処理が確実に完了してから
* 次のシグナルが処理される
*/
3.5 システムコールの中断と再開
3.5.1 EINTR 問題
シグナルはシステムコールを中断させる可能性があります:
/*
* シグナルによるシステムコール中断
*/
ssize_t bytes = read(fd, buf, size);
if (bytes == -1)
{
if (errno == EINTR)
{
/*
* シグナルにより中断された
* データは読み込まれていない
*
* 対処方法:
* 1. 再試行する
* 2. エラーとして処理する
*/
}
}
「遅い」システムコールの例:
中断される可能性があるシステムコール:
- read(), write() (端末、パイプ、ソケット)
- open() (FIFO)
- wait(), waitpid()
- pause(), sigsuspend()
- connect(), accept()
- select(), poll()
- msgsnd(), msgrcv()
- fcntl() の一部
中断されないシステムコール:
- ディスクI/O(通常のファイル)
- メモリ操作
- プロセス情報取得
3.5.2 SA_RESTART による自動再開
SA_RESTART フラグを設定すると、多くのシステムコールが自動的に再開されます:
/*
* SA_RESTART の効果
*/
void setup_restartable_handler(void)
{
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_handler = my_handler;
sa.sa_flags = SA_RESTART; /* 自動再開を有効化 */
sigaction(SIGUSR1, &sa, NULL);
}
/*
* SA_RESTART 有効時:
*
* プロセス:
* 1. read(fd, buf, size) を呼び出し
* 2. I/O 待ちでブロック
* 3. SIGUSR1 到着
* 4. ハンドラ実行
* 5. read() が自動的に再開 ← SA_RESTART の効果
* 6. データ受信後に戻る
*
* SA_RESTART なしの場合:
* 5. read() が -1 を返し、errno = EINTR
*/
3.5.3 手動での再試行
SA_RESTART が使用できない場合の対処:
/*
* EINTR を考慮した read() ラッパー
*/
ssize_t safe_read(int fd, void *buf, size_t count)
{
ssize_t result;
do {
result = read(fd, buf, count);
} while (result == -1 && errno == EINTR);
return result;
}
/*
* 部分読み取りも考慮した完全版
*/
ssize_t full_read(int fd, void *buf, size_t count)
{
size_t total = 0;
ssize_t n;
while (total < count)
{
n = read(fd, (char *)buf + total, count - total);
if (n == -1)
{
if (errno == EINTR)
continue; /* シグナル中断: 再試行 */
return -1; /* 他のエラー */
}
if (n == 0)
break; /* EOF */
total += n;
}
return total;
}
3.6 再入可能性と async-signal-safety
3.6.1 再入可能性の概念
「再入可能」(reentrant)関数は、実行途中で再度呼び出されても正しく動作する関数です:
/*
* 再入不可能な関数の例
*/
static int counter = 0;
int non_reentrant_increment(void)
{
return ++counter; /* 静的変数を変更 */
}
/*
* 問題のシナリオ:
*
* 1. main() が non_reentrant_increment() を呼び出し
* 2. counter を読み込み (counter = 5)
* 3. シグナル到着、ハンドラに制御移動
* 4. ハンドラが non_reentrant_increment() を呼び出し
* 5. counter++ (counter = 6)
* 6. ハンドラ終了、main() に戻る
* 7. main() が counter++ を完了 (counter = 7)
*
* 期待: 2回インクリメントで +2
* 実際: counter は元の値 +1 だけ増加(race condition)
*/
/*
* 再入可能な関数の例
*/
int reentrant_add(int a, int b)
{
return a + b; /* ローカル変数と引数のみ使用 */
}
3.6.2 async-signal-safe 関数
POSIX は「非同期シグナル安全」な関数を定義しています。シグナルハンドラ内ではこれらの関数のみを安全に使用できます:
/*
* async-signal-safe な関数(POSIX.1-2017)
*
* シグナルハンドラ内で安全に呼び出せる関数
*/
/* I/O */
read(), write(), open(), close()
dup(), dup2()
lseek()
/* プロセス制御 */
_exit(), _Exit()
fork(), execve()
getpid(), getppid()
wait(), waitpid()
/* シグナル */
signal(), sigaction()
sigprocmask(), sigsuspend()
kill(), raise()
/* ファイルシステム */
access(), chmod(), chown()
link(), unlink(), rename()
stat(), fstat(), lstat()
/* その他 */
alarm(), sleep(), pause()
time()
async-signal-unsafe な関数(シグナルハンドラ内で使用禁止):
/*
* 使用禁止の関数例
*/
printf(), fprintf() /* 内部バッファとロックを使用 */
malloc(), free() /* ヒープを操作 */
exit() /* atexit() ハンドラを呼び出す */
fopen(), fclose() /* stdio バッファリング */
fread(), fwrite()
pthread_* 関数 /* ロックを使用 */
/*
* なぜ危険か:
*
* main() で printf("Hello") を実行中
* ↓
* printf の内部ロックを獲得
* ↓
* シグナル到着
* ↓
* ハンドラで printf("Signal!") を呼び出す
* ↓
* printf がロックを獲得しようとする
* ↓
* デッドロック!(ロックは既に main() が保持)
*/
3.6.3 シグナルハンドラの安全な実装パターン
/*
* パターン1: フラグを設定するだけ
*
* 最も安全なパターン
*/
volatile sig_atomic_t g_signal_received = 0;
void safe_handler(int sig)
{
g_signal_received = 1; /* フラグを設定するだけ */
}
int main(void)
{
/* ... setup ... */
while (1)
{
if (g_signal_received)
{
g_signal_received = 0;
/* ここで安全に処理 */
printf("Signal processed\n"); /* main では安全 */
}
/* ... other work ... */
}
}
/*
* パターン2: write() のみ使用
*
* シグナルハンドラ内で出力が必要な場合
*/
void handler_with_output(int sig)
{
const char msg[] = "Signal received\n";
write(STDOUT_FILENO, msg, sizeof(msg) - 1); /* 安全 */
}
/*
* パターン3: Minitalk スタイル
*
* ビット処理と write() のみ
*/
void minitalk_safe_handler(int sig, siginfo_t *info, void *ctx)
{
static unsigned char ch = 0;
static int bits = 0;
(void)ctx;
/* ビット操作(ローカル/静的変数のみ) */
ch <<= 1;
if (sig == SIGUSR2)
ch |= 1;
bits++;
if (bits == 8)
{
write(1, &ch, 1); /* async-signal-safe */
kill(info->si_pid, SIGUSR1); /* async-signal-safe */
ch = 0;
bits = 0;
}
}
3.7 Minitalk サーバーの実装
3.7.1 堅牢なサーバー設計
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
/*
* グローバル状態
*
* 42の制約: グローバル変数は1つまで
* 構造体でまとめることで遵守
*/
typedef struct s_server
{
unsigned char current_char; /* 構築中の文字 */
int bit_count; /* 受信ビット数 */
pid_t client_pid; /* 現在のクライアント */
} t_server;
static t_server g_server = {0, 0, 0};
/*
* シグナルハンドラ
*
* async-signal-safe な関数のみ使用:
* - write()
* - kill()
*/
void signal_handler(int signum, siginfo_t *info, void *context)
{
(void)context;
/* クライアント切り替えの検出 */
if (g_server.client_pid != info->si_pid)
{
g_server.client_pid = info->si_pid;
g_server.current_char = 0;
g_server.bit_count = 0;
}
/* ビット処理 */
g_server.current_char <<= 1;
if (signum == SIGUSR2)
g_server.current_char |= 1;
g_server.bit_count++;
/* 8ビット完了 */
if (g_server.bit_count == 8)
{
if (g_server.current_char == '\0')
{
write(1, "\n", 1);
kill(g_server.client_pid, SIGUSR2); /* 完了通知 */
}
else
{
write(1, &g_server.current_char, 1);
kill(g_server.client_pid, SIGUSR1); /* ACK */
}
g_server.current_char = 0;
g_server.bit_count = 0;
}
}
/*
* シグナルハンドラの設定
*/
void setup_signals(void)
{
struct sigaction sa;
/* 構造体を初期化 */
sigemptyset(&sa.sa_mask);
/* ハンドラ実行中に両シグナルをブロック */
sigaddset(&sa.sa_mask, SIGUSR1);
sigaddset(&sa.sa_mask, SIGUSR2);
/* ハンドラと設定 */
sa.sa_sigaction = signal_handler;
sa.sa_flags = SA_SIGINFO | SA_RESTART;
/* エラーチェック付きで登録 */
if (sigaction(SIGUSR1, &sa, NULL) == -1)
{
write(2, "Error: sigaction failed\n", 24);
_exit(1); /* async-signal-safe な終了 */
}
if (sigaction(SIGUSR2, &sa, NULL) == -1)
{
write(2, "Error: sigaction failed\n", 24);
_exit(1);
}
}
/*
* PID 表示用ヘルパー
*/
void ft_putnbr(int n)
{
char c;
if (n >= 10)
ft_putnbr(n / 10);
c = (n % 10) + '0';
write(1, &c, 1);
}
/*
* メイン関数
*/
int main(void)
{
pid_t pid;
pid = getpid();
write(1, "Server PID: ", 12);
ft_putnbr(pid);
write(1, "\n", 1);
setup_signals();
write(1, "Waiting for messages...\n", 24);
/* 無限ループでシグナルを待つ */
while (1)
pause();
return (0);
}
3.7.2 エラー回復機能
/*
* タイムアウト検出付きハンドラ
*
* SIGALRM を使用してクライアントの応答タイムアウトを検出
*/
void setup_timeout_handler(void)
{
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_handler = timeout_handler;
sa.sa_flags = SA_RESTART;
sigaction(SIGALRM, &sa, NULL);
}
void timeout_handler(int sig)
{
(void)sig;
if (g_server.bit_count > 0)
{
/* 不完全なデータを破棄 */
const char msg[] = "\n[Timeout: incomplete data discarded]\n";
write(2, msg, sizeof(msg) - 1);
g_server.current_char = 0;
g_server.bit_count = 0;
g_server.client_pid = 0;
}
}
void signal_handler_with_timeout(int signum, siginfo_t *info, void *ctx)
{
(void)ctx;
/* アラームをリセット(次のシグナルまで5秒待つ) */
alarm(5);
/* 通常の処理... */
/* ... */
}
3.8 Minitalk クライアントの実装
3.8.1 ACK 付きクライアント
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
static volatile sig_atomic_t g_ack = 0;
void ack_handler(int sig)
{
(void)sig;
g_ack = 1;
}
/*
* 1ビット送信(ACK待機付き)
*/
int send_bit(pid_t server_pid, int bit)
{
int timeout;
g_ack = 0;
if (kill(server_pid, bit ? SIGUSR2 : SIGUSR1) == -1)
{
write(2, "Error: kill failed\n", 19);
return (0);
}
/* ACK を待つ */
timeout = 10000; /* 最大100ms */
while (!g_ack && timeout > 0)
{
usleep(10);
timeout--;
}
if (!g_ack)
{
write(2, "Error: ACK timeout\n", 19);
return (0);
}
return (1);
}
/*
* 1文字送信
*/
int send_char(pid_t server_pid, unsigned char c)
{
int bit;
bit = 7;
while (bit >= 0)
{
if (!send_bit(server_pid, (c >> bit) & 1))
return (0);
bit--;
}
return (1);
}
/*
* 文字列送信
*/
int send_string(pid_t server_pid, const char *str)
{
while (*str)
{
if (!send_char(server_pid, *str))
return (0);
str++;
}
/* 終端の NUL */
return send_char(server_pid, '\0');
}
/*
* 文字列を数値に変換
*/
pid_t ft_atoi(const char *str)
{
pid_t result = 0;
while (*str >= '0' && *str <= '9')
{
result = result * 10 + (*str - '0');
str++;
}
return result;
}
int main(int argc, char **argv)
{
pid_t server_pid;
struct sigaction sa;
if (argc != 3)
{
write(2, "Usage: ./client <server_pid> <message>\n", 39);
return (1);
}
server_pid = ft_atoi(argv[1]);
if (server_pid <= 0)
{
write(2, "Error: Invalid PID\n", 19);
return (1);
}
/* ACK ハンドラを設定 */
sigemptyset(&sa.sa_mask);
sa.sa_handler = ack_handler;
sa.sa_flags = SA_RESTART;
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
if (send_string(server_pid, argv[2]))
write(1, "Message sent!\n", 14);
else
write(2, "Error: Failed to send\n", 22);
return (0);
}
3.9 まとめ
この章では、POSIX シグナル標準と sigaction() について学びました:
学んだこと
- POSIX 標準化の歴史
- sigaction() の設計
- siginfo_t 構造体
- シグナルマスク
- SA_RESTART と EINTR
- async-signal-safety
次章の予告
第4章では、ビット操作と文字エンコーディング について学びます:
- ビット演算の詳細
- 文字の送受信アルゴリズム
- Unicode と UTF-8
---
参考文献:
- IEEE Std 1003.1-2017. POSIX.1-2017 (IEEE Standard for Information Technology). IEEE.
- Stevens, W. R., & Rago, S. A. (2013). Advanced Programming in the UNIX Environment (3rd ed.). Addison-Wesley.
- Kerrisk, M. (2010). The Linux Programming Interface. No Starch Press.
- Bach, M. J. (1986). The Design of the UNIX Operating System. Prentice Hall.