Linux信号捕捉特性详解:从基础到高级实践(超详细)

Linux信号捕捉特性详解:从基础到高级实践

引言

在Linux系统编程中,信号是一种重要的进程间通信机制,也是操作系统与应用程序交互的关键手段。信号机制允许进程在特定事件发生时接收通知,这些事件可能来自内核、其他进程或进程自身。理解信号的捕捉与处理是成为高级Linux开发者的必备技能。

信号机制最初出现在UNIX系统中,经过几十年的发展,已经成为所有类UNIX系统的核心特性之一。Linux信号机制继承自UNIX,并在其基础上进行了扩展和完善。本文将深入探讨Linux信号捕捉的各个方面,从基础概念到高级技术,最后给出实际应用的最佳实践。

第一章:信号的基本概念

1.1 什么是信号

信号是Linux系统中用于通知进程发生了某种事件的机制。这些事件可以是硬件异常(如除零错误、段错误)、用户输入(如Ctrl+C)、定时器到期,或者是其他进程通过系统调用发送的信号。

信号本质上是异步的,即信号可能在进程执行的任何时刻到达。当信号到达时,进程有三种处理方式:

  1. 忽略信号(除了SIGKILL和SIGSTOP)
  2. 执行默认操作(通常是终止进程)
  3. 捕捉信号并执行自定义的信号处理函数

1.2 信号的分类

Linux系统中的信号可以分为标准信号和实时信号两大类:

c 复制代码
#include <signal.h>

// 标准信号:1-31,有特定名称
#define SIGHUP    1   // 终端挂起
#define SIGINT    2   // 中断信号(Ctrl+C)
#define SIGQUIT   3   // 退出信号(Ctrl+\)
#define SIGILL    4   // 非法指令
#define SIGABRT   6   // 异常终止
#define SIGFPE    8   // 浮点异常
#define SIGKILL   9   // 强制终止(不可捕捉)
#define SIGSEGV  11   // 段错误
#define SIGPIPE  13   // 管道破裂
#define SIGALRM  14   // 闹钟信号
#define SIGTERM  15   // 终止信号
#define SIGUSR1  10   // 用户自定义信号1
#define SIGUSR2  12   // 用户自定义信号2

// 实时信号:34-64,没有特定名称
#define SIGRTMIN 34
#define SIGRTMAX 64

1.3 信号的产生方式

信号可以通过多种方式产生:

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>

int main() {
    pid_t pid = getpid();
    
    // 1. 通过键盘产生信号(如Ctrl+C产生SIGINT)
    printf("进程ID: %d,尝试按下Ctrl+C发送SIGINT信号\n", pid);
    sleep(5);
    
    // 2. 通过kill命令或kill()函数发送信号
    printf("在另一个终端执行: kill -SIGUSR1 %d\n", pid);
    sleep(5);
    
    // 3. 通过raise()函数向自身发送信号
    printf("向自身发送SIGUSR2信号\n");
    raise(SIGUSR2);
    
    // 4. 通过alarm()函数发送SIGALRM信号
    printf("设置3秒后发送SIGALRM信号\n");
    alarm(3);
    pause();  // 等待信号
    
    return 0;
}

1.4 信号的生命周期

一个信号从产生到处理完成经历了以下几个阶段:

  1. 产生:信号由内核、其他进程或进程自身产生
  2. 递送:信号被发送到目标进程
  3. 未决:信号已产生但尚未被处理
  4. 阻塞:进程可以选择阻塞某些信号,使其保持未决状态
  5. 处理:进程执行信号处理函数或默认操作
  6. 清除:信号处理完成后被清除

第二章:信号捕捉的基本机制

2.1 signal()函数:传统信号处理

signal()函数是信号处理的最基本接口,提供了一种简单但不够强大的信号处理机制。

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

// 信号处理函数
void signal_handler(int signum) {
    const char* signal_name;
    
    switch(signum) {
        case SIGINT:
            signal_name = "SIGINT";
            break;
        case SIGTERM:
            signal_name = "SIGTERM";
            break;
        case SIGUSR1:
            signal_name = "SIGUSR1";
            break;
        default:
            signal_name = "未知信号";
    }
    
    printf("捕捉到信号: %s (%d)\n", signal_name, signum);
    
    // 如果是SIGINT,可以选择优雅地退出
    if (signum == SIGINT) {
        printf("接收到中断信号,开始清理资源...\n");
        sleep(2);  // 模拟资源清理
        printf("资源清理完成,退出程序\n");
        exit(0);
    }
}

// 演示signal()函数的基本用法
void demo_signal_basic() {
    printf("=== signal()函数基础演示 ===\n");
    printf("进程ID: %d\n", getpid());
    printf("可以执行以下操作:\n");
    printf("1. 按下Ctrl+C发送SIGINT\n");
    printf("2. 在另一个终端执行: kill -SIGUSR1 %d\n", getpid());
    printf("3. 在另一个终端执行: kill -SIGTERM %d\n", getpid());
    
    // 注册信号处理函数
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);
    signal(SIGUSR1, signal_handler);
    
    // 忽略SIGQUIT信号
    signal(SIGQUIT, SIG_IGN);
    printf("SIGQUIT信号已被忽略,按下Ctrl+\\将不会生效\n");
    
    // 恢复SIGALRM信号的默认行为
    signal(SIGALRM, SIG_DFL);
    printf("SIGALRM信号已恢复默认行为\n");
    
    // 保持程序运行以接收信号
    while(1) {
        printf("程序运行中...\n");
        sleep(5);
    }
}

// 演示signal()函数的局限性
void demo_signal_limitations() {
    printf("\n=== signal()函数局限性演示 ===\n");
    
    // signal()在不同系统的行为不一致
    // 在某些系统上,信号处理函数执行后会被重置为默认行为
    
    void old_handler(int sig) {
        printf("旧处理函数: 收到信号 %d\n", sig);
        // 这里不重新注册信号处理函数
    }
    
    signal(SIGUSR1, old_handler);
    
    printf("第一次发送SIGUSR1:\n");
    raise(SIGUSR1);
    
    printf("第二次发送SIGUSR1(某些系统会执行默认操作):\n");
    raise(SIGUSR1);
}

int main() {
    demo_signal_basic();
    // demo_signal_limitations();  // 取消注释以演示局限性
    
    return 0;
}

2.2 sigaction()函数:现代信号处理

sigaction()函数提供了更强大、更可靠的信号处理机制,是现代Linux程序推荐使用的信号处理接口。

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

// 改进的信号处理函数
void sigaction_handler(int signum, siginfo_t *info, void *context) {
    const char* signal_name;
    char sender_info[100];
    
    // 获取信号名称
    switch(signum) {
        case SIGINT:
            signal_name = "SIGINT";
            break;
        case SIGTERM:
            signal_name = "SIGTERM";
            break;
        case SIGUSR1:
            signal_name = "SIGUSR1";
            break;
        case SIGSEGV:
            signal_name = "SIGSEGV";
            break;
        default:
            signal_name = "未知信号";
    }
    
    // 获取发送者信息
    if (info->si_pid == 0) {
        strcpy(sender_info, "内核");
    } else {
        snprintf(sender_info, sizeof(sender_info), "进程 %d", info->si_pid);
    }
    
    printf("捕捉到信号: %s (%d)\n", signal_name, signum);
    printf("发送者: %s\n", sender_info);
    printf("用户ID: %d\n", info->si_uid);
    
    // 对于SIGSEGV,打印地址信息
    if (signum == SIGSEGV) {
        printf("错误地址: %p\n", info->si_addr);
        printf("错误原因: ");
        switch(info->si_code) {
            case SEGV_MAPERR:
                printf("地址未映射到对象\n");
                break;
            case SEGV_ACCERR:
                printf("对映射对象的无效权限\n");
                break;
            default:
                printf("未知原因\n");
        }
    }
    
    // 对于实时信号,打印额外信息
    if (signum >= SIGRTMIN && signum <= SIGRTMAX) {
        printf("实时信号值: %d\n", info->si_value.sival_int);
        printf("信号代码: %d\n", info->si_code);
    }
    
    // 演示信号处理期间的信号屏蔽
    printf("信号处理函数开始执行...\n");
    sleep(3);  // 模拟处理时间
    printf("信号处理函数执行结束\n");
}

// 设置信号处理
void setup_sigaction(int signum, struct sigaction *oldact) {
    struct sigaction act;
    
    // 清空结构体
    memset(&act, 0, sizeof(act));
    
    // 设置信号处理函数
    act.sa_sigaction = sigaction_handler;
    
    // 设置SA_SIGINFO标志以获取额外信息
    act.sa_flags = SA_SIGINFO;
    
    // 设置信号处理期间要阻塞的信号
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask, SIGTERM);  // 在处理当前信号时阻塞SIGTERM
    sigaddset(&act.sa_mask, SIGUSR1);  // 在处理当前信号时阻塞SIGUSR1
    
    // 设置SA_RESTART标志以自动重启被中断的系统调用
    act.sa_flags |= SA_RESTART;
    
    // 应用信号处理
    if (sigaction(signum, &act, oldact) == -1) {
        perror("sigaction failed");
        exit(EXIT_FAILURE);
    }
    
    printf("已为信号 %d 设置sigaction处理\n", signum);
}

// 演示sigaction的高级特性
void demo_sigaction_advanced() {
    printf("\n=== sigaction()高级特性演示 ===\n");
    printf("进程ID: %d\n", getpid());
    
    struct sigaction oldact;
    
    // 为多个信号设置处理函数
    setup_sigaction(SIGINT, &oldact);
    setup_sigaction(SIGTERM, &oldact);
    setup_sigaction(SIGUSR1, &oldact);
    
    // 设置SIGSEGV处理函数以演示错误处理
    struct sigaction segv_act;
    memset(&segv_act, 0, sizeof(segv_act));
    segv_act.sa_sigaction = sigaction_handler;
    segv_act.sa_flags = SA_SIGINFO | SA_ONSTACK;  // 使用备用信号栈
    sigemptyset(&segv_act.sa_mask);
    sigaction(SIGSEGV, &segv_act, NULL);
    
    printf("\n测试说明:\n");
    printf("1. 按下Ctrl+C发送SIGINT\n");
    printf("2. 在另一个终端执行: kill -SIGUSR1 %d\n", getpid());
    printf("3. 在另一个终端执行: kill -SIGTERM %d\n", getpid());
    printf("4. SIGSEGV信号将在后续演示中触发\n");
    
    // 演示SA_RESTART标志的效果
    printf("\n测试SA_RESTART标志(输入一些字符):\n");
    char buffer[100];
    int n = read(STDIN_FILENO, buffer, sizeof(buffer));
    if (n == -1) {
        perror("read error");
    } else {
        printf("成功读取 %d 字节\n", n);
    }
    
    // 保持程序运行
    while(1) {
        pause();  // 等待信号
    }
}

// 演示信号处理函数的持久性
void demo_signal_persistence() {
    printf("\n=== 信号处理函数的持久性 ===\n");
    
    struct sigaction act, oldact;
    memset(&act, 0, sizeof(act));
    act.sa_handler = SIG_IGN;  // 忽略信号
    act.sa_flags = 0;
    
    // 临时忽略SIGINT
    sigaction(SIGINT, &act, &oldact);
    printf("临时忽略SIGINT信号\n");
    
    sleep(3);
    
    // 恢复原来的信号处理
    sigaction(SIGINT, &oldact, NULL);
    printf("恢复SIGINT信号处理\n");
}

