Skip to content

C 标准库 - <signal.h>

概述

<signal.h> 头文件提供了信号处理机制,允许程序响应异步事件,如中断、异常和系统事件。信号是一种进程间通信和异常处理的重要机制。

信号类型

标准信号

c
#define SIGINT   2
#define SIGILL   4
#define SIGFPE   8
#define SIGSEGV  11
#define SIGTERM  15
#define SIGABRT  6

SIGINT

中断信号(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 语言中处理异步事件的重要工具:

  1. 功能强大 - 可以处理各种系统事件和异常
  2. 灵活性高 - 可以自定义信号处理函数
  3. 应用广泛 - 用于进程控制、错误处理、定时器等

记住:

  • 在信号处理函数中只调用信号安全函数
  • 使用 volatile sig_atomic_t 类型的全局变量
  • 避免在信号处理函数中调用不可重入函数
  • 在关键操作期间屏蔽信号