操作系统可向进程发送信号(signal),通知异步事件,例如用户按下 Ctrl+C(SIGINT)或发生段错误(SIGSEGV)。C 程序可通过注册处理函数改变信号的默认行为,实现优雅退出或资源清理;这与错误处理、长期运行服务中的关闭逻辑相关。

信号的概念

信号是软件层面的中断通知,由内核或同机其他进程发出。每种信号对应一个编号,例如 SIGINT 在 POSIX 系统中通常为 2。

默认处置

未自定义处理时,常见处置包括忽略、终止进程或停止/继续执行。下列循环在默认情况下收到 SIGINT 会终止进程:

#include <stdio.h>

int main(void) {
    while (1) {
        printf("hello world\n");
    }
    return 0;
}

注册信号处理函数

除 SIGKILL 等少数信号外,多数信号可捕获。处理函数原型为 void handler(int sig),可用 signal 注册(新代码更推荐 sigaction,可移植性与语义更明确):

signal(type, signalHandler);
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

void signalHandler(int sig) {
    printf("Caught signal: %d\n", sig);
    exit(sig);
}

int main(void) {
    signal(SIGINT, signalHandler);

    while (1) {
        printf("Hello World!\n");
    }
    return 0;
}

运行中按 Ctrl+C 将执行自定义处理逻辑而非立即采用默认终止(本例中处理函数内调用 exit)。

raise():向本进程发信号

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void signalHandler(int sig) {
    printf("Interrupt handled: %d\n", sig);
    exit(sig);
}

int main(void) {
    signal(SIGINT, signalHandler);

    raise(SIGINT);
    return 0;
}

kill():向指定进程发信号

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

void handle_signal(int sig) {
    printf("Received signal: %d\n", sig);
}

int main(void) {
    signal(SIGINT, handle_signal);

    pid_t pid = getpid();
    kill(pid, SIGINT);
    return 0;
}

kill 向进程发送信号,具体效应取决于信号类型与进程是否已注册处理函数。

常见信号

信号含义默认行为
SIGABRT异常中止(abort)终止
SIGFPE浮点异常终止
SIGILL非法指令终止
SIGINT终端中断(Ctrl+C)终止
SIGSEGV无效内存访问终止
SIGTERM终止请求终止
SIGKILL强制终止终止(不可捕获)
SIGQUIT退出并可能生成核心转储终止
SIGCHLD子进程状态改变忽略或通知父进程
SIGCONT继续执行已停止进程继续
SIGTSTP终端停止(Ctrl+Z)停止

实现层面的注意点

  • 信号可能处于待处理状态,直至进程从可中断的系统调用返回后再递送。
  • 可在临界区暂时阻塞特定信号(信号掩码)。
  • 处理函数在独立上下文中执行,仅应调用异步信号安全函数;复杂清理宜通过标志位在主循环中完成。

本节介绍了信号的默认行为与自定义处理。下一节概述 POSIX 线程(多线程)的基本 API。