int main() {
    demo_sigaction_advanced();
    // demo_signal_persistence();  // 取消注释以演示持久性
    
    return 0;
}

第三章:信号捕捉的关键特性

3.1 信号处理函数的可重入性

可重入函数是指可以被多个任务同时调用而不会产生冲突的函数。在信号处理函数中,必须使用可重入函数,因为信号可能在程序执行的任何时刻发生。

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>

// 全局变量 - 在信号处理函数中访问这些变量是不安全的
int unsafe_counter = 0;
char unsafe_buffer[100];

// 不可重入的信号处理函数(存在安全隐患)
void unsafe_signal_handler(int signum) {
    // 问题1: 调用不可重入函数
    printf("不安全处理: 收到信号 %d\n", signum);  // printf不可重入
    
    // 问题2: 修改全局变量
    unsafe_counter++;
    sprintf(unsafe_buffer, "信号计数: %d", unsafe_counter);  // sprintf不可重入
    
    // 问题3: 调用其他不可重入函数
    // malloc, free, strtok等都不应在信号处理函数中使用
}

// 安全的信号处理函数(只使用异步信号安全函数)
void safe_signal_handler(int signum) {
    // 只能使用异步信号安全函数
    // 写入文件描述符2(stderr)
    const char* msg = "安全处理: 收到信号\n";
    write(STDERR_FILENO, msg, strlen(msg));
    
    // 使用系统调用获取进程ID
    pid_t pid = getpid();  // getpid是异步信号安全的
    char pid_msg[50];
    
    // 注意:snprintf不是异步信号安全的,这里仅作演示
    // 在实际应用中应避免在信号处理函数中使用格式化输出
    
    // 安全的退出方式
    if (signum == SIGINT) {
        _exit(EXIT_FAILURE);  // _exit是异步信号安全的,exit不是
    }
}

// 演示信号处理函数中的可重入问题
volatile sig_atomic_t signal_flag = 0;  // 使用sig_atomic_t类型

void demo_reentrancy_issue() {
    printf("\n=== 信号处理函数的可重入性问题演示 ===\n");
    
    // 设置信号处理函数
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = unsafe_signal_handler;
    sigaction(SIGUSR1, &act, NULL);
    
    printf("发送多个SIGUSR1信号演示可重入问题...\n");
    
    // 快速发送多个信号
    for (int i = 0; i < 5; i++) {
        raise(SIGUSR1);
    }
    
    sleep(1);
    printf("\n使用安全处理函数...\n");
    
    // 设置安全处理函数
    act.sa_handler = safe_signal_handler;
    sigaction(SIGUSR1, &act, NULL);
    
    for (int i = 0; i < 3; i++) {
        raise(SIGUSR1);
    }
}

// 使用标志位的安全模式
void flag_based_handler(int signum) {
    // 只设置标志位,在主循环中处理
    signal_flag = 1;
}

void demo_flag_based_approach() {
    printf("\n=== 基于标志位的信号处理模式 ===\n");
    
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = flag_based_handler;
    act.sa_flags = SA_RESTART;
    sigaction(SIGUSR1, &act, NULL);
    
    printf("进程ID: %d\n", getpid());
    printf("发送SIGUSR1信号将在主循环中处理\n");
    
    while(1) {
        // 检查信号标志
        if (signal_flag) {
            printf("在主循环中处理信号\n");
            signal_flag = 0;
            
            // 这里可以安全地调用任何函数
            // 因为我们在主线程上下文中
            
            // 模拟一些处理工作
            sleep(1);
            printf("信号处理完成\n");
        }
        
        // 主程序的其他工作
        printf(".");
        fflush(stdout);
        sleep(2);
    }
}

// 异步信号安全函数列表演示
void list_async_safe_functions() {
    printf("\n=== 常见的异步信号安全函数 ===\n");
    printf("以下函数可以在信号处理函数中安全调用:\n");
    printf("1. 系统调用: read(), write(), open(), close()\n");
    printf("2. 进程控制: _exit(), getpid(), getuid(), kill()\n");
    printf("3. 信号处理: sigaction(), sigprocmask(), sigpending()\n");
    printf("4. 其他: pause(), alarm(), sleep()\n");
    printf("\n以下函数不安全,不应在信号处理函数中使用:\n");
    printf("1. 内存分配: malloc(), calloc(), realloc(), free()\n");
    printf("2. 标准I/O: printf(), scanf(), fopen(), fclose()\n");
    printf("3. 字符串处理: strtok(), strerror()\n");
    printf("4. 其他: system(), exit()\n");
}

int main() {
    demo_reentrancy_issue();
    // demo_flag_based_approach();  // 取消注释以演示基于标志位的处理
    list_async_safe_functions();
    
    return 0;
}

3.2 信号屏蔽与阻塞

信号屏蔽允许进程暂时阻止某些信号的传递,这在关键代码段执行时非常有用。

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>

// 显示信号集的辅助函数
void print_signal_set(const sigset_t *set, const char *name) {
    printf("%s信号集: ", name);
    for (int sig = 1; sig <= 32; sig++) {
        if (sigismember(set, sig)) {
            printf("%d ", sig);
        }
    }
    printf("\n");
}

// 演示基本的信号屏蔽
void demo_basic_signal_mask() {
    printf("\n=== 基本信号屏蔽演示 ===\n");
    
    sigset_t new_mask, old_mask, pending_mask;
    
    // 初始化信号集
    sigemptyset(&new_mask);
    
    // 添加要屏蔽的信号
    sigaddset(&new_mask, SIGINT);
    sigaddset(&new_mask, SIGTERM);
    sigaddset(&new_mask, SIGUSR1);
    
    // 获取并显示当前信号掩码
    sigprocmask(SIG_BLOCK, NULL, &old_mask);
    print_signal_set(&old_mask, "当前");
    
    // 设置新的信号掩码(阻塞指定信号)
    sigprocmask(SIG_BLOCK, &new_mask, &old_mask);
    printf("已阻塞SIGINT, SIGTERM, SIGUSR1信号\n");
    
    // 显示新的信号掩码
    sigprocmask(SIG_BLOCK, NULL, &new_mask);
    print_signal_set(&new_mask, "新的");
    
    // 发送一些信号,它们将被阻塞
    printf("\n发送被阻塞的信号...\n");
    raise(SIGINT);
    raise(SIGTERM);
    raise(SIGUSR1);
    
    // 检查未决信号
    sigpending(&pending_mask);
    print_signal_set(&pending_mask, "未决");
    
    // 解除信号阻塞
    printf("\n解除信号阻塞...\n");
    sigprocmask(SIG_UNBLOCK, &new_mask, NULL);
    
    // 检查未决信号(应该为空)
    sigpending(&pending_mask);
    print_signal_set(&pending_mask, "解除后未决");
    
    printf("注意:解除阻塞后,未决信号会立即被递送\n");
}

// 演示关键代码段的信号保护
void critical_section_protection() {
    printf("\n=== 关键代码段的信号保护 ===\n");
    
    sigset_t mask, oldmask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    
    printf("进入关键代码段...\n");
    
    // 阻塞SIGINT信号
    sigprocmask(SIG_BLOCK, &mask, &oldmask);
    
    // 关键代码段开始
    printf("关键代码段执行中(不会被SIGINT中断)\n");
    printf("尝试按下Ctrl+C,信号将被阻塞\n");
    
    for (int i = 0; i < 5; i++) {
        printf("关键操作 %d/5\n", i + 1);
        sleep(1);
    }
    
    // 关键代码段结束
    
    // 恢复原来的信号掩码
    sigprocmask(SIG_SETMASK, &oldmask, NULL);
    
    printf("关键代码段结束,信号处理已恢复\n");
}

// 演示pthread中的信号屏蔽
void* thread_function(void* arg) {
    printf("线程开始执行\n");
    
    // 线程可以有自己的信号掩码
    sigset_t thread_mask;
    sigemptyset(&thread_mask);
    sigaddset(&thread_mask, SIGUSR1);
    
    // 设置线程信号掩码
    pthread_sigmask(SIG_BLOCK, &thread_mask, NULL);
    
    printf("线程已阻塞SIGUSR1信号\n");
    printf("线程ID: %lu\n", pthread_self());
    
    // 线程执行一些工作
    for (int i = 0; i < 3; i++) {
        printf("线程工作中... %d/3\n", i + 1);
        sleep(2);
    }
    
    return NULL;
}

void demo_thread_signal_mask() {
    printf("\n=== 线程中的信号屏蔽 ===\n");
    
    pthread_t thread;
    
    // 创建线程
    if (pthread_create(&thread, NULL, thread_function, NULL) != 0) {
        perror("pthread_create failed");
        return;
    }
    
    // 主线程发送信号给子线程
    printf("主线程发送SIGUSR1到子线程(将被阻塞)\n");
    pthread_kill(thread, SIGUSR1);
    
    // 等待线程结束
    pthread_join(thread, NULL);
    printf("线程已结束\n");
}

// 演示sigaction中的sa_mask
void mask_in_sigaction_handler(int signum) {
    printf("信号处理函数开始,信号 %d\n", signum);
    
    // 显示处理期间的信号掩码
    sigset_t current_mask;
    sigprocmask(SIG_BLOCK, NULL, &current_mask);
    print_signal_set(&current_mask, "处理中");
    
    // 模拟长时间处理
    printf("信号处理中...\n");
    sleep(3);
    
    printf("信号处理结束\n");
}

void demo_sigaction_mask() {
    printf("\n=== sigaction中的sa_mask演示 ===\n");
    
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = mask_in_sigaction_handler;
    
    // 设置信号处理期间要阻塞的信号
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask, SIGTERM);
    sigaddset(&act.sa_mask, SIGUSR1);
    
    sigaction(SIGINT, &act, NULL);
    
    printf("设置SIGINT处理函数,在处理期间将阻塞SIGTERM和SIGUSR1\n");
    printf("进程ID: %d\n", getpid());
    printf("1. 按下Ctrl+C发送SIGINT\n");
    printf("2. 在SIGINT处理期间发送SIGTERM或SIGUSR1\n");
    
    pause();
}

int main() {
    demo_basic_signal_mask();
    critical_section_protection();
    demo_thread_signal_mask();
    demo_sigaction_mask();
    
    return 0;
}

3.3 信号的可靠性与不可靠信号

Linux信号分为可靠信号(实时信号)和不可靠信号(标准信号)。不可靠信号可能丢失,而可靠信号支持排队,不会丢失。

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

volatile int standard_signal_count = 0;
volatile int realtime_signal_count = 0;

// 标准信号处理函数
void standard_signal_handler(int signum) {
    standard_signal_count++;
    printf("标准信号 %d 处理,计数: %d\n", signum, standard_signal_count);
}

// 实时信号处理函数
void realtime_signal_handler(int signum) {
    realtime_signal_count++;
    printf("实时信号 %d 处理,计数: %d\n", signum, realtime_signal_count);
}

