目录

  1. 信号处理概述
  2. 信号的类型
  3. 信号的处理方式
  4. 安装信号处理函数
  5. 使用 signal() 函数
  6. 信号的常见应用
  7. 示例代码
  8. 参考资料

1. 信号处理概述

在 C++ 中,信号处理是指程序在执行过程中通过接收信号来响应特定的事件或异常。信号是一种异步的机制,通常由操作系统或硬件触发,传递给程序的运行进程。信号的主要作用是在程序运行时能够对某些事件作出反应,比如终止进程、处理中断等。

信号处理是通过捕获信号来实现的,程序通过定义相应的信号处理函数来响应这些信号。在 Unix-like 系统(如 Linux、macOS)中,信号处理是通过 signal()sigaction() 函数来完成的。


2. 信号的类型

常见的信号类型包括:

  • SIGINT:由用户键入 Ctrl+C 时触发,通常用于中断程序执行。
  • SIGTERM:程序请求正常终止时触发,通常用于外部进程终止当前程序。
  • SIGSEGV:当程序发生段错误(访问非法内存地址)时触发,通常导致程序崩溃。
  • SIGFPE:当程序执行除零或溢出等数学错误时触发。
  • SIGILL:当程序执行非法指令时触发,通常表示程序代码损坏或试图执行不支持的操作。
  • SIGUSR1, SIGUSR2:用户自定义信号,允许程序自定义处理的信号。

3. 信号的处理方式

C++ 中的信号处理有三种方式:

  1. 默认处理:每个信号都默认有一个处理方式。比如 SIGINT 默认会终止程序,SIGSEGV 会使程序崩溃。
  2. 忽略信号:可以通过信号处理函数将信号忽略,从而避免默认处理。SIG_IGN 可以用来忽略信号。
  3. 自定义处理函数:可以通过信号处理函数自定义信号的处理方式,捕获信号并执行自定义的响应操作。

4. 安装信号处理函数

为了捕获和处理信号,程序需要使用 signal()sigaction() 函数来注册一个信号处理函数。信号处理函数会在相应信号发生时被调用。

signal() 函数

signal() 函数用于设置信号的处理程序,语法如下:

#include <csignal>

void signal_handler(int signal) {
    // 处理信号的代码
}

int main() {
    signal(SIGINT, signal_handler);  // 注册 SIGINT 的处理函数
    while (true) {
        // 程序的主要逻辑
    }
    return 0;
}

sigaction() 函数

sigaction() 是一个更为复杂和更强大的函数,允许更细致地控制信号的处理行为,如设置信号掩码、阻塞信号等。它在现代的 Unix-like 系统中比 signal() 更常用。

#include <csignal>
#include <iostream>

void signal_handler(int signal) {
    std::cout << "Caught signal " << signal << std::endl;
}

int main() {
    struct sigaction sa;
    sa.sa_handler = signal_handler;
    sa.sa_flags = 0;
    sigemptyset(&sa.sa_mask);
    
    sigaction(SIGINT, &sa, NULL);  // 注册 SIGINT 的处理函数

    while (true) {
        // 程序的主要逻辑
    }
    return 0;
}


5. 使用 signal() 函数

在 C++ 中,signal() 函数用于注册信号处理函数。它的原型如下:

void (*signal(int sig, void (*func)(int)))(int);

  • sig:需要处理的信号,如 SIGINT, SIGTERM, SIGSEGV 等。
  • func:信号处理函数,接收一个整数参数(表示接收到的信号)。

示例:

#include <iostream>
#include <csignal>

void signal_handler(int signal) {
    std::cout << "Received signal " << signal << std::endl;
    if (signal == SIGINT) {
        std::cout << "Interrupt signal received, terminating the program." << std::endl;
        exit(0);
    }
}

int main() {
    signal(SIGINT, signal_handler);  // 捕获 SIGINT 信号并执行处理函数

    std::cout << "Running program. Press Ctrl+C to stop..." << std::endl;
    while (true) {
        // 主程序逻辑
    }

    return 0;
}

解释:

  • 当用户按下 Ctrl+C 发送 SIGINT 信号时,程序会调用 signal_handler() 函数,输出信号信息,并正常终止程序。

6. 信号的常见应用

信号处理常用于以下场景:

  • 优雅地关闭程序:在程序收到 SIGTERMSIGINT 等信号时,执行清理操作并优雅地退出。
  • 捕获错误信息:处理如 SIGSEGV 等信号,记录错误信息或执行其他恢复操作。
  • 定时任务:使用 SIGALRM 信号来设置定时器,定时执行某些任务。
  • 外部进程通知:使用 SIGUSR1SIGUSR2 等用户定义的信号,接收外部进程的通知。

7. 示例代码

示例 1:捕获 SIGINT 信号

#include <iostream>
#include <csignal>
#include <cstdlib>

void signal_handler(int signal) {
    if (signal == SIGINT) {
        std::cout << "Caught SIGINT! Terminating the program." << std::endl;
        exit(0);
    }
}

int main() {
    signal(SIGINT, signal_handler);  // 注册信号处理函数

    while (true) {
        std::cout << "Running... Press Ctrl+C to stop." << std::endl;
        sleep(1);
    }

    return 0;
}

示例 2:捕获 SIGSEGV(段错误)

#include <iostream>
#include <csignal>

void signal_handler(int signal) {
    std::cout << "Caught signal " << signal << std::endl;
    if (signal == SIGSEGV) {
        std::cout << "Segmentation fault occurred!" << std::endl;
    }
}

int main() {
    signal(SIGSEGV, signal_handler);  // 注册 SIGSEGV 信号处理函数

    int *ptr = nullptr;
    *ptr = 42;  // 引发段错误

    return 0;
}

解释:

  • 第一个示例演示了如何捕获 SIGINT 并优雅地退出程序。
  • 第二个示例演示了如何捕获 SIGSEGV(段错误),这通常发生在非法内存访问时。

8. 参考资料


总结

C++ 中的信号处理机制为程序提供了对异步事件的响应能力。通过使用 signal()sigaction() 函数,程序能够捕获并响应各种操作系统信号,进行相应的错误处理、资源释放、优雅退出等操作。信号处理是操作系统和进程之间进行交互的一个重要工具,掌握它对于处理多种复杂的运行时情况非常有用。