C 语言信号处理
外观
C语言信号处理[编辑 | 编辑源代码]
介绍[编辑 | 编辑源代码]
信号(Signal)是操作系统用于通知进程发生某种事件的一种机制。在C语言中,信号处理允许程序捕获并响应这些事件,例如用户按下Ctrl+C(SIGINT)或进程尝试访问无效内存(SIGSEGV)。信号处理是系统编程的重要组成部分,尤其在多进程、守护进程或需要优雅退出的程序中。
信号分为两类:
- 标准信号:如SIGINT(中断)、SIGTERM(终止请求)、SIGKILL(强制终止)等。
- 实时信号:如SIGRTMIN到SIGRTMAX,支持排队和携带数据。
基本概念[编辑 | 编辑源代码]
信号的产生[编辑 | 编辑源代码]
信号可以由以下方式产生:
- 用户操作(如Ctrl+C发送SIGINT)
- 内核检测到异常(如SIGSEGV)
- 其他进程通过`kill()`系统调用发送
信号的处理方式[编辑 | 编辑源代码]
1. 默认行为:如SIGINT默认终止进程。 2. 忽略信号:如`signal(SIGINT, SIG_IGN)`。 3. 自定义处理函数:通过`signal()`或`sigaction()`注册处理函数。
信号处理函数[编辑 | 编辑源代码]
signal() 函数[编辑 | 编辑源代码]
`signal()`是传统的信号处理函数,但因其可移植性问题,推荐使用`sigaction()`。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handler(int sig) {
printf("Received signal %d\n", sig);
}
int main() {
signal(SIGINT, handler); // 捕获SIGINT(Ctrl+C)
while (1) {
printf("Running...\n");
sleep(1);
}
return 0;
}
输出示例:
Running... Running... ^CReceived signal 2 Running...
sigaction() 函数[编辑 | 编辑源代码]
`sigaction()`提供了更精细的控制,是更现代的替代方案。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handler(int sig) {
printf("Received signal %d\n", sig);
}
int main() {
struct sigaction sa;
sa.sa_handler = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
while (1) {
printf("Running...\n");
sleep(1);
}
return 0;
}
信号集与阻塞[编辑 | 编辑源代码]
信号可以通过`sigprocmask()`临时阻塞,防止在处理关键代码时被中断。
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigprocmask(SIG_BLOCK, &set, NULL); // 阻塞SIGINT
// 关键代码
sigprocmask(SIG_UNBLOCK, &set, NULL); // 解除阻塞
实际案例[编辑 | 编辑源代码]
守护进程的信号处理[编辑 | 编辑源代码]
守护进程通常需要处理SIGHUP(重新加载配置)和SIGTERM(优雅退出)。
void handle_sighup(int sig) {
reload_config();
}
void handle_sigterm(int sig) {
cleanup();
exit(0);
}
int main() {
signal(SIGHUP, handle_sighup);
signal(SIGTERM, handle_sigterm);
daemonize();
// 主循环
}
多线程信号处理[编辑 | 编辑源代码]
在多线程程序中,信号处理需特别注意:
- 使用`pthread_sigmask()`控制线程的信号掩码
- 通常由一个专用线程处理所有信号
高级主题[编辑 | 编辑源代码]
信号与竞态条件[编辑 | 编辑源代码]
信号处理函数可能中断主程序的执行,导致竞态条件。解决方法:
- 使用`sig_atomic_t`类型声明共享变量
- 在关键代码段阻塞信号
实时信号[编辑 | 编辑源代码]
实时信号(SIGRTMIN-SIGRTMAX)支持排队和携带数据:
union sigval value;
value.sival_int = 42;
sigqueue(pid, SIGRTMIN, value); // 发送带数据的信号
常见问题[编辑 | 编辑源代码]
问题 | 解决方案 |
---|---|
信号处理函数被中断 | 使用`sigaction()`并设置SA_RESTART标志 |
信号丢失 | 使用实时信号或信号队列 |
不可靠的信号行为 | 避免在信号处理函数中调用非异步安全函数 |
总结[编辑 | 编辑源代码]
信号处理是C语言系统编程的核心概念,用于:
- 处理用户中断
- 实现进程间通信
- 构建健壮的守护进程
正确使用信号需要理解其异步特性和潜在陷阱。对于新项目,推荐使用`sigaction()`而非`signal()`,并遵循异步安全编程原则。