// 演示标准信号的不可靠性
void demo_unreliable_signals() {
    printf("\n=== 标准信号的不可靠性演示 ===\n");
    
    // 设置SIGUSR1处理函数
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = standard_signal_handler;
    act.sa_flags = 0;  // 不使用SA_RESTART
    
    sigaction(SIGUSR1, &act, NULL);
    
    printf("标准信号(SIGUSR1)处理函数已设置\n");
    printf("将快速发送多个SIGUSR1信号...\n");
    
    // 重置计数器
    standard_signal_count = 0;
    
    // 快速发送多个信号
    for (int i = 0; i < 10; i++) {
        raise(SIGUSR1);
    }
    
    sleep(1);  // 等待信号处理
    
    printf("发送了10个信号,实际处理了 %d 个信号\n", standard_signal_count);
    printf("结论:标准信号可能丢失,不支持排队\n");
}

// 演示实时信号的可靠性
void demo_reliable_signals() {
    printf("\n=== 实时信号的可靠性演示 ===\n");
    
    int realtime_sig = SIGRTMIN;  // 使用第一个实时信号
    
    // 设置实时信号处理函数
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = realtime_signal_handler;
    act.sa_flags = SA_SIGINFO;  // 使用SA_SIGINFO获取额外信息
    
    sigaction(realtime_sig, &act, NULL);
    
    printf("实时信号(%d)处理函数已设置\n", realtime_sig);
    printf("将快速发送多个实时信号...\n");
    
    // 重置计数器
    realtime_signal_count = 0;
    
    // 创建信号值
    union sigval value;
    value.sival_int = 0;
    
    // 快速发送多个实时信号
    for (int i = 0; i < 10; i++) {
        value.sival_int = i;
        if (sigqueue(getpid(), realtime_sig, value) == -1) {
            perror("sigqueue failed");
        }
    }
    
    sleep(2);  // 等待信号处理
    
    printf("发送了10个实时信号,实际处理了 %d 个信号\n", realtime_signal_count);
    printf("结论:实时信号可靠,支持排队\n");
}

// 演示信号丢失的极端情况
void demo_signal_loss() {
    printf("\n=== 信号丢失的极端情况演示 ===\n");
    
    // 长时间运行的信号处理函数
    void slow_handler(int signum) {
        printf("开始处理信号 %d\n", signum);
        sleep(5);  // 长时间处理
        printf("结束处理信号 %d\n", signum);
    }
    
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = slow_handler;
    sigaction(SIGUSR1, &act, NULL);
    
    printf("设置慢速处理函数(5秒)\n");
    printf("进程ID: %d\n", getpid());
    printf("在5秒内快速发送多个SIGUSR1信号...\n");
    
    // 在子进程中快速发送信号
    pid_t pid = fork();
    if (pid == 0) {
        // 子进程:快速发送信号
        for (int i = 0; i < 10; i++) {
            kill(getppid(), SIGUSR1);
            usleep(100000);  // 100ms间隔
        }
        exit(0);
    }
    
    // 父进程等待
    wait(NULL);
    printf("信号发送完成\n");
}

// 演示使用实时信号避免丢失
void demo_realtime_solution() {
    printf("\n=== 使用实时信号避免丢失 ===\n");
    
    int realtime_sig = SIGRTMIN + 1;
    
    void realtime_slow_handler(int signum) {
        printf("开始处理实时信号 %d\n", signum);
        sleep(3);  // 长时间处理
        printf("结束处理实时信号 %d\n", signum);
    }
    
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = realtime_slow_handler;
    act.sa_flags = SA_SIGINFO;
    
    sigaction(realtime_sig, &act, NULL);
    
    printf("设置实时信号处理函数(3秒)\n");
    printf("进程ID: %d\n", getpid());
    printf("在3秒内快速发送多个实时信号...\n");
    
    // 在子进程中快速发送实时信号
    pid_t pid = fork();
    if (pid == 0) {
        // 子进程:快速发送实时信号
        union sigval value;
        for (int i = 0; i < 10; i++) {
            value.sival_int = i;
            sigqueue(getppid(), realtime_sig, value);
            usleep(100000);  // 100ms间隔
        }
        exit(0);
    }
    
    // 父进程等待更长时间以处理所有排队的信号
    wait(NULL);
    sleep(15);  // 给足够时间处理所有排队的信号
    printf("所有实时信号都已处理完成\n");
}

int main() {
    demo_unreliable_signals();
    demo_reliable_signals();
    demo_signal_loss();
    demo_realtime_solution();
    
    return 0;
}

3.4 信号处理期间的信号屏蔽

当信号处理函数执行时,系统会自动阻塞当前正在处理的信号,防止递归调用。此外,还可以通过sa_mask指定额外的阻塞信号。

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

// 用于跟踪递归深度的全局变量
volatile int recursion_depth = 0;

// 递归信号处理函数
void recursive_handler(int signum) {
    recursion_depth++;
    printf("信号处理函数深度: %d,信号: %d\n", recursion_depth, signum);
    
    if (recursion_depth < 3) {
        // 模拟在处理期间再次收到同种信号
        printf("模拟再次收到信号...\n");
        raise(signum);
    }
    
    sleep(1);
    recursion_depth--;
    printf("信号处理函数结束,深度: %d\n", recursion_depth);
}

// 演示自动阻塞当前信号
void demo_auto_block() {
    printf("\n=== 信号处理期间的自动阻塞 ===\n");
    
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = recursive_handler;
    act.sa_flags = 0;  // 默认行为会自动阻塞当前信号
    
    sigaction(SIGUSR1, &act, NULL);
    
    printf("设置递归信号处理函数\n");
    printf("进程ID: %d\n", getpid());
    printf("发送SIGUSR1信号...\n");
    
    recursion_depth = 0;
    raise(SIGUSR1);
    
    printf("注意:虽然调用了raise(),但同种信号被自动阻塞\n");
}

// 使用sa_mask阻塞额外信号
void handler_with_mask(int signum) {
    printf("开始处理信号 %d\n", signum);
    
    // 显示当前的信号掩码
    sigset_t current_mask;
    sigprocmask(SIG_BLOCK, NULL, &current_mask);
    
    printf("处理期间的信号掩码:\n");
    for (int sig = 1; sig <= 32; sig++) {
        if (sigismember(&current_mask, sig)) {
            printf("  信号 %d 被阻塞\n", sig);
        }
    }
    
    printf("处理中...\n");
    sleep(3);
    printf("处理完成\n");
}

void demo_custom_mask() {
    printf("\n=== 使用sa_mask自定义阻塞信号 ===\n");
    
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = handler_with_mask;
    
    // 设置信号处理期间要阻塞的信号
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask, SIGINT);
    sigaddset(&act.sa_mask, SIGTERM);
    sigaddset(&act.sa_mask, SIGUSR1);
    
    sigaction(SIGUSR2, &act, NULL);
    
    printf("设置SIGUSR2处理函数\n");
    printf("在处理期间将阻塞SIGINT、SIGTERM和SIGUSR1\n");
    printf("进程ID: %d\n", getpid());
    
    // 创建子进程发送信号
    pid_t pid = fork();
    if (pid == 0) {
        sleep(1);  // 等待父进程进入信号处理
        
        printf("子进程发送SIGUSR1到父进程(应被阻塞)\n");
        kill(getppid(), SIGUSR1);
        
        printf("子进程发送SIGTERM到父进程(应被阻塞)\n");
        kill(getppid(), SIGTERM);
        
        printf("子进程发送SIGUSR2到父进程(应被阻塞)\n");
        kill(getppid(), SIGUSR2);
        
        exit(0);
    }
    
    // 父进程发送SIGUSR2触发处理
    printf("父进程发送SIGUSR2触发处理\n");
    raise(SIGUSR2);
    
    wait(NULL);
    printf("所有信号处理完成\n");
}

// 演示SA_NODEFER标志
void handler_with_nodefer(int signum) {
    static int count = 0;
    count++;
    
    printf("SA_NODEFER处理函数调用 %d,信号: %d\n", count, signum);
    
    if (count < 3) {
        // 由于设置了SA_NODEFER,同种信号不会被阻塞
        printf("再次发送同种信号(可能造成递归)\n");
        raise(signum);
    }
    
    sleep(1);
    printf("SA_NODEFER处理函数结束 %d\n", count);
}

void demo_nodefer_flag() {
    printf("\n=== SA_NODEFER标志演示 ===\n");
    
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = handler_with_nodefer;
    act.sa_flags = SA_NODEFER;  // 不自动阻塞当前信号
    
    sigaction(SIGUSR1, &act, NULL);
    
    printf("设置SA_NODEFER标志,同种信号不会被自动阻塞\n");
    printf("警告:这可能导致递归和栈溢出\n");
    printf("发送SIGUSR1信号...\n");
    
    raise(SIGUSR1);
}

// 演示嵌套信号处理
volatile int outer_handler_called = 0;
volatile int inner_handler_called = 0;

void inner_handler(int signum) {
    inner_handler_called = 1;
    printf("内部处理函数被调用,信号: %d\n", signum);
    
    // 显示当前的信号掩码
    sigset_t current_mask;
    sigprocmask(SIG_BLOCK, NULL, &current_mask);
    printf("内部处理期间的信号掩码:\n");
    for (int sig = 1; sig <= 32; sig++) {
        if (sigismember(&current_mask, sig)) {
            printf("  信号 %d 被阻塞\n", sig);
        }
    }
}

void outer_handler(int signum) {
    outer_handler_called = 1;
    printf("外部处理函数被调用,信号: %d\n", signum);
    
    // 设置内部信号处理函数
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = inner_handler;
    sigaction(SIGUSR2, &act, NULL);
    
    // 发送另一个信号
    printf("发送SIGUSR2信号...\n");
    raise(SIGUSR2);
    
    printf("外部处理函数结束\n");
}

void demo_nested_handling() {
    printf("\n=== 嵌套信号处理演示 ===\n");
    
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = outer_handler;
    
    sigaction(SIGUSR1, &act, NULL);
    
    printf("设置嵌套信号处理\n");
    printf("发送SIGUSR1将触发SIGUSR2\n");
    
    outer_handler_called = 0;
    inner_handler_called = 0;
    
    raise(SIGUSR1);
    
    printf("外部处理函数调用: %d\n", outer_handler_called);
    printf("内部处理函数调用: %d\n", inner_handler_called);
}

int main() {
    demo_auto_block();
    demo_custom_mask();
    demo_nodefer_flag();
    demo_nested_handling();
    
    return 0;
}

第四章:高级信号处理技术

4.1 使用sigwait同步处理信号

sigwait()函数允许线程同步等待信号的到达,这是一种更可控的信号处理方式,特别适合多线程程序。

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>

// 全局变量
volatile sig_atomic_t shutdown_flag = 0;
pthread_t signal_thread;

