【Linux】信号概念和信号的产生

文章目录

  • 一、什么是信号?
    • 1.signal系统调用
    • 2.从硬件解析键盘数据如何输入给内核
    • 3.同步和异步
  • 二、信号的产生
    • 1.键盘组合键
    • 2. kill命令
    • 3.系统调用接口
      • 3.1kill
      • 3.2 raise
      • 3.3abort
    • 4.异常
    • 5.软件条件
  • 重谈core dump标志位


一、什么是信号?

以日常为例,信号弹,上下课铃声,动物求偶行为,红绿灯等等。这些都是信号。


我们怎么认识信号?
从小开始,父母教的,老师教我们识别这些信号。


在信号产生后,可能我并不会马上处理该信号,但我记得有这个信号。所以必须在未来某个时间窗口内处理该信号。

比如:在宿舍点外卖的行为,外卖员打电话,我当时在打游戏,电话一挂,推高地去了,然后游戏打完后,我再下去拿外卖。
这个就是典型的信号收到之后,没有马上处理,而是再未来某个时间窗口内处理。


对于进程:

  • 1.进程必须具备识别和处理信号的能力。即使没有产生信号,但是也要能够识别对应未来可能产生的信号,属于进程的内置功能。
  • 2.当进程真的收到一个信号的时候,可能并不会立刻处理该信号,会在合适的时候处理。
  • 3.一个进程必须具备一个信号产生,到该信号被处理这个时间窗口内将信号保存的能力。

根据上面第二点,进程处理信号的方式有三种:

  • 默认动作
  • 忽略
  • 自定义动作

提出问题:
ctrl + c为什么能够杀掉前台进程呢?

前台进程:
在Linux中,一次登陆中,一个终端配一个bash,每一个登陆,只允许一个进程是前台进程。

也就是说,我们的bash,输命令的进程,就是前台进程,当写好代码,将一个程序运行起来后,这个正在运行的进程就是前台进程,bash就被变成后台进程了。

键盘在输入的时候,是前台进程获取的。

所以一个循环执行的时候,在键盘输入ctrl + c,会被前台进程获取并识别成2号信号,2号信号就是将进程中断。

所以ctrl + c的本质是被进程识别成2号信号。

1.signal系统调用

前面说过,进程处理信号的方式有三种。
对于2号信号来说,进程处理该信号的默认方式就是终止自己。
而其他两种处理方式,就是要signal函数解决。
在这里插入图片描述
第一个参数:signum,几号信号。
第二个函数:这个参数的类型是一个函数指针类型,其实就是自定义的处理signum信号的方式。

也就是说,调用signal系统调用,如果传入2号参数,在将来遇到2号信号时,处理方式就是调用handler函数的自定义处理方式。

void myheader(int sig)
{
    cout << "process get a signal : %d "<< sig << endl;
}

int main()
{
    signal(SIGINT,myheader);
    int cnt = 50;
    while(cnt--)
    {
        cout << "I am a process" << endl;
        sleep(1);
    }
    return 0;
}

在这里插入图片描述
遇到2号信号时,调用的自定义处理方式就是调用自己写的handler函数。

注意:signal函数只需要设置一次,就一直有效,往后只要遇到2号信号,都会使用自定义处理信号的方式,即调用handler函数。


2.从硬件解析键盘数据如何输入给内核

首先,键盘只要被摁下了,一定是操作系统先知道!

那么,操作系统怎么知道键盘上有数据?

在计算机中有许多硬件,其中,只要我们摁下键盘的键,就会产生信号中断,
通过高低电流的形式发送给中断单元,中断单元再将该信号发送到CPU的引脚,CPU接收到信号后,告知操作系统,并将中断单元号发送给操作系统,操作系统收到信号后,执行中断向量表中对应中断编号的方法的地址。

而再执行读取键盘的方法,也就是将键盘上摁下的内容拷贝到内存中的内核缓冲区中。
至此,就完成了操作系统可以知道键盘上有数据并将数据拷贝到内核缓冲区中的动作。

在这里插入图片描述

所以,操作系统不需要再对每一个硬件进行信号的检测了,只需等待CPU将对应的中断信号发送过来即可。


在这里插入图片描述

对于上面这种后台进程输入了l,再输入s,但是在屏幕上显示的是乱序的问题。

其实原理如下:

键盘在被摁下的时候,按键被加载到缓冲区中,第一次摁下l键,此时键盘缓冲区中有一个l键,然后再将该字符拷贝到显示器缓冲区中,打印到显示器上,此时后台进程一直再跑,也将对应的字符串打印到显示器上,造成打印是乱序的。
但是丝毫不影响键盘缓冲区中的l字符。

