C++ 信号处理

2025 年 8 月 29 日 | 阅读 9 分钟

在 C++ 中,信号是中断,由操作系统传递给进程,以停止其正在进行的任务并处理已生成中断的任务。中断用于通知程序特定事件或条件,例如错误、终止请求或外部输入。当收到信号时,操作系统会停止程序的正常执行以处理它。

在 C++ 中,信号也可以由操作系统根据系统或错误条件生成。我们可以通过在 Linux、UNIX、Mac OS X 或 Windows 系统上按 Ctrl+C 来生成中断。

当发出信号时,系统可以

  • 终止程序。
  • 暂停它
  • 直接忽略它。
  • 或者,调用程序员定义的自定义处理函数。

捕获和处理信号

在 C++ 中,并非所有信号都可以被捕获或处理。某些信号是不可捕获的,例如 SIGKILL 或 SIGSTOP,操作系统会严格执行它们以确保系统稳定性。许多其他信号,例如 SIGINT、SIGFPE、SIGSEGV 和 SIGTERM,可以在程序中被拦截和处理。

如果要在 C++ 中使用信号,请使用 <csignal> 头文件,它提供信号名称和函数,例如 signal() 将处理程序与信号关联起来。

C++ 简单信号处理示例

让我们看一个示例来说明 C++ 中的信号处理。

示例

编译并运行

输出

Hello, World!!
Hello, World!!
Hello, World!!
Hello, World!!
Hello, World!!

说明

在此示例中,我们创建一个无限循环,向终端写入消息“Hello, World!!”。当用户按下 Ctrl+C 时,系统会生成 SIGINT 信号,该信号会中断并终止当前运行的程序。

C++ 中常用信号及其用途

以下是 C++ 中几种类型的信号列表。其中一些如下

信号描述
SIGABRT当程序调用 abort() 函数时发生。它导致异常终止。
SIGFPE由算术错误触发,例如除以零或数字溢出。
SIGILL当处理器检测到非法或未定义的机器指令时使用。
SIGINT当用户中断程序时使用(例如,按 Ctrl+C)。
SIGSEGV当程序尝试访问没有权限的内存时发生,例如解引用空指针或无效指针。
SIGTERM它通常用于通过 kill 或其他方法发送终止请求。
SIGHUP当终端关闭或控制进程结束时使用。它用于检测挂起。
SIGQUIT它类似于 SIGINT,但也会生成核心转储。它通常由 Ctrl+\ 触发。
SIGTRAP调试器用于触发断点或陷阱条件。
SIGBUS当进程在硬件级别错误访问内存时引发(例如,未对齐的内存访问)。
SIGUSR1它保留用于用户定义的行为。开发人员可以在收到此信号时分配自定义操作。
SIGUSR2这是另一个用户定义的信号,类似于 SIGUSR1。
SIGALRM当 alarm() 函数设置的计时器到期时使用。通常用于超时。
SIGCONT它用于恢复已暂停的进程(例如,在 SIGSTOP 之后)。
SIGSTOP它立即停止(暂停)进程。它不能被捕获、阻塞或忽略。

signal() 函数

在 C++ 中,signal() 函数是信号处理库的一部分,该库在头文件中描述。它允许我们的程序通过自定义信号处理来响应意外事件,例如中断或非法操作。

语法

它具有以下语法:

在此语法中

  • sig: 我们要处理的信号编号(例如,SIGINT、SIGTERM)。
  • func: 指向在收到信号时要调用的函数的指针。
  • 返回值: 返回值是指向前一个信号处理程序的指针。

使用 signal() 函数处理信号的方法

如果调用 signal() 函数,我们必须指定程序应如何响应提供的信号。我们有三个主要选项

1) 默认操作 - SIG_DFL

在 C++ 中,它指示系统应用信号的默认行为。例如,程序可能会终止或创建核心转储。

2) 忽略信号 - SIG_IGN

它允许应用程序完全忽略信号。信号将被检测到,但不会采取行动。执行继续,就好像什么都没有发生一样。

3) 自定义函数处理程序

在 C++ 中,我们可以编写自己的信号处理函数。当收到信号时,处理函数被调用,我们可以指定应用程序应如何响应(例如,保存数据、打印消息、清理资源等)。

C++ 信号处理示例

让我们看一个示例来说明 C++ 中的信号处理。

示例

编译并运行

输出

The program is running. Press Ctrl+C to stop the flow..
Running...
Running...
Running...
Running...
Running...

说明

在此示例中,我们使用无限循环每秒打印“Running...”。之后,当用户按下 Ctrl+C 时,自定义处理程序 my_handler() 接收 SIGINT 信号,打印终止消息,并优雅地停止程序。

raise() 函数

在 C++ 中,raise() 函数向当前运行的进程发送信号。它易于使用,适用于在程序中测试信号处理程序。