// 信号处理线程函数
void* signal_handler_thread(void* arg) {
    printf("信号处理线程启动,线程ID: %lu\n", pthread_self());
    
    // 设置要等待的信号集
    sigset_t signal_set;
    sigemptyset(&signal_set);
    sigaddset(&signal_set, SIGINT);
    sigaddset(&signal_set, SIGTERM);
    sigaddset(&signal_set, SIGUSR1);
    sigaddset(&signal_set, SIGUSR2);
    
    int sig_number;
    int ret;
    
    while (!shutdown_flag) {
        // 等待信号
        ret = sigwait(&signal_set, &sig_number);
        if (ret != 0) {
            fprintf(stderr, "sigwait失败: %s\n", strerror(ret));
            break;
        }
        
        // 处理信号
        switch(sig_number) {
            case SIGINT:
                printf("线程收到SIGINT,开始优雅关闭\n");
                shutdown_flag = 1;
                break;
                
            case SIGTERM:
                printf("线程收到SIGTERM,开始优雅关闭\n");
                shutdown_flag = 1;
                break;
                
            case SIGUSR1:
                printf("线程收到SIGUSR1,执行特定操作\n");
                // 执行一些操作
                break;
                
            case SIGUSR2:
                printf("线程收到SIGUSR2,执行其他操作\n");
                // 执行其他操作
                break;
                
            default:
                printf("线程收到未知信号: %d\n", sig_number);
        }
    }
    
    printf("信号处理线程结束\n");
    return NULL;
}

// 初始化信号处理线程
int init_signal_handler() {
    printf("初始化信号处理线程\n");
    
    // 在主线程中阻塞所有信号
    sigset_t all_signals;
    sigfillset(&all_signals);
    pthread_sigmask(SIG_BLOCK, &all_signals, NULL);
    
    // 创建信号处理线程
    int ret = pthread_create(&signal_thread, NULL, signal_handler_thread, NULL);
    if (ret != 0) {
        fprintf(stderr, "创建线程失败: %s\n", strerror(ret));
        return -1;
    }
    
    printf("信号处理线程创建成功\n");
    return 0;
}

// 工作线程函数
void* worker_thread(void* arg) {
    int thread_id = *(int*)arg;
    printf("工作线程 %d 启动\n", thread_id);
    
    while (!shutdown_flag) {
        printf("工作线程 %d 工作中...\n", thread_id);
        sleep(thread_id + 1);  // 不同的工作间隔
        
        // 模拟工作
        for (int i = 0; i < 1000000; i++) {
            // 空循环模拟工作
        }
    }
    
    printf("工作线程 %d 收到关闭信号,结束\n", thread_id);
    return NULL;
}

// 演示多线程信号处理
void demo_multithread_signal_handling() {
    printf("\n=== 多线程信号处理演示 ===\n");
    
    // 初始化信号处理
    if (init_signal_handler() != 0) {
        exit(EXIT_FAILURE);
    }
    
    // 创建工作线程
    pthread_t workers[3];
    int thread_ids[3] = {1, 2, 3};
    
    for (int i = 0; i < 3; i++) {
        pthread_create(&workers[i], NULL, worker_thread, &thread_ids[i]);
    }
    
    // 主线程显示信息
    printf("主线程ID: %lu\n", pthread_self());
    printf("进程ID: %d\n", getpid());
    printf("\n可以执行以下操作:\n");
    printf("1. 按下Ctrl+C发送SIGINT\n");
    printf("2. 发送SIGTERM: kill -TERM %d\n", getpid());
    printf("3. 发送SIGUSR1: kill -USR1 %d\n", getpid());
    printf("4. 发送SIGUSR2: kill -USR2 %d\n", getpid());
    printf("\n等待信号...\n");
    
    // 主线程等待所有工作线程结束
    for (int i = 0; i < 3; i++) {
        pthread_join(workers[i], NULL);
    }
    
    // 等待信号处理线程结束
    pthread_join(signal_thread, NULL);
    
    printf("所有线程已结束,程序退出\n");
}

// 使用sigwaitinfo获取更多信号信息
void demo_sigwaitinfo() {
    printf("\n=== 使用sigwaitinfo获取信号信息 ===\n");
    
    // 阻塞SIGUSR1和SIGUSR2
    sigset_t signal_set;
    sigemptyset(&signal_set);
    sigaddset(&signal_set, SIGUSR1);
    sigaddset(&signal_set, SIGUSR2);
    pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
    
    printf("进程ID: %d\n", getpid());
    printf("信号已阻塞,使用sigwaitinfo等待\n");
    
    // 创建子进程发送信号
    pid_t pid = fork();
    if (pid == 0) {
        // 子进程:发送带额外信息的信号
        union sigval value;
        
        sleep(1);
        
        value.sival_int = 100;
        printf("子进程发送SIGUSR1,值: %d\n", value.sival_int);
        sigqueue(getppid(), SIGUSR1, value);
        
        sleep(1);
        
        value.sival_int = 200;
        printf("子进程发送SIGUSR2,值: %d\n", value.sival_int);
        sigqueue(getppid(), SIGUSR2, value);
        
        exit(0);
    }
    
    // 父进程:等待信号
    siginfo_t info;
    int sig;
    
    for (int i = 0; i < 2; i++) {
        sig = sigwaitinfo(&signal_set, &info);
        if (sig == -1) {
            perror("sigwaitinfo失败");
            continue;
        }
        
        printf("收到信号: %d\n", sig);
        printf("发送进程: %d\n", info.si_pid);
        printf("信号值: %d\n", info.si_value.sival_int);
        printf("用户ID: %d\n", info.si_uid);
    }
    
    wait(NULL);
    printf("sigwaitinfo演示完成\n");
}

// 使用sigtimedwait等待信号(带超时)
void demo_sigtimedwait() {
    printf("\n=== 使用sigtimedwait带超时等待信号 ===\n");
    
    sigset_t signal_set;
    sigemptyset(&signal_set);
    sigaddset(&signal_set, SIGUSR1);
    pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
    
    printf("设置5秒超时等待SIGUSR1信号\n");
    printf("进程ID: %d\n", getpid());
    
    // 设置超时时间
    struct timespec timeout;
    timeout.tv_sec = 5;
    timeout.tv_nsec = 0;
    
    int sig = sigtimedwait(&signal_set, NULL, &timeout);
    if (sig == -1) {
        if (errno == EAGAIN) {
            printf("超时:在5秒内未收到SIGUSR1信号\n");
        } else {
            perror("sigtimedwait失败");
        }
    } else {
        printf("收到信号: %d\n", sig);
    }
    
    printf("sigtimedwait演示完成\n");
}

int main() {
    demo_multithread_signal_handling();
    demo_sigwaitinfo();
    demo_sigtimedwait();
    
    return 0;
}

4.2 信号栈

信号栈是一个专门用于信号处理函数执行的栈空间,可以防止栈溢出,特别是在处理递归信号或大量数据时。

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>

// 递归信号处理函数,可能造成栈溢出
void recursive_signal_handler(int signum) {
    static int depth = 0;
    char large_buffer[8192];  // 分配大缓冲区
    
    depth++;
    printf("递归深度: %d,栈地址: %p\n", depth, &large_buffer);
    
    // 模拟递归调用
    if (depth < 10) {
        // 填充缓冲区以消耗栈空间
        memset(large_buffer, depth, sizeof(large_buffer));
        
        // 再次发送信号
        raise(signum);
    }
    
    depth--;
}

// 使用备用信号栈的处理函数
void handler_on_altstack(int signum) {
    char buffer[4096];
    printf("在备用信号栈上处理信号,栈地址: %p\n", &buffer);
    
    // 显示栈信息
    stack_t current_stack;
    sigaltstack(NULL, &current_stack);
    
    printf("当前栈信息:\n");
    printf("  栈基址: %p\n", current_stack.ss_sp);
    printf("  栈大小: %zu 字节\n", current_stack.ss_size);
    printf("  标志: %d\n", current_stack.ss_flags);
    
    sleep(1);
}

// 演示栈溢出问题
void demo_stack_overflow() {
    printf("\n=== 信号处理栈溢出演示 ===\n");
    
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = recursive_signal_handler;
    
    sigaction(SIGUSR1, &act, NULL);
    
    printf("设置递归信号处理函数\n");
    printf("警告:这可能导致栈溢出\n");
    printf("发送SIGUSR1信号...\n");
    
    // 注意:这里可能真的导致栈溢出和程序崩溃
    // 在实际运行时要小心
    raise(SIGUSR1);
    
    printf("如果看到这行,说明没有栈溢出\n");
}

// 设置和使用备用信号栈
void setup_alternate_signal_stack() {
    printf("\n=== 设置备用信号栈 ===\n");
    
    // 分配备用信号栈内存
    size_t stack_size = SIGSTKSZ * 4;  // 使用4倍默认大小
    void* stack_memory = mmap(NULL, stack_size,
                              PROT_READ | PROT_WRITE,
                              MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK,
                              -1, 0);
    
    if (stack_memory == MAP_FAILED) {
        perror("mmap失败");
        exit(EXIT_FAILURE);
    }
    
    printf("分配的备用栈内存: %p,大小: %zu 字节\n", 
           stack_memory, stack_size);
    
    // 设置备用信号栈
    stack_t altstack;
    altstack.ss_sp = stack_memory;
    altstack.ss_size = stack_size;
    altstack.ss_flags = 0;
    
    if (sigaltstack(&altstack, NULL) == -1) {
        perror("sigaltstack失败");
        exit(EXIT_FAILURE);
    }
    
    printf("备用信号栈设置成功\n");
    
    // 设置使用备用栈的信号处理
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = handler_on_altstack;
    act.sa_flags = SA_ONSTACK;  // 关键:使用备用栈
    
    sigaction(SIGUSR2, &act, NULL);
    
    printf("设置使用备用栈的信号处理函数\n");
    printf("进程ID: %d\n", getpid());
    printf("发送SIGUSR2信号将使用备用栈处理\n");
    
    raise(SIGUSR2);
    
    // 清理
    sleep(1);
    munmap(stack_memory, stack_size);
    printf("备用栈内存已释放\n");
}

// 演示信号栈的自动切换
void demo_auto_stack_switch() {
    printf("\n=== 信号栈自动切换演示 ===\n");
    
    // 获取当前信号栈设置
    stack_t old_stack;
    if (sigaltstack(NULL, &old_stack) == -1) {
        perror("获取当前栈失败");
        return;
    }
    
    if (old_stack.ss_flags & SS_ONSTACK) {
        printf("当前正在信号栈上执行\n");
    } else {
        printf("当前不在信号栈上执行\n");
    }
    
    // 递归函数,在信号处理中调用
    void recursive_func(int depth) {
        char buffer[1024];
        printf("递归深度 %d,缓冲区地址: %p\n", depth, buffer);
        
        if (depth < 5) {
            recursive_func(depth + 1);
        }
    }
    
    // 信号处理函数
    void stack_test_handler(int signum) {
        printf("信号处理函数开始\n");
        
        // 检查是否在备用栈上
        stack_t current_stack;
        sigaltstack(NULL, &current_stack);
        
        if (current_stack.ss_flags & SS_ONSTACK) {
            printf("正在备用信号栈上执行\n");
        } else {
            printf("不在备用信号栈上执行\n");
        }
        
        // 进行递归调用
        recursive_func(1);
        
        printf("信号处理函数结束\n");
    }
    
    // 设置信号处理
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = stack_test_handler;
    act.sa_flags = SA_ONSTACK;
    
    sigaction(SIGUSR1, &act, NULL);
    
    // 设置备用栈
    size_t stack_size = SIGSTKSZ;
    void* stack_mem = malloc(stack_size);
    
    stack_t new_stack;
    new_stack.ss_sp = stack_mem;
    new_stack.ss_size = stack_size;
    new_stack.ss_flags = 0;
    
    sigaltstack(&new_stack, NULL);
    
    printf("发送信号测试备用栈...\n");
    raise(SIGUSR1);
    
    free(stack_mem);
}