因为键盘文件和显示器文件是两个不同的文件,分别具有不同的缓冲区。
他们互不干扰,所以再输入s时,键盘缓冲区就形成了ls指令,再将该s拷贝到显示器缓冲区中打印到显示器文件上,就看到了乱序的ls指令,但是按下回车键后,键盘会向CPU发送硬件中断。就执行了ls命令。

在这里插入图片描述

3.同步和异步

信号的产生和我们写的代码执行是异步的。

就比如说:

我和张三去自习室学习,张三说,等我会宿舍拿本书,然后我就在楼下等张三,等到张三后我们在一起去自习。我们一起去的过程就是同步进行的。
如果我和张三去自习室自习,张三说他去拿书,我不等他,我就先走了,然后我就和张三不同时到自习室,这个过程就是异步的。

二、信号的产生

信号产生的方式有五种。

1.键盘组合键

在这里插入图片描述

  • 1.ctrl + c产生2号信号。
  • 2.ctrl + \产生3号信号。

2. kill命令

kill -signo + 进程pid

对特定进程发送对应的信号。

3.系统调用接口

3.1kill

在这里插入图片描述
给pid进程发送sig信号。

//实现一个kill命令
void Usage(string name)
{
    cout << "Usage:\n\t" << name << " signo pid" << endl;
}

int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }

    int signo = stoi(argv[1]); //获取信号码
    pid_t pid = stoi(argv[2]); //获取信号id
    int n = kill(pid,signo);
    if(n < 0) //kill 失败了
    {
        perror("kill");
        exit(1);
    }
    return 0;
}

3.2 raise

给调用者发送指定的信号。
也就是给自己发送指定信号。

在这里插入图片描述
该函数的本质就是调用kill(getpid(),sig);

3.3abort

给自己发送6号信号。
在这里插入图片描述

该函数的底层就是调用Kill(getpid(),6)

4.异常

当代码出现异常时,会向进程发送信号。

//模拟异常后发信号
void handler(int sig)
{
    cout << "receive " << sig << " signo!"<<endl;
    sleep(1);
}

int main()
{
    signal(SIGFPE,handler);

    int a = 10;
    a/=0;
    return 0;
}

在这里插入图片描述
这里有个问题:信号为什么一直被触发?操作系统怎么知道进程发信号了?

在这里插入图片描述
CPU内的寄存器eip/pc会记录进程中的代码走到哪一步了。
CPU中有一个寄存器叫做状态寄存器,这个状态寄存器虽然也是寄存器,但是该寄存器被设置成了具有比特位的寄存器,也就是说一个寄存器可以表示多个状态。

这些状态寄存器,eip寄存器等等保存的数据,都叫做进程上下文

一个进程在退出时会把关于该进程本身的上下文数据全部带走,下一个进程在被CPU调度执行时,会把自己进程的上下文数据加载到CPU的寄存器中。

所以,上一个进程如果修改了状态寄存器中的内容,也丝毫不影响下一个进程!!!

所以虽然我们修改了状态寄存器的内容,但只影响我进程自己!

所以,当一个进程出现异常时,状态寄存器的某个比特位会由0置1。而操作系统要保证CPU的健康状态,一定会频繁地检查寄存器的内容是否被修改,从而获取到进程是否出异常了!!

CPU也是硬件,操作系统是硬件的管理者!!!

为什么操作系统不直接根据进程发的信号,帮助进程处理这些信号呢?

因为信号存在的意义,就是为了让进程死的明白!!

进程会被操作系统指派去执行各种各样的任务,而这些任务又是用户给的。一旦某个任务在执行过程中出现异常,产生信号了,进程要知道自己到底因为什么而产生的信号,这样就算自己死了也能给上面一个交代!

异常的出现只是为了可以统一在一个地方处理后续的工作,而不是用来解决的。

异常无法解决,只能让用户知道到底是什么原因而改变相应的策略。

5.软件条件

软件条件实现闹钟

alarm函数实现闹钟。
在这里插入图片描述

设置一个函数实现闹钟,seconds秒之后会响,然后向进程发送14号信号终止进程。
返回值是:上一次设置的闹钟的剩余时间。

如果第一次设置闹钟是10秒,过了5秒后,再设置一个闹钟,此时设置的第二个闹钟时,返回值就是第一个闹钟的剩余时间,也就是5。

void handler(int sig)
{
    cout << "receive a " << sig << " signal" << endl;
}

int main()
{
    signal(SIGALRM,handler);

    int n = alarm(5);
    while(true)
    {
        cout << "i am a process ,pid : " << getpid() << endl;
        sleep(1);
    }
    return 0;
}

该代码验证了闹钟响,并向进程发送14号信号的结论。


重谈core dump标志位

