《深入 C 语言和程序运行原理》15 标准库:信号与操作系统软中断有什么关系?(学习笔记)

本文介绍了C语言中信号(signal)的概念,它是用于通知进程发生异步事件的软中断。信号处理函数可以预先设定,当特定信号触发时执行。文章举例说明了如何设置信号处理函数来捕获和处理浮点异常(SIGFPE)。同时,讨论了不可重入函数的问题,以及在多线程环境中使用信号处理的挑战。并提到了C标准库中的sig_atomic_t类型,确保原子性访问变量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

仅作为本人学习《深入 C 语言和程序运行原理》的学习笔记,原课程链接:极客时间《深入 C 语言和程序运行原理》——于航

我们都知道除法运算分母不能为 0,如果在程序中用一个数除以 0,程序中常常会出现“除零异常”。

平时我们在运行程序或与操作系统、底层硬件进行交互时,你可能会遇到下面这些情况:

  1. 程序运行时访问了非法内存,导致段错误(Segmentation Fault);
  2. 程序卡死时或用户想强制关闭程序时,在控制台终端输入 Ctrl+C;
  3. 计算机底层硬件出现故障,导致无法实现特定功能;
  4. 。。。

以上这些情况都可以通过“信号”来解决。

什么是信号

软中断信号(signal,简称为信号)用来通知进程发生了异步事件。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。注意,信号只是用来通知某进程发生了什么事件,并不给该进程传递任何数据。

——百度百科

信号产生是一个随机的过程。程序不需要轮询某个全局变量来处理事情,而是可以提前设定好当某个信号到来时如何进行处理,这是典型的异步事件处理方式。

信号与软件中断

信号是一种软中断。

通常来说,中断触发分为两种形式,即硬件中断和软件中断,其中硬件中断是指与计算机硬件特定状态相关的中断过程,该过程直接由硬件触发。

软件中断是指由计算机软件,通过机器指令引起的 CPU 执行流程的临时转移过程,系统调用也是一种软中断。

在 C 代码中与信号交互

C 语言自 C90 标准开始,便提供了 signal.h 头文件,下面这份代码出自原文:


#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void sigHandler(int sig) {
  printf("Signal %d catched!\n", sig);
  exit(sig);
}
int main(void) {
  signal(SIGFPE, sigHandler);
  int x = 10;
  int y = 0;
  printf("%d", x / y);
}

main 函数开头先用 signal()函数设置了一个信号捕获,当 SIGFPE 信号触发时,便会调用 sigHandler() 函数。

在这里插入图片描述
Signal 8 对应的就是 SIGFPE 信号,下面是 C 标准库提供的 6 种不同类型的信号:

图片来源:课程原文

在这里插入图片描述

除了上面自定义信号处理函数的方式外,C 标准库还为我们提供了两种基本的信号处理方式,分别是 SIG_DEF(使用默认的信号处理函数)和 SIG_IGN(忽略该信号),它们可以直接作为 signal() 函数的第二个参数使用。

但是并非所有类型的信号都可以忽略,有些难以恢复软硬件异常对应的信号不能被忽略,比如 SIGTERM,或者 Linux 上的 SIGKILL 和 SIGSTOP 等。

可重入函数

信号处理可以发生在程序运行的任意时刻,但是有些时候,程序运行到一个函数 A 内部,此时来了一个信号,导致 CPU 转去执行信号处理函数,但信号处理函数内部也有函数 A,这样会不会有影响呢?

答案是如果函数 A 是不可重入函数,上面这种情况就会导致意想不到的后果,很多 C 标准库函数,如 printf、exit、malloc 都是不可重入函数。这也就是我们常说的中断内不能放不可重入函数。

C 标准库为我们提供了一个名为 sig_atomic_t 的整数类型,即使中断可能打断执行流程,对该类型变量的读写也都是原子的,这是一种在异步信号处理场景下的原子性。

在处理信号(signal)的时候,有时对于一些变量的访问希望不会被中断,无论是硬件中断还是软件中断,这就要求访问或改变这些变量需要在计算机的一条指令内完成。通常情况下,int类型的变量通常是原子访问的,也可以认为 sig_atomic_t就是int类型的数据,因为对这些变量要求一条指令完成,所以sig_atomic_t不可能是结构体,只会是数字类型。

——百度百科

这里我不是很理解,反正无论如何,中断函数最好不要使用不可重入函数。

多线程应用的信号处理

C 语言并没有对并发编程中的信号处理做任何约束和建议,所以在不同操作系统上使用 signal()raise() 函数,可能功能上会有差异。

所以如果要考虑程序的移植性,最好不要在多线程应用中使用信号处理。处理方法:可以通过宏判断来区分不同的操作系统,从而在不同系统上运行不同的代码。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小辉_Super

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值