// 演示在不同栈上的信号处理
void demo_different_stacks() {
    printf("\n=== 不同栈上的信号处理演示 ===\n");
    
    // 处理函数1:在主栈上执行
    void handler_on_main_stack(int signum) {
        char buffer[100];
        printf("处理函数1:在主栈上,缓冲区地址: %p\n", &buffer);
    }
    
    // 处理函数2:在备用栈上执行
    void handler_on_alt_stack(int signum) {
        char buffer[100];
        printf("处理函数2:在备用栈上,缓冲区地址: %p\n", &buffer);
    }
    
    // 设置备用栈
    size_t stack_size = SIGSTKSZ;
    void* stack_mem = malloc(stack_size);
    
    stack_t alt_stack;
    alt_stack.ss_sp = stack_mem;
    alt_stack.ss_size = stack_size;
    alt_stack.ss_flags = 0;
    
    sigaltstack(&alt_stack, NULL);
    
    // 设置两个信号处理
    struct sigaction act1, act2;
    
    memset(&act1, 0, sizeof(act1));
    act1.sa_handler = handler_on_main_stack;
    act1.sa_flags = 0;  // 不在备用栈上
    
    memset(&act2, 0, sizeof(act2));
    act2.sa_handler = handler_on_alt_stack;
    act2.sa_flags = SA_ONSTACK;  // 在备用栈上
    
    sigaction(SIGUSR1, &act1, NULL);
    sigaction(SIGUSR2, &act2, NULL);
    
    printf("设置完成:\n");
    printf("  SIGUSR1: 在主栈上处理\n");
    printf("  SIGUSR2: 在备用栈上处理\n");
    printf("进程ID: %d\n", getpid());
    
    // 发送信号
    printf("\n发送SIGUSR1...\n");
    raise(SIGUSR1);
    
    printf("\n发送SIGUSR2...\n");
    raise(SIGUSR2);
    
    free(stack_mem);
}

int main() {
    // 小心运行这个演示,可能导致栈溢出
    // demo_stack_overflow();
    
    setup_alternate_signal_stack();
    demo_auto_stack_switch();
    demo_different_stacks();
    
    return 0;
}

4.3 实时信号

实时信号是Linux中更可靠的信号机制,支持排队、携带额外数据等特性。

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <time.h>

// 实时信号处理函数
void realtime_signal_handler(int signum, siginfo_t* info, void* context) {
    printf("\n=== 实时信号处理 ===\n");
    printf("信号编号: %d\n", signum);
    printf("信号代码: %d\n", info->si_code);
    
    // 检查信号来源
    if (info->si_code == SI_USER) {
        printf("来源: 用户进程 (%d)\n", info->si_pid);
    } else if (info->si_code == SI_KERNEL) {
        printf("来源: 内核\n");
    } else if (info->si_code == SI_QUEUE) {
        printf("来源: sigqueue()\n");
        printf("发送进程: %d\n", info->si_pid);
        printf("用户ID: %d\n", info->si_uid);
        printf("信号值: %d\n", info->si_value.sival_int);
    }
    
    // 获取时间戳
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    printf("处理时间: %ld.%09ld\n", ts.tv_sec, ts.tv_nsec);
    
    // 模拟处理时间
    usleep(100000);  // 100ms
}

// 演示实时信号的基本使用
void demo_realtime_basic() {
    printf("\n=== 实时信号基本演示 ===\n");
    
    int rt_signal = SIGRTMIN;
    
    // 设置实时信号处理
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_sigaction = realtime_signal_handler;
    act.sa_flags = SA_SIGINFO;  // 必须设置以获取额外信息
    
    if (sigaction(rt_signal, &act, NULL) == -1) {
        perror("sigaction失败");
        exit(EXIT_FAILURE);
    }
    
    printf("实时信号 %d 处理已设置\n", rt_signal);
    printf("进程ID: %d\n", getpid());
    
    // 发送普通实时信号
    printf("\n1. 发送普通实时信号\n");
    kill(getpid(), rt_signal);
    
    // 发送带数据的实时信号
    printf("\n2. 发送带数据的实时信号\n");
    union sigval value;
    value.sival_int = 12345;
    
    if (sigqueue(getpid(), rt_signal, value) == -1) {
        perror("sigqueue失败");
    }
    
    // 发送多个信号演示排队
    printf("\n3. 发送多个实时信号演示排队\n");
    for (int i = 0; i < 5; i++) {
        value.sival_int = i * 100;
        sigqueue(getpid(), rt_signal, value);
    }
    
    // 等待所有信号处理完成
    sleep(2);
    printf("\n所有实时信号处理完成\n");
}

// 演示实时信号的优先级
void demo_realtime_priority() {
    printf("\n=== 实时信号优先级演示 ===\n");
    
    // 设置多个实时信号的处理
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_sigaction = realtime_signal_handler;
    act.sa_flags = SA_SIGINFO;
    
    int rt_signals[] = {SIGRTMIN, SIGRTMIN + 1, SIGRTMIN + 2};
    
    for (int i = 0; i < 3; i++) {
        sigaction(rt_signals[i], &act, NULL);
        printf("设置实时信号 %d 处理\n", rt_signals[i]);
    }
    
    printf("进程ID: %d\n", getpid());
    
    // 以不同顺序发送信号
    printf("\n以不同顺序发送信号:\n");
    
    // 先发送高编号信号
    union sigval value;
    value.sival_int = 300;
    sigqueue(getpid(), rt_signals[2], value);
    
    // 然后发送低编号信号
    value.sival_int = 100;
    sigqueue(getpid(), rt_signals[0], value);
    
    // 最后发送中间编号信号
    value.sival_int = 200;
    sigqueue(getpid(), rt_signals[1], value);
    
    // 注意:实时信号按照编号顺序处理,编号小的先处理
    // 所以处理顺序应该是:SIGRTMIN, SIGRTMIN+1, SIGRTMIN+2
    
    sleep(2);
}

// 演示实时信号的带宽限制
void demo_realtime_bandwidth() {
    printf("\n=== 实时信号带宽限制演示 ===\n");
    
    int rt_signal = SIGRTMIN + 3;
    int count = 0;
    struct timespec start_time, end_time;
    
    // 简单的计数处理函数
    void counting_handler(int signum, siginfo_t* info, void* context) {
        count++;
    }
    
    // 设置处理函数
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_sigaction = counting_handler;
    act.sa_flags = SA_SIGINFO;
    
    sigaction(rt_signal, &act, NULL);
    
    printf("测试实时信号处理能力\n");
    printf("进程ID: %d\n", getpid());
    
    // 获取开始时间
    clock_gettime(CLOCK_MONOTONIC, &start_time);
    
    // 发送大量信号
    int total_signals = 10000;
    printf("发送 %d 个实时信号...\n", total_signals);
    
    for (int i = 0; i < total_signals; i++) {
        union sigval value;
        value.sival_int = i;
        sigqueue(getpid(), rt_signal, value);
    }
    
    // 等待所有信号处理完成
    sleep(2);
    
    // 获取结束时间
    clock_gettime(CLOCK_MONOTONIC, &end_time);
    
    // 计算耗时
    double elapsed = (end_time.tv_sec - start_time.tv_sec) +
                   (end_time.tv_nsec - start_time.tv_nsec) / 1e9;
    
    printf("处理结果:\n");
    printf("  发送信号数: %d\n", total_signals);
    printf("  处理信号数: %d\n", count);
    printf("  总耗时: %.3f 秒\n", elapsed);
    printf("  处理速率: %.0f 信号/秒\n", count / elapsed);
    
    if (count < total_signals) {
        printf("警告:%d 个信号可能丢失或仍在队列中\n", total_signals - count);
    }
}

// 演示实时信号在多进程间的通信
void demo_realtime_interprocess() {
    printf("\n=== 实时信号进程间通信演示 ===\n");
    
    pid_t pid = fork();
    
    if (pid == 0) {
        // 子进程:接收信号
        int rt_signal = SIGRTMIN + 4;
        
        void child_handler(int signum, siginfo_t* info, void* context) {
            printf("子进程收到信号 %d\n", signum);
            printf("来自父进程: %d\n", info->si_pid);
            printf("信号值: %d\n", info->si_value.sival_int);
            
            // 回送确认信号
            union sigval value;
            value.sival_int = info->si_value.sival_int * 2;
            
            if (sigqueue(info->si_pid, rt_signal, value) == -1) {
                perror("子进程sigqueue失败");
            }
        }
        
        // 设置信号处理
        struct sigaction act;
        memset(&act, 0, sizeof(act));
        act.sa_sigaction = child_handler;
        act.sa_flags = SA_SIGINFO;
        
        sigaction(rt_signal, &act, NULL);
        
        printf("子进程启动,PID: %d\n", getpid());
        
        // 等待信号
        while(1) {
            pause();
        }
        
        exit(0);
    } else if (pid > 0) {
        // 父进程:发送信号
        sleep(1);  // 等待子进程准备
        
        int rt_signal = SIGRTMIN + 4;
        
        void parent_handler(int signum, siginfo_t* info, void* context) {
            printf("父进程收到确认信号\n");
            printf("来自子进程: %d\n", info->si_pid);
            printf("返回值: %d\n", info->si_value.sival_int);
        }
        
        // 设置父进程的信号处理
        struct sigaction act;
        memset(&act, 0, sizeof(act));
        act.sa_sigaction = parent_handler;
        act.sa_flags = SA_SIGINFO;
        
        sigaction(rt_signal, &act, NULL);
        
        printf("父进程启动,PID: %d\n", getpid());
        
        // 发送多个信号到子进程
        for (int i = 1; i <= 5; i++) {
            printf("\n父进程发送信号 %d\n", i);
            
            union sigval value;
            value.sival_int = i;
            
            if (sigqueue(pid, rt_signal, value) == -1) {
                perror("父进程sigqueue失败");
            }
            
            // 等待子进程回应
            sleep(1);
        }
        
        // 等待子进程结束
        sleep(2);
        kill(pid, SIGTERM);
        wait(NULL);
        
        printf("进程间通信演示完成\n");
    } else {
        perror("fork失败");
        exit(EXIT_FAILURE);
    }
}

