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

【Linux】进程信号(一):信号的产生与信号的保存

📝前言:

这篇文章我们来讲讲Linux——进程信号

🎬个人简介:努力学习ing
📋个人专栏:Linux
🎀CSDN主页 愚润求学
🌄其他专栏:C++学习笔记,C语言入门基础,python入门基础,C++刷题专栏


目录

  • 一,认识信号
    • 1. 查看信号
    • 2. 信号处理动作
      • 2.1 默认处理动作
        • term 和 core 的区别
      • 2.2 自定义处理动作
        • signal
        • 示例
    • 3. 前台进程和后台进程
        • 区别
        • 切换
  • 二,信号的产生
    • 1. 四种方式基本介绍
    • 1. 异常产生信号
    • 2. 软件条件产生信号
  • 三,信号的保存
    • 1. 基本概念
      • 阻塞
    • 2. pending表
    • 3. block表
      • 3.1 sigset_t
        • 信号集操作函数
        • 信号集“上传”函数
      • 3.2 . 自定义处理动作sigaction
    • 4. handler表

一,认识信号

1. 查看信号

kill -l查看信号:
在这里插入图片描述

  • 每个信号都有⼀个编号和⼀个宏定义名称:左边是信号编号,右边是对应的宏
  • 其中1-31号信号是普通信号,34 - 64为实时信号
  • 普通信号:可以不立即处理
  • 实时信号:立即处理

signal.h中可以看到:编号和宏对应
在这里插入图片描述

2. 信号处理动作

按下 Ctrl + c 组合键会向当前前台进程发送 SIGINT(2 号信号),此时进程会终止。让进程终止是该2号信号的默认处理动作。
信号处理的动作有三种:默认自定义忽略

2.1 默认处理动作

man 7 signal我们可以去杂项章节看一下信号的具体描述:
在这里插入图片描述
Action是信号的默认处理动作

  • term / core → 终止(但有区别)
  • Ign → 忽略
  • Cont → 继续
  • Stop → 暂停
term 和 core 的区别
  • coreterm的基础上多了一个核心转储功能,会生成核心转储文件(Core Dump)。
  • 用途:用于后续debug,分析程序崩溃原因
    • 如,test1崩了,我们gdb ./test1 core就可以直接定位到崩溃的位置
  • 生产环境上(如:云服务器),core dump会被禁止,因为容易产生大量core文件占据磁盘空间

waitpid()的输出型参数的wstatus里面就有一个core dump标记位记录,当前信号是否是core终止的,是否生成core文件

ulimit -a可以查看允许生成的core文件的大小:
在这里插入图片描述
可以看到默认core文件的大小是0个blocks,即:禁止
ulimit -c <数字>:可以重新设置大小

2.2 自定义处理动作

signal

signal用来自定义信号处理动作
在这里插入图片描述

  • signum:要自定义的信号的编号(也可以传对应的宏,本质都是数字)
  • handler:自定义函数的指针
  • 同时要求我们的自定义函数
    • 返回值void
    • 一个参数:接受信号编号
示例
void handler(int signum)
{cout << "我收到了 " << signum << " 号信号" << endl;
}int main()
{signal(2, handler);while (true){cout << "进程PID: " << getpid() << endl;sleep(1);}return 0;
}

在这里插入图片描述
这时候可以killed -9 8968把这个进程杀掉,因为9号是强杀信号,不能被自定义动作,类似的不能被自定义的还有19

3. 前台进程和后台进程

在可执行程序执行后面带 &,运行的就是后台进程。

区别
  • 前台进程可以从标准输入中获取数据,但是后台进程不行(即:我们的键盘输入没办法发给后台)
  • 前后台进程都可以往标准输出打印
  • 在一个bash下:某一时刻,只能有一个前台进程,后台进程可以有多个(因为标准输入只有一个,不能同时有多个进程抢着读,会乱)
  • 每个bash 进程都有自己独立的作业列表,不同的 bash 进程之间的作业是相互隔离的
  • bash本身是前台进程,当我们执行前台进程的时候,bash就会被切换后台