在这里插入图片描述
在进程等待环节中,进程等待的中的status参数的其中一个比特位叫做core dump标志位

这个比特位的意义是:
一旦进程出现异常,OS会将进程在内存中的运行信息,给我转储(转而存储,dump)到进程的当前目录(磁盘)形成core.pid文件,这个过程叫做核心转储。
并将core dump对应的比特位设置为1,告诉用户。
为什么要有core dump这个功能呢,这是为了方便进行事后调试,也就是代码出错后再调试。

使用

ulimit -a查看系统的一些配置,其中第一个就是core file size,core文件的大小为0,说明core功能未打开
使用
ulimit -c 10240,将core文件大小设置最大为10240字节,此时就相当于打开了core功能了。

在这里插入图片描述

在.cc文件中写代码写一个除零错误后,会发现结果如下
在这里插入图片描述

那就意味着进程出现异常后,生成了一个core.pid文件,就是把进程在内存的运行信息转储到当前进程的目录下。

打开调试器调试当前可执行程序后,在这里插入图片描述
结果如下,将core.pid文件导入后,就能看到具体错误出现在什么地方,这样的调试就是事后调试

其中,默认的云服务器没有打开core dump功能。

因为在公司的服务器中,如果某个服务挂掉了,不管三七二十一,先不管为什么挂掉,立刻进行重启,立刻重新启动服务,事后再根据日志其他的排查问题即可。可是,如果打开了core dump功能,每次有服务挂掉,都会形成一个core.pid文件,这个文件还挺大的,如果在某个时间段,有许许多多的服务挂掉,或者一个服务挂了之后重启,重启后又挂掉,这样重重复复会产生大量的core.pid文件,久而久之会将磁盘空间占满,这时候就不再是服务挂掉那么简单的问题了,可能就转变成了操作系统挂掉的严重问题!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/210975.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

lv11 嵌入式开发 中断处理 15

前言&#xff1a;中断属于7种异常的1种&#xff0c;这节主要讲CPU接收到中断之后怎么处理&#xff0c;回顾之前的知识。 目录 1 异常 1.1 概念 1.2 异常处理机制 1.3 ARM异常源 1.4 异常模式 1.5 ARM异常响应 1.6 异常向量表 1.7 异常返回 ​编辑 1.8 IRQ异常举例 …

Android 缩减、混淆处理和优化应用

为了尽可能减小应用的大小&#xff0c;您应在发布 build 中启用缩减功能来移除不使用的代码和资源。启用缩减功能后&#xff0c;您还会受益于两项功能&#xff0c;一项是混淆处理功能&#xff0c;该功能会缩短应用的类和成员的名称&#xff1b;另一项是优化功能&#xff0c;该功…

无效的目标发行版: 21 和springboot爆错

目录 问题描述 原因分析&#xff1a; 问题描述 springboot爆红 调整一下这个&#xff1a;把这里的version调低一点应该就可以了 无效的目标发行版: 21 调整一下这个把这里的Java version调整一下&#xff0c;我是调整到1.8&#xff08;其他没有试过&#xff09; 原因分析&a…

基于ASP的购物网站设计

摘 要 随着计算机科学的不断发展和网络的迅速普及&#xff0c;Internet 的应用已经涉及到人们生活的方方面面&#xff0c;越来越多的现代企业也意识到了这一点&#xff0c;如何在当前的网络大发展的背景下开拓市场已经成为了企业关注的重中之重。总的来说&#xff0c;互联网的…

一篇五分生信临床模型预测文章代码复现——Figure 10.机制及肿瘤免疫浸润(七)

之前讲过临床模型预测的专栏,但那只是基础版本,下面我们以自噬相关基因为例子,模仿一篇五分文章,将图和代码复现出来,学会本专栏课程,可以具备发一篇五分左右文章的水平: 本专栏目录如下: Figure 1:差异表达基因及预后基因筛选(图片仅供参考) Figure 2. 生存分析,…

【计算机网络学习之路】URL概念及组成

目录 一. URL是什么 二. URL的组成 三. encode和decode 前言 本系列文章是计算机网络学习的笔记&#xff0c;欢迎大佬们阅读&#xff0c;纠错&#xff0c;分享相关知识。希望可以与你共同进步。 本篇讲解使用浏览器不可或缺的部分——URL 一. URL是什么 域名及DNS 我们在…

第一百八十六回 DropdownMenu组件

文章目录 1. 概念介绍2. 使用方法2.1 DropdownMenu2.1 DropdownMenuEntry 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何禁止页面跟随手机自动旋转"相关的内容&#xff0c;本章回中将介 绍DropdownMenu组件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 …

Azure Machine Learning - 在 Azure AI 搜索中创建全文查询