// 演示实时信号与标准信号的比较
void demo_comparison() {
    printf("\n=== 实时信号与标准信号比较 ===\n");
    
    int standard_count = 0;
    int realtime_count = 0;
    
    // 标准信号处理函数
    void standard_handler(int signum) {
        standard_count++;
    }
    
    // 实时信号处理函数
    void realtime_handler(int signum, siginfo_t* info, void* context) {
        realtime_count++;
    }
    
    // 设置标准信号处理
    signal(SIGUSR1, standard_handler);
    
    // 设置实时信号处理
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_sigaction = realtime_handler;
    act.sa_flags = SA_SIGINFO;
    sigaction(SIGRTMIN, &act, NULL);
    
    printf("测试信号丢失情况\n");
    printf("进程ID: %d\n", getpid());
    
    // 快速发送标准信号
    printf("\n快速发送标准信号(SIGUSR1):\n");
    for (int i = 0; i < 1000; i++) {
        kill(getpid(), SIGUSR1);
    }
    
    // 快速发送实时信号
    printf("快速发送实时信号(SIGRTMIN):\n");
    for (int i = 0; i < 1000; i++) {
        union sigval value;
        value.sival_int = i;
        sigqueue(getpid(), SIGRTMIN, value);
    }
    
    // 等待处理完成
    sleep(2);
    
    printf("\n结果比较:\n");
    printf("  标准信号发送: 1000,接收: %d,丢失: %d\n", 
           standard_count, 1000 - standard_count);
    printf("  实时信号发送: 1000,接收: %d,丢失: %d\n", 
           realtime_count, 1000 - realtime_count);
    
    printf("\n结论:实时信号支持排队,不会丢失\n");
}

int main() {
    demo_realtime_basic();
    demo_realtime_priority();
    demo_realtime_bandwidth();
    demo_realtime_interprocess();
    demo_comparison();
    
    return 0;
}

第五章:信号捕捉的最佳实践

5.1 设计安全的信号处理程序

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

// 全局标志,使用volatile和sig_atomic_t确保原子性
volatile sig_atomic_t graceful_shutdown = 0;
volatile sig_atomic_t reload_config = 0;
volatile sig_atomic_t emergency_stop = 0;

// 日志函数(信号安全的简化版本)
void safe_log(const char* message) {
    // 使用write系统调用,它是异步信号安全的
    write(STDERR_FILENO, message, strlen(message));
    write(STDERR_FILENO, "\n", 1);
}

// 安全的信号处理函数
void safe_signal_handler(int signum) {
    const char* msg = NULL;
    
    switch(signum) {
        case SIGINT:
        case SIGTERM:
            msg = "接收到关闭信号,设置优雅关闭标志";
            graceful_shutdown = 1;
            break;
            
        case SIGHUP:
            msg = "接收到重载配置信号";
            reload_config = 1;
            break;
            
        case SIGUSR1:
            msg = "接收到紧急停止信号";
            emergency_stop = 1;
            break;
            
        case SIGUSR2:
            msg = "接收到状态查询信号";
            // 可以在这里设置状态查询标志
            break;
            
        default:
            // 未知信号,使用默认处理
            return;
    }
    
    if (msg != NULL) {
        safe_log(msg);
    }
}

// 初始化信号处理
void init_signal_handling() {
    struct sigaction sa;
    
    // 初始化结构体
    memset(&sa, 0, sizeof(sa));
    
    // 设置处理函数
    sa.sa_handler = safe_signal_handler;
    
    // 设置标志:自动重启被中断的系统调用
    sa.sa_flags = SA_RESTART;
    
    // 设置信号掩码:在处理信号时阻塞所有其他信号
    sigfillset(&sa.sa_mask);
    
    // 注册信号处理
    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);
    sigaction(SIGHUP, &sa, NULL);
    sigaction(SIGUSR1, &sa, NULL);
    sigaction(SIGUSR2, &sa, NULL);
    
    // 忽略某些信号
    signal(SIGPIPE, SIG_IGN);  // 忽略管道破裂信号
    signal(SIGCHLD, SIG_IGN);  // 忽略子进程退出信号
    
    safe_log("信号处理初始化完成");
}

// 主事件循环
void main_event_loop() {
    int iteration = 0;
    
    safe_log("主事件循环开始");
    
    while (!graceful_shutdown && !emergency_stop) {
        iteration++;
        
        // 检查配置重载标志
        if (reload_config) {
            safe_log("重新加载配置");
            reload_config = 0;  // 清除标志
            
            // 这里可以安全地重新加载配置
            // 注意:在信号处理函数中不能做复杂操作
        }
        
        // 主循环工作
        printf("主循环迭代 %d\n", iteration);
        
        // 模拟工作
        for (int i = 0; i < 1000000; i++) {
            // 模拟CPU工作
            asm volatile("" ::: "memory");
        }
        
        // 可以安全地进行I/O操作
        sleep(1);
        
        // 检查是否有挂起的信号
        sigset_t pending;
        sigpending(&pending);
        
        if (sigismember(&pending, SIGINT)) {
            safe_log("检测到未决的SIGINT信号");
        }
    }
    
    // 清理资源
    if (emergency_stop) {
        safe_log("执行紧急停止");
    } else {
        safe_log("执行优雅关闭");
    }
    
    // 这里可以安全地进行资源清理
    safe_log("清理资源...");
    sleep(2);  // 模拟清理时间
    safe_log("程序退出");
}

// 演示错误处理
void demo_error_handling() {
    printf("\n=== 信号处理中的错误处理 ===\n");
    
    // 1. 检查sigaction返回值
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = SIG_IGN;
    
    if (sigaction(SIGINT, &sa, NULL) == -1) {
        perror("sigaction失败");
        // 适当的错误处理
    }
    
    // 2. 处理信号掩码错误
    sigset_t mask;
    if (sigemptyset(&mask) == -1) {
        perror("sigemptyset失败");
    }
    
    if (sigaddset(&mask, SIGINT) == -1) {
        perror("sigaddset失败");
    }
    
    // 3. 检查sigprocmask错误
    sigset_t oldmask;
    if (sigprocmask(SIG_BLOCK, &mask, &oldmask) == -1) {
        perror("sigprocmask失败");
    }
}

// 演示资源清理
void demo_resource_cleanup() {
    printf("\n=== 信号处理中的资源清理 ===\n");
    
    // 文件描述符清理的示例
    int fd = open("/tmp/testfile", O_CREAT | O_WRONLY, 0644);
    if (fd == -1) {
        perror("打开文件失败");
        return;
    }
    
    // 设置退出时的清理函数
    void cleanup_file(void) {
        safe_log("清理文件资源");
        if (fd != -1) {
            close(fd);
            unlink("/tmp/testfile");
        }
    }
    
    // 注册清理函数(注意:atexit不是信号安全的)
    atexit(cleanup_file);
    
    // 写入数据
    write(fd, "测试数据\n", 9);
    
    printf("文件已创建和写入,程序退出时将自动清理\n");
}

// 演示多线程环境下的信号处理
void demo_multithread_safety() {
    printf("\n=== 多线程信号安全 ===\n");
    
    // 在所有线程中阻塞信号
    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGTERM);
    
    // 设置线程信号掩码
    pthread_sigmask(SIG_BLOCK, &mask, NULL);
    
    printf("在所有线程中阻塞了SIGINT和SIGTERM\n");
    
    // 创建专门的信号处理线程
    pthread_t sig_thread;
    
    void* signal_thread_func(void* arg) {
        printf("信号处理线程启动\n");
        
        int sig;
        while (1) {
            // 同步等待信号
            sigwait(&mask, &sig);
            
            printf("信号处理线程收到信号: %d\n", sig);
            
            // 这里可以安全地处理信号
            // 因为在线程上下文中,不是信号处理函数
        }
        
        return NULL;
    }
    
    pthread_create(&sig_thread, NULL, signal_thread_func, NULL);
    
    printf("专门的信号处理线程已创建\n");
    printf("这避免了在信号处理函数中执行复杂操作的问题\n");
}

int main() {
    printf("=== 信号处理最佳实践演示 ===\n");
    
    init_signal_handling();
    demo_error_handling();
    demo_resource_cleanup();
    demo_multithread_safety();
    
    // 运行主事件循环
    main_event_loop();
    
    return 0;
}

5.2 避免常见陷阱

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>

// 常见陷阱1:在信号处理函数中使用不可重入函数
void trap1_unsafe_handler(int signum) {
    printf("不安全:在信号处理函数中使用printf\n");  // 陷阱!
    fflush(stdout);  // 更不安全!
}

// 常见陷阱2:信号处理函数中调用非异步信号安全函数
void trap2_unsafe_memory_handler(int signum) {
    char* buffer = (char*)malloc(100);  // 陷阱:使用malloc
    if (buffer) {
        strcpy(buffer, "不安全");  // 陷阱:使用strcpy
        free(buffer);  // 陷阱:使用free
    }
}

// 常见陷阱3:忽略信号处理后的重置
void trap3_reset_handler(int signum) {
    printf("处理信号 %d\n", signum);
    // 某些系统在信号处理后会将处理函数重置为默认
    // 需要手动重新注册
}

// 常见陷阱4:信号竞争条件
volatile int trap4_counter = 0;

void trap4_race_handler(int signum) {
    trap4_counter++;  // 潜在的数据竞争!
}

// 常见陷阱5:死锁可能性
pthread_mutex_t trap5_mutex = PTHREAD_MUTEX_INITIALIZER;

void trap5_deadlock_handler(int signum) {
    pthread_mutex_lock(&trap5_mutex);  // 可能死锁!
    printf("获取锁\n");
    sleep(2);
    pthread_mutex_unlock(&trap5_mutex);
}

// 常见陷阱6:在信号处理函数中调用exit()
void trap6_exit_handler(int signum) {
    printf("调用exit()退出\n");
    exit(1);  // 陷阱:exit()不是异步信号安全的
    // 应该使用_exit()或设置标志在主循环中退出
}

// 避免陷阱的正确做法
volatile sig_atomic_t correct_flag = 0;

void correct_handler(int signum) {
    // 只设置标志位,不做其他操作
    correct_flag = 1;
    
    // 可以安全地使用write
    const char* msg = "信号已接收\n";
    write(STDERR_FILENO, msg, strlen(msg));
}

// 演示如何避免陷阱1:使用异步信号安全函数
void avoid_trap1() {
    printf("\n=== 避免陷阱1:使用异步信号安全函数 ===\n");
    
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = correct_handler;
    
    sigaction(SIGUSR1, &sa, NULL);
    
    printf("安全的信号处理函数已设置\n");
    printf("使用write而不是printf\n");
    
    raise(SIGUSR1);
}

// 演示如何避免陷阱2:避免动态内存分配
void avoid_trap2() {
    printf("\n=== 避免陷阱2:避免动态内存分配 ===\n");
    
    // 使用预先分配的缓冲区
    static char safe_buffer[256];
    volatile sig_atomic_t buffer_ready = 0;
    
    void safe_handler(int signum) {
        // 只设置标志,在主循环中处理
        buffer_ready = 1;
    }
    
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = safe_handler;
    
    sigaction(SIGUSR2, &sa, NULL);
    
    printf("安全的处理模式:信号处理函数只设置标志\n");
    printf("复杂的操作在主循环中执行\n");
}

// 演示如何避免陷阱3:正确管理信号处理函数
void avoid_trap3() {
    printf("\n=== 避免陷阱3:正确管理信号处理函数 ===\n");
    
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    
    // 使用sigaction而不是signal
    // sigaction默认不会重置处理函数
    
    // 或者使用SA_RESETHAND标志控制行为
    sa.sa_flags = SA_RESETHAND;  // 处理一次后重置为默认
    
    printf("使用sigaction并适当设置标志\n");
}

