在 Unix/Linux 系统编程中,管理信号处理行为涉及以下核心概念和模型,它们共同构成了信号处理的框架:
1. 信号(Signal)模型
- 软件中断:信号是异步事件通知机制,类比硬件中断
- 预定义类型 :
SIGINT
(Ctrl+C)、SIGTERM
(终止请求)、SIGSEGV
(段错误) 等标准信号 - 生命周期 :
- 生成(Generation):事件触发信号产生
- 递送(Delivery):内核将信号传递给目标进程
- 处理(Handling):进程执行注册的处理动作
2. 信号处理行为控制
行为类型 | 说明 | 设置方式 |
---|---|---|
默认行为 | 系统预定义行为(终止/忽略/暂停) | SIG_DFL |
忽略信号 | 丢弃信号不做任何响应 | SIG_IGN |
自定义处理函数 | 用户注册的信号处理例程 | 函数指针 |
3. 关键控制机制
(1) 信号阻塞(Blocking)
-
目的:临时阻止信号递送
-
实现 :
csigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigprocmask(SIG_BLOCK, &mask, NULL); // 阻塞SIGINT
-
特点 :
- 被阻塞的信号处于**挂起(Pending)**状态
sigpending()
可获取挂起信号集- 解除阻塞后立即递送
(2) 信号屏蔽(Masking)
- 执行处理函数时自动生效 :
- 当前处理的信号自动被屏蔽(除非设置
SA_NODEFER
) - 通过
sa_mask
指定额外屏蔽的信号集
- 当前处理的信号自动被屏蔽(除非设置
- 作用:防止信号处理函数被重入
4. 信号处理模型
(1) 单次处理模型
c
struct sigaction sa = {
.sa_handler = handler,
.sa_flags = SA_RESETHAND // 处理一次后恢复默认行为
};
- 特点 :类似传统
signal()
的行为 - 风险:信号再次发生时可能触发默认行为(如终止进程)
(2) 持久处理模型
c
struct sigaction sa = {
.sa_handler = handler,
.sa_flags = 0 // 持续有效
};
- 特点:处理函数保持激活状态
- 最佳实践:配合信号阻塞使用
(3) 实时信号处理模型
c
struct sigaction sa = {
.sa_sigaction = rt_handler,
.sa_flags = SA_SIGINFO | SA_RESTART
};
-
特点 :
- 支持信号排队(避免丢失)
- 可携带附加信息(发送者PID、错误地址等)
- 使用
sigqueue()
发送:
cunion sigval value = {.sival_int = 42}; sigqueue(pid, SIGRTMIN+1, value);
5. 关键系统调用
系统调用 | 用途 |
---|---|
sigaction() |
注册信号处理行为(核心接口) |
sigprocmask() |
控制进程信号屏蔽集 |
sigsuspend() |
原子操作:设置屏蔽集 + 等待信号 |
kill() /raise() |
发送信号(跨进程/自身) |
sigaltstack() |
设置备选信号栈(处理栈溢出信号) |
6. 特殊处理场景
(1) 系统调用中断处理
-
问题 :慢速系统调用(如
read()
)被信号中断 -
解决方案 :
cstruct sigaction sa = { .sa_handler = handler, .sa_flags = SA_RESTART // 自动重启被中断的系统调用 };
(2) 信号竞争处理
-
临界区保护模式 :
csigset_t new_mask, old_mask; sigemptyset(&new_mask); sigaddset(&new_mask, SIGINT); // 进入临界区前阻塞信号 sigprocmask(SIG_BLOCK, &new_mask, &old_mask); /* 临界区代码(不会被SIGINT中断) */ // 等待可能发生的信号 sigsuspend(&old_mask); // 恢复原始屏蔽集 sigprocmask(SIG_SETMASK, &old_mask, NULL);
(3) 子进程终止处理
c
void sigchld_handler(int sig) {
while (waitpid(-1, NULL, WNOHANG) > 0); // 非阻塞回收所有僵尸进程
}
// 注册处理
struct sigaction sa = {
.sa_handler = sigchld_handler,
.sa_flags = SA_RESTART | SA_NOCLDSTOP
};
sigaction(SIGCHLD, &sa, NULL);
7. 安全编程模型
-
异步信号安全(Async-signal-safe):
- 信号处理函数中只能调用 异步安全函数(如
write()
,_exit()
) - 禁止调用 非可重入函数(
malloc
,printf
等)
- 信号处理函数中只能调用 异步安全函数(如
-
自包含状态:
cvolatile sig_atomic_t flag = 0; // 信号安全标志 void handler(int sig) { flag = 1; // 仅设置标志,主循环中处理 }
-
备选信号栈:
cstack_t ss = { .ss_sp = malloc(SIGSTKSZ), .ss_size = SIGSTKSZ }; sigaltstack(&ss, NULL); // 设置备选栈 struct sigaction sa = { .sa_handler = handler, .sa_flags = SA_ONSTACK // 使用备选栈 };
概念关系图
+---------------------+
| 信号产生源 | (硬件/内核/进程)
+----------+----------+
| 生成信号
v
+---------------------+
| 内核信号队列 | (实时信号排队)
+----------+----------+
| 递送决策
v
+---------------------+ 阻塞? +----------+
| 进程信号屏蔽集 +--------->| 挂起状态 |
+----------+----------+ +----------+
| 未阻塞?
v
+---------------------+
| 信号处理分发 |
| +----------------+ |
| | 默认行为处理 | |
| | 忽略信号 | |
| | 自定义处理函数 | |
| +----------------+ |
+---------------------+
|
v
+---------------------+
| 处理函数执行环境 |
| - 自动信号屏蔽 |
| - 备选信号栈 |
| - 中断系统调用 |
+---------------------+
这些核心概念共同构成了 Unix/Linux 信号处理的完整模型,开发者需要理解其交互机制才能编写出健壮可靠的信号处理代码。