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