// 演示如何避免陷阱4:避免竞争条件
void avoid_trap4() {
    printf("\n=== 避免陷阱4:避免竞争条件 ===\n");
    
    // 使用sig_atomic_t类型
    volatile sig_atomic_t safe_counter = 0;
    
    void safe_counter_handler(int signum) {
        safe_counter++;  // sig_atomic_t保证原子性
    }
    
    // 或者使用原子操作(如果可用)
    // __atomic_fetch_add(&counter, 1, __ATOMIC_SEQ_CST);
    
    printf("使用sig_atomic_t类型保证原子性\n");
}

// 演示如何避免陷阱5:避免死锁
void avoid_trap5() {
    printf("\n=== 避免陷阱5:避免死锁 ===\n");
    
    // 在信号处理函数中不要获取锁
    // 使用无锁数据结构或标志位
    
    volatile sig_atomic_t need_lock_operation = 0;
    
    void no_lock_handler(int signum) {
        need_lock_operation = 1;  // 只设置标志
    }
    
    printf("在信号处理函数中避免使用锁\n");
    printf("使用标志位在主循环中执行需要锁的操作\n");
}

// 演示如何避免陷阱6:安全退出
void avoid_trap6() {
    printf("\n=== 避免陷阱6:安全退出 ===\n");
    
    volatile sig_atomic_t shutdown_requested = 0;
    
    void safe_exit_handler(int signum) {
        shutdown_requested = 1;  // 只设置标志
        
        // 如果需要立即退出,使用_exit()
        // _exit(EXIT_FAILURE);  // 这是安全的
    }
    
    printf("使用标志位控制优雅退出\n");
    printf("或使用_exit()立即退出\n");
}

// 综合示例:安全的信号处理框架
typedef struct {
    volatile sig_atomic_t shutdown_requested;
    volatile sig_atomic_t reload_requested;
    volatile sig_atomic_t user_signal_received;
    int user_signal_number;
} SignalState;

SignalState signal_state = {0, 0, 0, 0};

void master_signal_handler(int signum) {
    switch(signum) {
        case SIGINT:
        case SIGTERM:
            signal_state.shutdown_requested = 1;
            break;
            
        case SIGHUP:
            signal_state.reload_requested = 1;
            break;
            
        default:
            if (signum >= SIGRTMIN && signum <= SIGRTMAX) {
                signal_state.user_signal_received = 1;
                signal_state.user_signal_number = signum;
            }
            break;
    }
    
    // 安全地记录信号接收
    char buffer[50];
    char* pos = buffer;
    
    // 手动构建消息(避免sprintf)
    const char* prefix = "收到信号: ";
    const char* suffix = "\n";
    
    memcpy(pos, prefix, strlen(prefix));
    pos += strlen(prefix);
    
    // 简单地将数字转换为字符串
    int n = signum;
    if (n == 0) {
        *pos++ = '0';
    } else {
        char rev[10];
        int i = 0;
        while (n > 0) {
            rev[i++] = '0' + (n % 10);
            n /= 10;
        }
        while (--i >= 0) {
            *pos++ = rev[i];
        }
    }
    
    memcpy(pos, suffix, strlen(suffix));
    pos += strlen(suffix);
    
    write(STDERR_FILENO, buffer, pos - buffer);
}

void setup_safe_signal_handling() {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    
    sa.sa_handler = master_signal_handler;
    sa.sa_flags = SA_RESTART;
    
    // 阻塞所有其他信号
    sigfillset(&sa.sa_mask);
    
    // 注册信号
    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);
    sigaction(SIGHUP, &sa, NULL);
    
    // 注册实时信号
    for (int sig = SIGRTMIN; sig <= SIGRTMIN + 5; sig++) {
        sigaction(sig, &sa, NULL);
    }
    
    printf("安全的信号处理框架已设置\n");
}

int main() {
    printf("=== 避免信号处理常见陷阱 ===\n");
    
    avoid_trap1();
    avoid_trap2();
    avoid_trap3();
    avoid_trap4();
    avoid_trap5();
    avoid_trap6();
    
    setup_safe_signal_handling();
    
    printf("\n安全的信号处理框架正在运行\n");
    printf("进程ID: %d\n", getpid());
    printf("可以发送各种信号测试\n");
    
    // 主循环
    while (!signal_state.shutdown_requested) {
        // 检查并处理信号标志
        if (signal_state.reload_requested) {
            printf("处理配置重载请求\n");
            signal_state.reload_requested = 0;
        }
        
        if (signal_state.user_signal_received) {
            printf("处理用户信号: %d\n", signal_state.user_signal_number);
            signal_state.user_signal_received = 0;
        }
        
        // 主循环工作
        printf(".");
        fflush(stdout);
        sleep(1);
    }
    
    printf("\n接收到关闭信号,优雅退出\n");
    
    return 0;
}

第六章:示例:完整的信号处理程序

6.1 综合示例:一个健壮的服务器信号处理器

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <syslog.h>

// ============================================================================
// 配置部分
// ============================================================================

// 程序状态
typedef enum {
    STATE_RUNNING,
    STATE_RELOADING,
    STATE_SHUTTING_DOWN,
    STATE_EMERGENCY_STOP
} ProgramState;

// 信号管理器结构
typedef struct {
    volatile ProgramState state;
    volatile sig_atomic_t signal_received;
    int last_signal;
    time_t start_time;
    pthread_mutex_t state_mutex;
    pthread_cond_t state_cond;
} SignalManager;

// ============================================================================
// 全局变量
// ============================================================================

static SignalManager signal_manager;
static pthread_t signal_thread_id;
static int log_fd = -1;

// ============================================================================
// 日志系统(信号安全版本)
// ============================================================================

// 初始化日志系统
int init_logging() {
    // 打开系统日志
    openlog("signal-server", LOG_PID | LOG_CONS, LOG_DAEMON);
    
    // 同时记录到文件
    log_fd = open("/var/tmp/signal-server.log", 
                  O_WRONLY | O_CREAT | O_APPEND, 0644);
    if (log_fd == -1) {
        // 如果无法打开文件,使用stderr
        log_fd = STDERR_FILENO;
    }
    
    return 0;
}

// 安全的日志记录函数
void safe_log(int level, const char* format, ...) {
    char buffer[1024];
    time_t now;
    struct tm tm_info;
    va_list args;
    
    // 获取当前时间
    time(&now);
    localtime_r(&now, &tm_info);
    
    // 格式化时间
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S ", &tm_info);
    int len = strlen(buffer);
    
    // 添加日志级别
    const char* level_str;
    switch(level) {
        case LOG_EMERG:   level_str = "[EMERG] "; break;
        case LOG_ALERT:   level_str = "[ALERT] "; break;
        case LOG_CRIT:    level_str = "[CRIT] "; break;
        case LOG_ERR:     level_str = "[ERROR] "; break;
        case LOG_WARNING: level_str = "[WARN] "; break;
        case LOG_NOTICE:  level_str = "[NOTICE] "; break;
        case LOG_INFO:    level_str = "[INFO] "; break;
        case LOG_DEBUG:   level_str = "[DEBUG] "; break;
        default:          level_str = "[UNKNOWN] "; break;
    }
    
    strcat(buffer, level_str);
    len = strlen(buffer);
    
    // 格式化消息
    va_start(args, format);
    vsnprintf(buffer + len, sizeof(buffer) - len, format, args);
    va_end(args);
    
    // 添加换行符
    strcat(buffer, "\n");
    
    // 写入日志
    write(log_fd, buffer, strlen(buffer));
    
    // 同时写入系统日志
    syslog(level, "%s", buffer + len);
}

// ============================================================================
// 信号处理函数
// ============================================================================

// 信号处理线程函数
void* signal_processing_thread(void* arg) {
    sigset_t mask;
    int sig;
    
    safe_log(LOG_INFO, "信号处理线程启动,线程ID: %lu", pthread_self());
    
    // 等待的信号集
    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGTERM);
    sigaddset(&mask, SIGHUP);
    sigaddset(&mask, SIGUSR1);
    sigaddset(&mask, SIGUSR2);
    sigaddset(&mask, SIGQUIT);
    
    // 添加实时信号
    for (int i = SIGRTMIN; i <= SIGRTMIN + 10; i++) {
        sigaddset(&mask, i);
    }
    
    while (signal_manager.state != STATE_EMERGENCY_STOP) {
        // 等待信号
        if (sigwait(&mask, &sig) != 0) {
            safe_log(LOG_ERR, "sigwait失败: %s", strerror(errno));
            continue;
        }
        
        safe_log(LOG_INFO, "接收到信号: %d (%s)", sig, strsignal(sig));
        
        // 处理信号
        pthread_mutex_lock(&signal_manager.state_mutex);
        
        signal_manager.last_signal = sig;
        signal_manager.signal_received = 1;
        
        switch(sig) {
            case SIGINT:
            case SIGTERM:
                safe_log(LOG_NOTICE, "接收到终止信号,开始优雅关闭");
                signal_manager.state = STATE_SHUTTING_DOWN;
                break;
                
            case SIGHUP:
                safe_log(LOG_NOTICE, "接收到重载配置信号");
                signal_manager.state = STATE_RELOADING;
                break;
                
            case SIGUSR1:
                safe_log(LOG_INFO, "接收到状态查询信号");
                // 状态查询不改变主状态
                break;
                
            case SIGUSR2:
                safe_log(LOG_INFO, "接收到调试信号");
                // 触发调试信息输出
                break;
                
            case SIGQUIT:
                safe_log(LOG_WARNING, "接收到退出信号,紧急停止");
                signal_manager.state = STATE_EMERGENCY_STOP;
                break;
                
            default:
                // 处理实时信号
                if (sig >= SIGRTMIN && sig <= SIGRTMAX) {
                    safe_log(LOG_DEBUG, "接收到实时信号: %d", sig);
                    // 实时信号处理逻辑
                }
                break;
        }
        
        // 通知等待状态变化的线程
        pthread_cond_broadcast(&signal_manager.state_cond);
        pthread_mutex_unlock(&signal_manager.state_mutex);
    }
    
    safe_log(LOG_INFO, "信号处理线程退出");
    return NULL;
}

// ============================================================================
// 初始化函数
// ============================================================================

// 初始化信号管理器
int init_signal_manager() {
    // 初始化信号管理器
    memset(&signal_manager, 0, sizeof(signal_manager));
    signal_manager.state = STATE_RUNNING;
    signal_manager.start_time = time(NULL);
    
    // 初始化互斥锁和条件变量
    pthread_mutex_init(&signal_manager.state_mutex, NULL);
    pthread_cond_init(&signal_manager.state_cond, NULL);
    
    // 在主线程中阻塞所有信号
    sigset_t all_signals;
    sigfillset(&all_signals);
    pthread_sigmask(SIG_BLOCK, &all_signals, NULL);
    
    // 创建信号处理线程
    if (pthread_create(&signal_thread_id, NULL, 
                       signal_processing_thread, NULL) != 0) {
        safe_log(LOG_ERR, "创建信号处理线程失败: %s", strerror(errno));
        return -1;
    }
    
    safe_log(LOG_INFO, "信号管理器初始化完成");
    return 0;
}