示例:
./test1 &test1放到后台运行
在这里插入图片描述
可见键盘输入Ctrl + ctest1就收不到了,但是ls命令bash还可以收到,最后我们kill -9 9193就可以把这个进程杀掉

切换
  • jobs可以看当前bash的后台进程
  • fg + 任务号:把后台进程切换到前台
  • Ctrl + z:暂停前台进程,并把前台进程切换到后台
  • bg + 任务号:恢复暂停的后台进程,重新运行

二,信号的产生

1. 四种方式基本介绍

一个信号,要经历三个阶段:
在这里插入图片描述
信号的产生方式有多种:

  • 键盘产生
    • 如:ctrl + c给前台进程发信号
  • 系统调用(命令)产生
    • 如:int kill(pid_t pid, int sig),给pid进程发一个sig信号(命令kill就是调这个的)
    • 如:int raise(int sig),自己给自己发sig信号
    • 如:abort():给自己发 6 号信号
  • [硬件]异常产生
    • 如:\0野指针错误产生异常就会发对应的信号(实际上是先硬件异常)
  • 软件条件产生
    • 如:管道文件写端继续,读端关闭。此时写是没有意义的,系统就会产生SIGPIPE信号
    • 如:alarm:用于设置一个定时器(闹钟),在指定的秒数后向当前进程发送一个 SIGALRM 信号

不管信号怎么产生,都要直接 / 间接的由OS来发对应的信号

1. 异常产生信号

出现异常的时候,其实最先变化的是硬件!
核心逻辑是:
硬件通过寄存器标记异常并主动触发 CPU 异常机制 → CPU 借助操作系统内核处理异常 → 操作系统将硬件事件转化为软件信号通知进程

2. 软件条件产生信号

这里以alarm为例,设计一个定期向进程发送信号,驱动进程完成对应工作的程序

void handler(int signum)
{cout << "执行任务 1 " << endl;cout << "执行任务 2 " << endl;cout << "#########################" << endl;alarm(2);
}int main()
{alarm(2);signal(SIGALRM, handler);while (true){}return 0;
}

每隔两秒,handler就会在“闹钟”的驱动下被执行一次。
OS的调度原理也是类似:通过定期传递信号,被动被驱动,然后运行对应的进程。简单理解:

  • 如果这个“闹钟”本身是一个任务结构体,里面还记录了时间片。
  • 将所有任务用最小堆组织起来,每次选取任务的时候,用现在的时间对比堆顶任务的最小时间,如果超时了(就是“闹钟响了”),就运行堆顶的任务。
  • 运行的同时,对应任务结构体内的时间片–。这就是调度算法

三,信号的保存

1. 基本概念

阻塞

  • 信号递达:实际执行信号的处理动作
  • 信号未决:信号已递达进程,但尚未被处理的状态。又可以细分成下面两种。
    • 未被阻塞:可立即被递达的(取决于进程的处理方式)。
    • 被阻塞:需先解除阻塞才能被递达的(是否阻塞由进程自己决定)

在这里插入图片描述

进程的信号保存主要依赖于三张表:
在这里插入图片描述
下面依次讲解

2. pending表

未决信号集:记录进程接受到的,但是未被处理信号,本质是一张位图。

  • 当OS给进程发信号的时候,就是把对应信号下标的位置由 0 → 1
  • 当一个信号被递达时,是pending表中对应位置先 → 0,然后才执行处理函数递达

3. block表

阻塞信号集:记录当前进程要阻塞的信号,本质也是一张位图。

  • 1代表,接受到该信号以后阻塞该信号。
  • 对于普通信号 0 - 31,若信号被阻塞,在阻塞期间多次产生该信号,则未决信号集中(pending)仅记录一次。
  • 系统默认的block表是全 0
  • 对应普通信号,当自定义处理函数在执行时,该时期内是不能接受到同编号的信号的,即:当一个信号被递达时,block表里对应的位置会置为 1

3.1 sigset_t