Azure AI搜索中如果要为全文搜索生成查询&#xff0c;本文提供了设置请求的步骤。 本文还介绍了查询结构&#xff0c;并说明了字段属性和语言分析器如何影响查询结果。 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&a…

【Cisco Packet Tracer】交换机 学习/更新/泛洪/VLAN实验

交换机的功能是连接计算机、服务器、网络打印机、网络摄像头、IP电话等终端设备&#xff0c;并实现与其它交换机、无线接入点、路由器、网络防火墙等网络设备的互联&#xff0c;从而构建局域网络&#xff0c;实现所有设备之间的通信。 本文使用Cisco Packet Tracer仿真软件&…

高效转码工具Compressor for Mac,让视频处理更轻松

在现如今的数字时代&#xff0c;视频内容已经成为人们生活中不可或缺的一部分。无论是在社交媒体上分享生活点滴&#xff0c;还是在工作中制作专业的营销视频&#xff0c;我们都希望能够以高质量、高效率地处理和传输视频文件。而Compressor for Mac作为一款强大的视频转码工具…

电大搜题微信公众号详解,助力学习者轻松考试

作为一名电大学者&#xff0c;我今天要向大家介绍一个引人注目的学习利器&#xff0c;那就是电大搜题微信公众号。对于广西开放大学的学习者们来说&#xff0c;这个微信公众号无疑将是他们学习路上的得力助手。 广西开放大学一直注重学生的学习体验和教学质量&#xff0c;为了…

【PTA-C语言】实验三-循环结构I

如果代码存在问题&#xff0c;麻烦大家指正 ~ ~有帮助麻烦点个赞 ~ ~ 实验三-循环结构I 7-1 求交错序列前N项和 &#xff08;分数 15&#xff09;7-2 寻找250&#xff08;分数 15&#xff09;7-3 最大公约数和最小公倍数&#xff08;分数 15&#xff09;7-4 统计字符&#xff0…

搭建nfs文件目录共享

搭建nfs文件目录共享 一、简介 NFS&#xff0c;英文全称是Network File System&#xff0c;中文全称是网络文件系统&#xff0c;是FreeBSD支持的文件系统中的一种&#xff0c;它允许网络中的计算机之间通过TCP/IP网络共享资源&#xff0c;在NFS应用中&#xff0c;本地NFS的客…

执行栈和执行上下文

前端面试大全JavaScript执行栈和执行上下文 &#x1f31f;经典真题 &#x1f31f;执行上下文 &#x1f31f;栈数据结构 &#x1f31f;执行上下文生命周期 &#x1f31f;真题解答 &#x1f31f;总结 &#x1f31f;经典真题 谈谈你对 JavaScript 执行上下文栈理解 &#…

企业计算机服务器中了locked1勒索病毒怎么解锁,locked1勒索病毒数据恢复

随着网络技术在企业的不断应用&#xff0c;企业的生产运营效率得到了极大提升&#xff0c;越来越多的企业利用网络开展工作&#xff0c;这也导致了企业的受到的网络安全威胁也不断增加。在本月&#xff0c;云天数据恢复中心陆续接到很多企业的求助&#xff0c;企业的计算机服务…

Screenshot To Code

序言 对于GPT-4我只是一个门外汉&#xff0c;至于我为什么要了解screenshot to code&#xff0c;只是因为我想知道&#xff0c;在我不懂前端设计的情况下&#xff0c;能不能通过一些工具辅助自己做一些简单的前端界面设计。如果你想通过此文深刻了解GPT-4或者该开源项目&#…

内部类Lambda

静态内部类 /*** 静态成员是在类加载成字节码时就已经存在的,静态只能访问静态*/ public class Demo {public static void main(String[] args) {Outer.Inner.show();} }class Outer {int num1 10;static int num2 20;static class Inner {static void show() {Outer outer …

重庆市失业金申领流程

1.领失业金的前提&#xff1a;非本人意愿中断就业。个人理解就是不是主动辞职才能领。 2.因此公司在处理社保减员的时候&#xff0c;不能是劳动者主动提出离职。 3.社保减员可选择原因&#xff1a;其他原因中断缴费 / 由单位提出双方协商一致解除劳动合同。 4.当社保暂停缴费…

A--Z与a--z的ASCII码的差异

从z到A还有一些字符 应该改为str[i]>A&&str[i]<Z||str[i]>a&&str[i]<z;

C++ string类—初始化、容量操作、迭代器

目录 前言 一、string类 二、初始化 1、无参或带参 2、用字符串变量初始化 3、用字符串初始化 4、指定数量字符 三、容量操作 1、size 2、push_back 3、append​编辑 4、运算符 5、reserve 6、resize 四、迭代器 五、OJ练习 反转字符 找出字符串中出现一次的…