// ============================================================================
// 服务器主循环
// ============================================================================

// 工作线程函数
void* worker_thread(void* arg) {
    int thread_id = *(int*)arg;
    
    safe_log(LOG_INFO, "工作线程 %d 启动", thread_id);
    
    while (1) {
        pthread_mutex_lock(&signal_manager.state_mutex);
        
        // 检查程序状态
        while (signal_manager.state == STATE_RELOADING) {
            safe_log(LOG_INFO, "工作线程 %d 等待重载完成", thread_id);
            pthread_cond_wait(&signal_manager.state_cond, 
                            &signal_manager.state_mutex);
        }
        
        if (signal_manager.state == STATE_SHUTTING_DOWN ||
            signal_manager.state == STATE_EMERGENCY_STOP) {
            pthread_mutex_unlock(&signal_manager.state_mutex);
            break;
        }
        
        pthread_mutex_unlock(&signal_manager.state_mutex);
        
        // 执行工作
        safe_log(LOG_DEBUG, "工作线程 %d 执行任务", thread_id);
        
        // 模拟工作负载
        struct timespec sleep_time = {0, 100000000};  // 100ms
        nanosleep(&sleep_time, NULL);
        
        // 定期检查信号
        if (signal_manager.signal_received) {
            safe_log(LOG_DEBUG, "工作线程 %d 检测到信号", thread_id);
            // 可以在这里处理线程特定的信号逻辑
        }
    }
    
    safe_log(LOG_INFO, "工作线程 %d 退出", thread_id);
    return NULL;
}

// 配置重载函数
void reload_configuration() {
    safe_log(LOG_NOTICE, "开始重载配置");
    
    // 模拟配置重载
    sleep(2);
    
    safe_log(LOG_NOTICE, "配置重载完成");
}

// 状态报告函数
void report_status() {
    time_t now = time(NULL);
    time_t uptime = now - signal_manager.start_time;
    
    char buffer[256];
    snprintf(buffer, sizeof(buffer),
             "服务器状态报告:\n"
             "  运行状态: %s\n"
             "  运行时间: %ld秒\n"
             "  最后信号: %d\n"
             "  工作线程: 活跃\n",
             signal_manager.state == STATE_RUNNING ? "运行中" :
             signal_manager.state == STATE_RELOADING ? "重载中" :
             signal_manager.state == STATE_SHUTTING_DOWN ? "关闭中" :
             "紧急停止",
             uptime,
             signal_manager.last_signal);
    
    write(STDOUT_FILENO, buffer, strlen(buffer));
}

// 主服务器循环
void server_main_loop() {
    pthread_t workers[5];
    int thread_ids[5] = {1, 2, 3, 4, 5};
    
    safe_log(LOG_NOTICE, "服务器主循环启动");
    
    // 创建工作线程
    for (int i = 0; i < 5; i++) {
        pthread_create(&workers[i], NULL, worker_thread, &thread_ids[i]);
    }
    
    // 主循环
    while (signal_manager.state != STATE_SHUTTING_DOWN &&
           signal_manager.state != STATE_EMERGENCY_STOP) {
        
        // 检查是否需要重载配置
        if (signal_manager.state == STATE_RELOADING) {
            reload_configuration();
            
            pthread_mutex_lock(&signal_manager.state_mutex);
            signal_manager.state = STATE_RUNNING;
            pthread_cond_broadcast(&signal_manager.state_cond);
            pthread_mutex_unlock(&signal_manager.state_mutex);
        }
        
        // 检查状态查询信号
        if (signal_manager.last_signal == SIGUSR1) {
            report_status();
            signal_manager.last_signal = 0;
        }
        
        // 主循环工作
        safe_log(LOG_DEBUG, "主循环运行中...");
        
        // 等待一段时间或条件变量
        pthread_mutex_lock(&signal_manager.state_mutex);
        struct timespec timeout;
        clock_gettime(CLOCK_REALTIME, &timeout);
        timeout.tv_sec += 5;  // 5秒超时
        
        pthread_cond_timedwait(&signal_manager.state_cond, 
                              &signal_manager.state_mutex, &timeout);
        pthread_mutex_unlock(&signal_manager.state_mutex);
    }
    
    // 等待工作线程结束
    safe_log(LOG_NOTICE, "等待工作线程结束...");
    for (int i = 0; i < 5; i++) {
        pthread_join(workers[i], NULL);
    }
    
    // 等待信号处理线程结束
    pthread_join(signal_thread_id, NULL);
    
    safe_log(LOG_NOTICE, "所有线程已结束");
}

// ============================================================================
// 清理函数
// ============================================================================

void cleanup_resources() {
    safe_log(LOG_INFO, "清理资源");
    
    // 关闭日志文件
    if (log_fd != -1 && log_fd != STDERR_FILENO) {
        close(log_fd);
    }
    
    // 关闭系统日志
    closelog();
    
    // 销毁互斥锁和条件变量
    pthread_mutex_destroy(&signal_manager.state_mutex);
    pthread_cond_destroy(&signal_manager.state_cond);
    
    safe_log(LOG_NOTICE, "资源清理完成");
}

// ============================================================================
// 主函数
// ============================================================================

int main(int argc, char* argv[]) {
    printf("=== 健壮的服务器信号处理器示例 ===\n");
    printf("进程ID: %d\n", getpid());
    printf("使用以下命令测试:\n");
    printf("  优雅关闭: kill -TERM %d 或 Ctrl+C\n", getpid());
    printf("  重载配置: kill -HUP %d\n", getpid());
    printf("  状态查询: kill -USR1 %d\n", getpid());
    printf("  调试模式: kill -USR2 %d\n", getpid());
    printf("  紧急停止: kill -QUIT %d\n", getpid());
    printf("  实时信号: kill -%d %d\n", SIGRTMIN, getpid());
    printf("\n");
    
    // 初始化日志系统
    if (init_logging() != 0) {
        fprintf(stderr, "初始化日志系统失败\n");
        return EXIT_FAILURE;
    }
    
    safe_log(LOG_NOTICE, "服务器启动,进程ID: %d", getpid());
    
    // 初始化信号管理器
    if (init_signal_manager() != 0) {
        safe_log(LOG_ERR, "初始化信号管理器失败");
        cleanup_resources();
        return EXIT_FAILURE;
    }
    
    // 运行服务器主循环
    server_main_loop();
    
    // 清理资源
    cleanup_resources();
    
    safe_log(LOG_NOTICE, "服务器正常退出");
    
    return EXIT_SUCCESS;
}

6.2 编译和运行说明

makefile 复制代码
# Makefile for signal server example
CC = gcc
CFLAGS = -Wall -Wextra -O2 -pthread -D_GNU_SOURCE
TARGET = signal_server
SOURCES = signal_server.c
LIBS = -lpthread

all: $(TARGET)

$(TARGET): $(SOURCES)
	$(CC) $(CFLAGS) -o $(TARGET) $(SOURCES) $(LIBS)

debug: $(SOURCES)
	$(CC) $(CFLAGS) -g -DDEBUG -o $(TARGET)_debug $(SOURCES) $(LIBS)

clean:
	rm -f $(TARGET) $(TARGET)_debug

install: $(TARGET)
	cp $(TARGET) /usr/local/bin/

test: $(TARGET)
	./$(TARGET) &

.PHONY: all debug clean install test
bash 复制代码
#!/bin/bash
# 测试脚本:test_signals.sh

SERVER_PID=$1

if [ -z "$SERVER_PID" ]; then
    echo "用法: $0 <服务器进程ID>"
    exit 1
fi

echo "测试信号处理器 - 服务器PID: $SERVER_PID"
echo "========================================"

# 测试各种信号
signals=("SIGHUP" "SIGUSR1" "SIGUSR2" "SIGRTMIN" "SIGTERM")

for sig in "${signals[@]}"; do
    echo -n "发送 $sig... "
    kill -$sig $SERVER_PID 2>/dev/null
    if [ $? -eq 0 ]; then
        echo "成功"
    else
        echo "失败"
    fi
    sleep 2
done

echo "测试完成"

总结

Linux信号机制是一个强大而复杂的系统特性,通过本文的详细探讨,我们深入了解了:

  1. 信号的基本概念:信号是进程间通信的异步机制,用于通知进程发生了特定事件。

  2. 信号捕捉机制

    • signal()函数提供简单的信号处理,但存在可移植性问题
    • sigaction()函数是现代程序推荐使用的信号处理接口,提供更强大的控制能力
  3. 关键特性

    • 可重入性:信号处理函数必须使用异步信号安全函数
    • 信号屏蔽:可以临时阻塞信号,保护关键代码段
    • 可靠性:实时信号支持排队,不会丢失
    • 处理期间的屏蔽:系统自动防止信号处理函数的递归调用
  4. 高级技术

    • 使用sigwait()同步处理信号,特别适合多线程程序
    • 信号栈可以防止栈溢出
    • 实时信号提供可靠通信和额外数据传递
  5. 最佳实践

    • 信号处理函数应尽可能简单,只设置标志位
    • 复杂处理应在主循环中进行
    • 避免在信号处理函数中使用非异步信号安全函数
    • 正确处理信号竞争条件和资源清理
  6. 完整示例:展示了如何构建一个健壮的服务器信号处理器,包括线程安全的信号处理、配置重载、优雅关闭等功能。

掌握Linux信号处理对于编写稳定、可靠的系统程序至关重要。正确的信号处理可以确保程序在异常情况下优雅地恢复或关闭,避免数据损坏和资源泄漏。随着Linux系统的发展,信号机制仍然是进程控制和进程间通信的重要工具,值得每个Linux开发者深入学习和掌握。

通过本文的学习,读者应该能够:

  • 理解Linux信号机制的工作原理
  • 选择合适的信号处理策略
  • 避免常见的信号处理陷阱
  • 设计健壮的信号处理框架
  • 在实际项目中应用信号处理技术

信号处理是Linux系统编程的艺术之一,需要结合理论知识和实践经验。希望本文能为读者提供全面的指导,帮助大家在日常开发中更好地利用这一强大的机制。

相关推荐
123过去14 小时前
ike-scan使用教程
linux·测试工具
ywf121514 小时前
前端的dist包放到后端springboot项目下一起打包
前端·spring boot·后端
恋猫de小郭14 小时前
2026,Android Compose 终于支持 Hot Reload 了,但是收费
android·前端·flutter
hpoenixf20 小时前
2026 年前端面试问什么
前端·面试
还是大剑师兰特20 小时前
Vue3 中的 defineExpose 完全指南
前端·javascript·vue.js
疯狂吧小飞牛20 小时前
GPG基础指令
linux·服务器·网络
C++ 老炮儿的技术栈20 小时前
volatile使用场景
linux·服务器·c语言·开发语言·c++
泯泷21 小时前
阶段一:从 0 看懂 JSVMP 架构,先在脑子里搭出一台最小 JSVM
前端·javascript·架构
AI科技星21 小时前
全尺度角速度统一:基于 v ≡ c 的纯推导与验证
c语言·开发语言·人工智能·opencv·算法·机器学习·数据挖掘
hjxu201621 小时前
【OpenClaw 龙虾养成笔记一】在远程服务器,使用Docker安装OpenClaw
服务器·笔记·docker