sigset_tC语言给我们提供的位图类型,我们无法直接修改信号集,需要利用sigset先创建用户层位图,然后间接修改。

一般我们创建了sgset_t位图时,要先清空,然后再自行修改好用户层的位图,再调用函数修改内核层的信息集。

信号集操作函数
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);

介绍一些常用的(我们使用时的 组合拳):

  • sigemptyset:清空set,全置为 0
  • sigfillset:全设置成 1
  • sigaddset:加入signo信号,即set中对应位置设置成 1
  • sigaddset:删除signo信号
  • sigismember:检查信号是否在信号集中
    • 信号存在:返回1
    • 信号不存在:返回0
信号集“上传”函数

sigprocmask :将用户空间的信号集配置传递到内核

  • 原型:int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
  • how:如何修改
    • SIG_BLOCK:将 set 中的信号(为1的位置)添加到当前阻塞集。
    • SIG_UNBLOCK:从当前阻塞集中移除 set 中的信号
    • SIG_SETMASK:用 set 完全替换当前阻塞集
  • set:用户层的信号集
  • oset:输出型参数:返回原来的信号集,如果不需要设置成:nullptr

3.2 . 自定义处理动作sigaction

sigaction也是用来自定义信号处理函数的,但是它更强大。

原型:

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
  • signum:信号编号
  • actsigaction类型的结构体(里面可以存自定义方法、sigset_t表等)
  • oldact:输出型参数

signal的区别就在于这个sigacion类型的结构体:
在这里插入图片描述

  • 其中,sa_handler就是自定义函数指针,sa_mask是信号集,用来设置在执行这个自定义函数时的block表,用来阻塞特定的信号

示例:

void handler(int signum)
{cout << "进程:" << getpid() << "捕抓到: " << signum << endl;while(true){}
}int main()
{signal(3, handler);sigset_t my_block;sigemptyset(&my_block);sigaddset(&my_block, 3);struct sigaction act2;act2.sa_handler = handler;act2.sa_flags = 0;act2.sa_mask = my_block;sigaction(2, &act2, nullptr);while(true){}return 0;
}

运行:
在这里插入图片描述
进入2信号的自定义处理函数后,2号和3号信号都被阻塞了。

4. handler表

handler表是一个函数指针数组,里面存储的就是对应的信号的处理函数。

  • 当我们signal/ sigaction自定义函数的时候,其实改的就是对应下标的函数指针。

🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

相关文章:

  • LLaMA-Factory 微调模型与训练数据量对应关系
  • 根据LangChain4j官方文档,三分钟完成Springboot项目集成LangChain4j
  • Bolt.new:重塑 Web 开发格局的 AI 利器
  • Pycharm and Flask 的学习心得(8)渲染form表单
  • 【漫话机器学习系列】277.梯度裁剪(Gradient Clipping)
  • 开发AR导航助手:ARKit+Unity+Mapbox全流程实战教程
  • 哪款云手机支持安卓12系统?掌派云手机-性价比之选
  • HUAWEI交换机配置镜像口验证(eNSP)
  • 深度解析视频剪辑SDK开发:从AI字幕提取到多端原生插件集成-优雅草卓伊凡
  • SheetMetal_Unfold方法 FreeCAD_SheetMetal deepwiki 源码笔记
  • Python模型优化技巧
  • FFMPEG-AAC编码
  • AI 赋能心理健康预测与干预:智能技术如何成为情绪“守护者”
  • C语言指针详解
  • PCM音频数据的编解码
  • boost之preprocessor
  • 12.LCD、FSMC和ILI9341芯片
  • GitLab-CI简介
  • 深入理解 JavaScript 面向对象编程与 Class
  • git子模块--常见操作
  • 汉中网站建设有限公司/南京seo公司排名
  • 做兼职的网站打字员/石家庄百度搜索优化
  • wordpress默认后台路径/宁波优化网页基本流程
  • 网络公司什么意思/安卓手机性能优化软件
  • 教你做文案的网站推荐/百度云盘官网登录入口
  • 广州网站建设studstu/吴中seo页面优化推广