Appearance
C 标准库 - <signal.h>
概述
<signal.h> 头文件提供了信号处理机制,允许程序响应异步事件,如中断、异常和系统事件。信号是一种进程间通信和异常处理的重要机制。
信号类型
标准信号
c
#define SIGINT 2
#define SIGILL 4
#define SIGFPE 8
#define SIGSEGV 11
#define SIGTERM 15
#define SIGABRT 6SIGINT
中断信号(Ctrl+C)。
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void sigint_handler(int sig) {
printf("\n收到 SIGINT 信号\n");
}
int main() {
signal(SIGINT, sigint_handler);
printf("按 Ctrl+C 发送 SIGINT 信号\n");
while (1) {
sleep(1);
}
return 0;
}SIGILL
非法指令信号。
c
#include <stdio.h>
#include <signal.h>
void sigill_handler(int sig) {
printf("收到 SIGILL 信号\n");
exit(1);
}
int main() {
signal(SIGILL, sigill_handler);
printf("程序运行中...\n");
return 0;
}SIGFPE
浮点异常信号(除零错误等)。
c
#include <stdio.h>
#include <signal.h>
void sigfpe_handler(int sig) {
printf("收到 SIGFPE 信号(浮点异常)\n");
exit(1);
}
int main() {
signal(SIGFPE, sigfpe_handler);
int a = 10;
int b = 0;
int c = a / b;
printf("这行不会执行\n");
return 0;
}SIGSEGV
段错误信号(非法内存访问)。
c
#include <stdio.h>
#include <signal.h>
void sigsegv_handler(int sig) {
printf("收到 SIGSEGV 信号(段错误)\n");
exit(1);
}
int main() {
signal(SIGSEGV, sigsegv_handler);
int* ptr = NULL;
*ptr = 10;
printf("这行不会执行\n");
return 0;
}SIGTERM
终止信号。
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void sigterm_handler(int sig) {
printf("收到 SIGTERM 信号\n");
exit(0);
}
int main() {
signal(SIGTERM, sigterm_handler);
printf("程序运行中...\n");
printf("使用 'kill %d' 发送 SIGTERM 信号\n", getpid());
while (1) {
sleep(1);
}
return 0;
}SIGABRT
中止信号(由 abort() 函数触发)。
c
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void sigabrt_handler(int sig) {
printf("收到 SIGABRT 信号\n");
}
int main() {
signal(SIGABRT, sigabrt_handler);
printf("调用 abort()\n");
abort();
printf("这行不会执行\n");
return 0;
}信号处理函数
signal()
c
void (*signal(int sig, void (*func)(int)))(int);设置信号处理函数。
c
#include <stdio.h>
#include <signal.h>
void handler(int sig) {
printf("收到信号: %d\n", sig);
}
int main() {
signal(SIGINT, handler);
signal(SIGTERM, handler);
printf("信号处理函数已设置\n");
return 0;
}raise()
c
int raise(int sig);向当前进程发送信号。
c
#include <stdio.h>
#include <signal.h>
void handler(int sig) {
printf("收到信号: %d\n", sig);
}
int main() {
signal(SIGINT, handler);
printf("发送 SIGINT 信号\n");
raise(SIGINT);
return 0;
}信号处理函数类型
SIG_DFL
默认信号处理。
c
#include <stdio.h>
#include <signal.h>
int main() {
signal(SIGINT, SIG_DFL);
printf("使用默认处理\n");
return 0;
}SIG_IGN
忽略信号。
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main() {
signal(SIGINT, SIG_IGN);
printf("忽略 SIGINT 信号\n");
printf("按 Ctrl+C 不会终止程序\n");
while (1) {
sleep(1);
}
return 0;
}实际应用示例
1. 优雅退出
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
volatile sig_atomic_t keep_running = 1;
void signal_handler(int sig) {
printf("\n收到信号 %d,准备退出...\n", sig);
keep_running = 0;
}
void cleanup() {
printf("清理资源...\n");
}
int main() {
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
printf("程序启动,PID: %d\n", getpid());
printf("按 Ctrl+C 或发送 SIGTERM 信号退出\n");
int counter = 0;
while (keep_running) {
printf("运行中... %d\n", counter++);
sleep(1);
}
cleanup();
printf("程序正常退出\n");
return 0;
}2. 定时器信号
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>
void timer_handler(int sig) {
static int count = 0;
printf("定时器触发: %d\n", ++count);
}
void set_timer(int seconds) {
struct itimerval timer;
timer.it_value.tv_sec = seconds;
timer.it_value.tv_usec = 0;
timer.it_interval.tv_sec = seconds;
timer.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &timer, NULL);
}
int main() {
signal(SIGALRM, timer_handler);
printf("设置 2 秒定时器\n");
set_timer(2);
while (1) {
pause();
}
return 0;
}3. 信号屏蔽
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handler(int sig) {
printf("收到信号: %d\n", sig);
}
int main() {
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
signal(SIGINT, handler);
printf("屏蔽 SIGINT 信号 5 秒\n");
sigprocmask(SIG_BLOCK, &mask, NULL);
for (int i = 0; i < 5; i++) {
printf("等待中... %d\n", i + 1);
sleep(1);
}
printf("解除屏蔽\n");
sigprocmask(SIG_UNBLOCK, &mask, NULL);
printf("按 Ctrl+C 测试\n");
pause();
return 0;
}4. 多信号处理
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
void sigint_handler(int sig) {
printf("\n收到 SIGINT (Ctrl+C)\n");
}
void sigterm_handler(int sig) {
printf("收到 SIGTERM\n");
exit(0);
}
void sigusr1_handler(int sig) {
printf("收到 SIGUSR1\n");
}
void sigusr2_handler(int sig) {
printf("收到 SIGUSR2\n");
}
int main() {
signal(SIGINT, sigint_handler);
signal(SIGTERM, sigterm_handler);
signal(SIGUSR1, sigusr1_handler);
signal(SIGUSR2, sigusr2_handler);
printf("程序启动,PID: %d\n", getpid());
printf("测试信号:\n");
printf(" Ctrl+C - SIGINT\n");
printf(" kill %d - SIGTERM\n", getpid());
printf(" kill -USR1 %d - SIGUSR1\n", getpid());
printf(" kill -USR2 %d - SIGUSR2\n", getpid());
while (1) {
pause();
}
return 0;
}5. 信号与数据
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
typedef struct {
int signal_count;
int last_signal;
} SignalData;
SignalData signal_data = {0, 0};
void signal_handler(int sig) {
signal_data.signal_count++;
signal_data.last_signal = sig;
printf("收到信号: %d (总计: %d)\n",
sig, signal_data.signal_count);
}
void print_signal_data() {
printf("信号数据:\n");
printf(" 信号计数: %d\n", signal_data.signal_count);
printf(" 最后信号: %d\n", signal_data.last_signal);
}
int main() {
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGUSR1, signal_handler);
printf("程序启动,PID: %d\n", getpid());
while (1) {
pause();
print_signal_data();
}
return 0;
}6. 自定义信号处理
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
typedef void (*SignalHandler)(int);
SignalHandler old_handlers[32];
void custom_signal_handler(int sig) {
printf("自定义信号处理器: %d\n", sig);
if (old_handlers[sig] != SIG_DFL &&
old_handlers[sig] != SIG_IGN) {
old_handlers[sig](sig);
}
}
void setup_custom_handler(int sig) {
old_handlers[sig] = signal(sig, custom_signal_handler);
}
int main() {
setup_custom_handler(SIGINT);
setup_custom_handler(SIGTERM);
printf("自定义信号处理器已设置\n");
printf("PID: %d\n", getpid());
while (1) {
pause();
}
return 0;
}7. 信号与线程安全
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
volatile sig_atomic_t flag = 0;
void signal_handler(int sig) {
flag = 1;
}
int main() {
signal(SIGINT, signal_handler);
printf("程序启动,PID: %d\n", getpid());
printf("按 Ctrl+C 设置标志\n");
while (!flag) {
printf("等待信号...\n");
sleep(1);
}
printf("标志已设置,退出\n");
return 0;
}注意事项
1. 信号安全函数
在信号处理函数中只能调用信号安全函数(async-signal-safe)。
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void unsafe_handler(int sig) {
printf("不安全的信号处理\n");
char buffer[100];
sprintf(buffer, "信号: %d\n", sig);
}
void safe_handler(int sig) {
write(STDOUT_FILENO, "安全的信号处理\n", 18);
}
int main() {
signal(SIGINT, safe_handler);
printf("使用安全的信号处理函数\n");
while (1) {
pause();
}
return 0;
}2. 全局变量
使用 volatile sig_atomic_t 类型的全局变量在信号处理函数中传递数据。
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
volatile sig_atomic_t signal_received = 0;
void signal_handler(int sig) {
signal_received = 1;
}
int main() {
signal(SIGINT, signal_handler);
while (!signal_received) {
printf("等待信号...\n");
sleep(1);
}
printf("信号已接收\n");
return 0;
}3. 信号重入
避免在信号处理函数中调用不可重入的函数。
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
void reentrant_handler(int sig) {
const char* msg = "信号处理\n";
write(STDOUT_FILENO, msg, 9);
}
void non_reentrant_handler(int sig) {
printf("不可重入的信号处理\n");
malloc(100);
}
int main() {
signal(SIGINT, reentrant_handler);
printf("使用可重入的信号处理函数\n");
while (1) {
pause();
}
return 0;
}4. 信号屏蔽
在关键操作期间屏蔽信号以避免中断。
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void critical_section() {
sigset_t old_mask, new_mask;
sigemptyset(&new_mask);
sigaddset(&new_mask, SIGINT);
sigprocmask(SIG_BLOCK, &new_mask, &old_mask);
printf("进入临界区\n");
sleep(3);
printf("退出临界区\n");
sigprocmask(SIG_SETMASK, &old_mask, NULL);
}
int main() {
printf("测试临界区\n");
critical_section();
return 0;
}总结
<signal.h> 提供的信号处理机制是 C 语言中处理异步事件的重要工具:
- 功能强大 - 可以处理各种系统事件和异常
- 灵活性高 - 可以自定义信号处理函数
- 应用广泛 - 用于进程控制、错误处理、定时器等
记住:
- 在信号处理函数中只调用信号安全函数
- 使用
volatile sig_atomic_t类型的全局变量 - 避免在信号处理函数中调用不可重入函数
- 在关键操作期间屏蔽信号