当前位置: 首页 > article >正文

Linux 下信号的保存和处理

信号的几个状态

  1. 信号抵达: 当接收到的信号被处理时, 此时就成为信号的抵达
  2. 信号的未决: 从信号的产生到信号抵达这个时间段之间, 称为信号未决
  3. 信号阻塞: 当进程设置了某个信号为阻塞后, 这个进程就不会在接收到这个信号
  4. 信号忽略: 将信号设置为忽略后, 接收到这个信号, 对这个信号的处理就是忽略

阻塞和忽略:

阻塞是进程没有收到对应的信号, 所以没有任何处理.

忽略接收到了信号, 但是对这个信号的处理就是不做任何处理, 进行忽视

虽然两者的行为是一样的, 没有任何处理, 但是本质还是有差别的

信号的保存

进程要对对接收到的信号处理, 那么就需要存在一块空间来存储接收到的信号.

在进程 PCB 中会存在三张表

1. block 表

block 表实际上是一个位图, 每一个比特位都对应一个信号.

信号对应比特位上要么是0, 要么是1.

1则代表在这个进程中, 这个比特位对应的信号设置为阻塞. 进程不接受此信号.

所以这张表也称为信号屏蔽字

2. pending 表

pending 表也是一个位图, 用来记录进程是否接收到了比特位对应的信号.

比特位为0表示没有接收到了对应位的信号. 为1表示接收到对应的信号.

这张表也可以称为未决表, 因为其中记录的信号都还未处理.

3. handler 表

handler 表和前两个表不同, handler 表是一个数组. 准确来说是一个函数指针数组.

这个表中记录着对应信号的处理函数地址. 当接收到信号时就会去调用对应的处理函数.

在上面的信号状态中, 有一个信号忽略, 实际上就是将 handler 表中的对应信号的函数指针设置为"SIG_IGN". SIG_IGN 是一个宏, 本质还是一个函数指针.

信号的一些操作

上面讲了信号存在阻塞状态, 还存在忽略状态. 除了系统默认的初始配置, 我们也可以手动进行设置.

1. 阻塞信号的设置

上面了解到阻塞表 (block) 是一个位图. 所以需要先来了解一下位图的使用.

sigset_t 类型就是位图

int sigemptyset(sigset_t *set) // 将每一个比特位都设置为 0

int sigfillset(sigset_t *set); // 将每一个比特位都设置为 1

int sigaddset(sigset_t *set, int signum); // 将第 signum 比特位设置为 1

int sigdelset(sigset_t *set, int signum); // 将第 signum 比特位设置为 0

int sigismember(const sigset_t *set, int signum); // 判断第 signum 比特位是0还是1并返回

当设置好了位图后, 使用函数 sigprocmask 来设置阻塞表

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

  • how:指定如何修改信号阻塞集,可以取以下值:
    • SIG_BLOCK:将 set 中的信号添加到当前信号阻塞集中。
    • SIG_UNBLOCK:将 set 中的信号从当前信号阻塞集中删除。
    • SIG_SETMASK:将当前信号阻塞集设置为 set
  • set:指向 sigset_t 类型的指针,表示要修改的信号集。
  • oldset:指向 sigset_t 类型的指针,用于存储修改前的信号阻塞集。如果不需要,可以传 NULL

返回值:

  • 成功时返回 0。
  • 失败时返回 -1.
int main()
{
    sigset_t myset;
    sigemptyset(&myset);
    sigaddset(&myset, 1);
    sigprocmask(SIG_BLOCK, &myset, NULL);
    // 将本进程的一号信号设置为阻塞
    return 0;
}

2. 信号处理函数设置

接下来我们来设置对于信号的处理函数.

我们需要使用 signal 函数.

signum: 要设置几号信号的处理函数, 可以使用数字, 也可以使用信号名如: SIGINT

  • SIG_DFL:恢复信号的默认处理行为。
  • SIG_IGN:忽略信号。
  • 自定义处理函数:用户定义的处理函数指针。

handler: 接收到信号时, 要调用的处理函数

void func(int signum) // 参数不能省略
{
    cout << "接收到信号: " << signum << endl;
}

int main()
{
    signal(1, func);
    // 当进程接收到 1 号信号时, 就会调用函数 func, 进程就会打印信息
    return 0;
}

信号的处理时机

从内核态返回用户态时, 会进行信号的处理

内核态和用户态都是操作系统的一种运行状态.

用户态拥有的权限较低, 只能访问自己的地址空间, 不能直接访问内核空间.

内核态拥有最高的权限, 可以访问系统的任何资源, 执行所有操作.

区分用户态和内核态是对操作系统的一种保护方式, 可以防止用户程序对系统资源的非法访问. 保护系统的稳定和安全.

那么什么时候会发生用户态和内核态之间的转换

系统调用, 如: 使用 read, write 函数, 这些就是系统提供的接口, 当调用这些系统调用时, 就会发生用户态到内核态的转换, 当执行完系统调用后, 就会从内核态转换为用户态.

此时, 就会对进程接收到的信号处理.


http://www.kler.cn/a/488709.html

相关文章:

  • 【stm32+K210项目】基于K210与STM32协同工作的智能垃圾分类系统设计与实现(完整工程资料源码)
  • 互联网架构变迁:从 TCP/IP “呼叫” 到 NDN “内容分发” 的逐浪之旅
  • 代码随想录算法训练营第六十天|KM94.城市间货物运输Ⅰ|KM95.城市间货物运输Ⅱ|KM96.城市间货物运输Ⅲ
  • matlab编写分段Hermite插值多项式
  • 决定系数(R²分数)——评估回归模型性能的一个指标
  • 缓存-Redis-常见问题-缓存击穿-永不过期+逻辑过期(全面 易理解)
  • Python 的医疗问句中的实体识别算法的研究(Flask)
  • SpringCloud微服务架构高可用设计方案
  • 自动驾驶领域的基础模型综述
  • 学习HLS.js
  • 高级java每日一道面试题-2025年01月08日-微服务篇-负载平衡的意义什么 ?
  • 2025-1-10-sklearn学习(36、37) 数据集转换-无监督降维+随机投影 沙上并禽池上暝。云破月来花弄影。
  • 二手母婴商品交易系统|Java|SSM|VUE| 前后端分离
  • JSON.stringify 实现深度克隆的缺陷
  • 《庐山派K230 从入熟悉到...》按键拍照保存
  • 牛客周赛74
  • Wireshark TCP 分析标志位说明汇总
  • 【Golang 面试题】每日 3 题(二十六)
  • MiniMind - 从0训练语言模型
  • 蓝桥杯python省赛备战day2--连续求和公式应用--829连续整数求和-枚举算法刷题学习笔记2--leetcode
  • ue5 动画通知
  • JavaScript 数组拓展:方法与实例全解析
  • 安科瑞 Acrel-1000DP 分布式光伏监控系统在工业厂房分布式光伏发电项目中的应用
  • 微信小程序评分小程序ssm+论文源码调试讲解
  • linux下MySQL的数据存放
  • ISP架构方案