语法

它具有以下语法:

在这个语法中,

signal_type: 要发送进行处理的信号编号。它可以采用以下值之一

  • SIGINT
  • SIGABRT
  • SIGFPE
  • SIGILL
  • SIGSEGV
  • SIGTERM
  • SIGHUP

返回值: 如果信号已正确传递,则返回 0。如果失败,则返回非零值。

C++ raise() 函数示例

让我们看一个示例来说明 C++ 中的 raise() 函数。

示例

编译并运行

输出

Interrupt is handled. Signal number is: 2

说明

在此示例中,我们使用 raise() 函数手动生成 SIGINT 信号,该信号通常通过按 Ctrl+C 发出。当使用 raise(SIGINT) 函数时,自定义处理程序 Signal_Handler 接收信号并在终止应用程序之前打印信号编号。

C++ 使用多个 raise() 函数的示例

让我们看一个示例来说明 C++ 中使用多个 raise() 函数。

示例

编译并运行

输出

Choose a signal to raise which displayed below:
1. SIGINT (Ctrl+C simulation)
2. SIGILL (Illegal Instruction)
3. SIGALRM (Timer Alarm)
4. SIGFPE (Division by Zero)
5. SIGSEGV (Invalid Memory Access)
6. Exit out of the program.
Enter your choice which is from 1 to 6: 1
Interrupt is handled and the Signal number is: 2

Choose a signal to raise which displayed below:
1. SIGINT (Ctrl+C simulation)
2. SIGILL (Illegal Instruction)
3. SIGALRM (Timer Alarm)
4. SIGFPE (Division by Zero)
5. SIGSEGV (Invalid Memory Access)
6. Exit out of the program.
Enter your choice which is from 1 to 6: 2
Interrupt is handled and the Signal number is: 4

Choose a signal to raise which displayed below:
1. SIGINT (Ctrl+C simulation)
2. SIGILL (Illegal Instruction)
3. SIGALRM (Timer Alarm)
4. SIGFPE (Division by Zero)
5. SIGSEGV (Invalid Memory Access)
6. Exit out of the program.
Enter your choice which is from 1 to 6: 3
Interrupt is handled and the Signal number is: 14

Choose a signal to raise which displayed below:
1. SIGINT (Ctrl+C simulation)
2. SIGILL (Illegal Instruction)
3. SIGALRM (Timer Alarm)
4. SIGFPE (Division by Zero)
5. SIGSEGV (Invalid Memory Access)
6. Exit out of the program.
Enter your choice which is from 1 to 6: 6
Exiting the program.

说明

在此示例中,我们为五个不同的信号注册了自定义处理程序,并且菜单驱动循环允许用户手动引发信号,例如 SIGINT、SIGILL、SIGALRM、SIGFPE 和 SIGSEGV。根据用户的偏好,每个信号都会被有目的地触发,并且处理程序会打印信号编号,而不是终止程序,除非发生致命错误。

kill() 函数

与只影响当前进程的 raise() 函数不同,kill() 函数可以向任何进程(包括当前进程)发送信号,只要我们有权限。

语法

它具有以下语法:

  • pid: 目标进程的进程 ID。
  • 成功返回 0,失败返回 -1。

C++ kill() 函数示例

让我们看一个示例来说明 C++ 中的 kill() 函数。

示例

编译并运行

输出

Signal received: 2

说明

在此示例中,我们利用 kill() 函数向其自己的进程(由 getpid() 返回的进程 ID 标识)传递 SIGINT 信号。发送信号时,调用自定义处理程序 Handle_Signal,该处理程序在控制台上显示信号编号。

结论

总之,C++ 中的信号处理使程序能够响应意外的运行时事件,例如中断或终止请求。如果使用 signal() 函数并定义信号处理函数,我们可以拦截和管理信号,例如 SIGINT 或 SIGTERM。它有助于提高程序的健壮性,确保在异常情况下更安全的关闭和受控行为。总的来说,信号处理在系统编程和资源关键型应用程序中特别有价值。

C++ 信号处理常见问题解答

1) 当用户按下 Ctrl + C 时使用什么信号?

当用户在终端中按下 Ctrl + C 时,会发送 SIGINT 信号。

2) 程序可以忽略信号吗?

是的,程序可以通过使用 signal() 函数将其处理程序设置为 SIG_IGN 来忽略信号。

3) C++ 中哪些信号不能被捕获、阻塞或忽略?

不,程序不能捕获、阻塞或忽略 SIGKILL 和 SIGSTOP 信号。

4) C++ 中 raise() 函数的目的是什么?

在 C++ 中,raise() 函数用于在运行进程中手动生成信号。

5) 什么函数使用其进程 ID 将信号发送到另一个进程?

在 C++ 中,kill() 函数用于通过引用其 PID 将信号传递给特定进程。