[Linux][OS][详解信号的产生]

news2024/11/15 21:57:07

目录

1.信号概念

硬件层面

2. 产生!

1. 键盘组合键

2. kill 命令 kill -signo pid

3. 系统调用

4. 硬件异常--会自动退出

软件条件--闹钟

发送


信号和信号量没有任何的关系,就像老婆和老婆饼,上一篇文章我们讲到了信号量,这篇文章我们将来对信号进行讲解~

信号部分讲解思路:

  1. 信号的概念搞定--输出一堆的结论,支撑我们对信号的理解
  2. 信号的产生--重点
  3. 信号的保存

1.信号概念

日常生活中的信号,例如:信号弹,铃声,红绿灯,闹钟...

1.你怎么认识这些信号的?有人教我->我记住了

2.即便是我们现在没有信号产生,我也知道信号产生之后,我该干什么

3.信号产生了,我们可能并不立即处理这个信号。在合适的时候,因为我们可能正在做更重要的事情--所以,信号产生后--时间窗口--信号处理时--在这个时间窗口内,你必须记住信号的到来!


认识信号:

  1. 识别信号
  2. 知道信号的处理方法

和进程联系起来:

  1. 进程 必须 识别+处理信号--信号没有产生,也要具备处理信号的能力--信号的处理能力,属于进程内置功能的一部分
  2. 进程即便是没有收到信号,也能知道哪些信号怎么处理
  3. 当进程真的收到了一个具体的信号的时候,进程可能并不会立即处理这个型号,合适的时候处理
  4. 一个进程必须当信号产生,到信号开始被处理,就一定会有时间窗口,进程具有临时保存哪些信号已经发生了的能力

./后,不接受指令了,叫做前台进程

  • 当你运行./myprogram时,这个程序会变成前台进程。此时,bash(或你正在使用的shell)将控制权交给这个程序,所以你不能再在同一个终端中输入其他命令,直到这个程序运行结束。

后面加上& ,接受指令,不再受 ctrl c 控制了,要 kill ,启动的就是后台进程了

  • 允许你在同一个终端中继续输入其他命令。
  • 后台进程通常不会直接接收键盘输入,但它们的输出(标准输出和标准错误输出)仍然会显示在终端上,除非你将其重定向到文件或其他地方。

Linux 中,一次登录中,一个终端,一般会配上一个 bash ,每一个登录,只允许一个进程是前台进程,可以允许多个进程是后台进程 (前台获取键盘输入?后台的前台是默认的 bash,所以会接受指令

  • “只允许一个进程是前台进程”:这意味着在任何给定时刻,命令行界面只能有一个进程直接与用户交互,接收用户的键盘输入
  • “可以允许多个进程是后台进程”:这意味着用户可以同时运行多个任务,这些任务在后台执行,不会干扰前台进程,也不会直接接收用户的输入。
  • “前台获取键盘输入?”:是的,前台进程可以获取键盘输入。这是用户与前台进程交互的方式。
  • “后台的前台是默认的 bash,所以会接受指令”:当你没有运行其他前台进程时,默认的前台进程是bash(或shell)。在这种情况下,bash作为命令行解释器,可以接收和执行用户的指令。当你在bash中启动一个后台进程后,bash仍然保持在前台,能够接受新的指令。

对于后台:

ps ajx | grep myprocess 
pidof myprocess | xargs kill -9

ctrl + c 为什么能够杀掉我们前台进程呢?

键盘输入首先是被前台进程收到的,ctrl c 本质是被进程解释成为收到了信号,2 号信号

kill -l 查看信号,在 linux 中信号就是数字,内核中以宏定义了

1-30 普通信号

31-64 实时信号,需要立即处理的

信号的处理方式:三选一

  1. 默认动作
  2. 忽略
  3. 自定义动作(信号的捕捉)

验证:进程收到 2 号信号的默认动作,就是终止自己

man signal 修改特定进程对于信号的处理动作,自定义捕捉

void myhandler(int signo)
{
    cout << "process get a signal: " << signo <<endl;
    // exit(1);
}
signal(2, myhandler);
  1. signal 只需要设置一次,往后都有效
  2. signal 是在后续执行中触发,信号的产生和代码运行是异步的,叫做软中断
#include<iostream>
#include<unistd.h>
#include<cstdio>
#include<signal.h>

void handler(int signo)
{
    cout<<"捕捉到信号:"<<signo<<endl;
}

int main()
{
    signal(2,handler);
    int cnt=0;
    while(true)
    {
        printf("我是一个进程,我正在运行%d\n",cnt++);
        sleep(1);
    }
    return 0;
}

硬件层面

键盘数据是如何输入给内核的,ctrl c 又是如何变成信号的--谈谈硬件

键盘被摁下,肯定是 OS 先知道!

OS 怎么知道键盘上有数据了??键盘上的数据存储到 os 缓冲区

外设有数据了, CPU 硬件中断进行识别,记录中断号

CPU 中的寄存器凭什么能保存数据呢?

充放电的过程,高低电频被解释为 01 数据,同时 CPU 中的中断信息被操作系统读取,把外设数据拷贝到缓冲区中,有了中断,os 就不用轮询检查外设了

我们学的信号,就是用软件方式,对进程模拟的硬件中断

数据拷贝到缓冲区前,os 会判断数据,控制(ctrl c 转化成为 2 号信号发送给进程)

os 就是不断接收外部中断来接受外设


不同的文件传的信号涉及到的是不同的缓冲区

2. 产生!

1. 键盘组合键

ctrl c2

ctrl \3 号信号

不是所有的信号都是可以被 signal 捕捉的,例如:9 19

测试!杀进程和暂停进程不能被捕捉,os 为了安全起见

2. kill 命令 kill -signo pid

3. 系统调用

man kill

man raise给自己发送信号

==kill(getpid(),2)

man abort 给自己发 6 号信号,终止

1 2 3 都是信号产生的方式!但是无论信号如何产生,最终一定是谁发送给进程的?OS

为什么?OS 是进程的管理者!

4. 硬件异常--会自动退出

常见例子:

  1. /0
  2. 野指针

硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号

  • 例如:当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释为SIGFPE信号发送给进程
  • 例如:当前进程访问了非法内存地址,,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程

由此看出,在C/C++中,除零,内存越界等异常,在系统层面上,是被当成信号处理的

测试对于自己报错的解释:

void handler(int signo)
{
    cout<<"进程捕捉到了一个信号,信号编号是:"<<signo<<endl;
}

int main(int argc,char* argv[])
{
    signal(SIGFPE,handler);
    int cnt=0;
    while(true)
    {
         printf("cnt:%d,pid:%d\n",cnt++,getpid());
         int a=10;
         a/=0;       
    }
     return 0;
}

捕捉之后为什么会死循环呢?

系统是为了让用户可以查看到错误信息的捕捉,但捕捉后 CPU 状态寄存器一直在获取异常信息,因为异常只影响进程,CPU 把错误消息一直在给 OS 传,一直在被调度

上层用来宽容度保存资源和日志,所以交给了上层对异常进行处理和退出

补充:右边监视窗口的打开 ps ajx | grep myprocess

为什么/0,野指针会让进程崩溃了?Os 会给进程发送信号

OS 如何知道的呢?

CPU 上的 eip/pc 扫描函数

还有状态寄存器,存在溢出标志位

原理:寄存器知道进程上下文,虽然我们修改的是 CPU 内部的状态寄存器,但是进程只影响你自

操作系统检测到了硬件错误之后,一直发送

很多异常都是硬件引起的

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

MMU 内存管理单元,集成在了 CPU 内部,CPU 读到的是虚拟地址

都在 CPU 中三位一体:页表 虚拟地址 MMU->物理地址

地址转化失败:虚拟到物理转化失败(野指针),转化失败 CPU 会进行物理报错--信号

CPU 怎么区别溢出和越界

  • CPU 通过不同的异常处理机制来区分溢出和越界。溢出通常通过算术指令的状态标志来指示,而越界通过内存访问异常来指示。

上层语言是如何知道异常的呢?可能是产生了信号

软件条件--闹钟

异常,只会由硬件产生吗??不是,eg 管道只写不读报错 13)

man alarm

会返回上一次闹钟的剩余时间 n,

测试

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <iostream>
#include<signal.h>
using namespace std;

void catchSig(int signo) {
    cout << "进程捕捉到了一个信号,信号编号是: " << signo << endl;
    alarm(1); // 重新设置闹钟信号,延迟时间为 1 秒
}

int main(int argc, char* argv[]) {
    signal(SIGALRM, catchSig); // 设置信号处理函数
    alarm(5); // 设置初始的闹钟信号,延迟时间为 5 秒

    int cnt = 0; // 初始化计数器

    while (true) {
        cnt++;
        if (cnt == 3) {
            int n = alarm(0); // 取消现有的闹钟信号,并返回剩余时间
            cout << "取消闹钟信号,剩余时间: " << n << " 秒" << endl;
        }
        sleep(1); // 让程序暂停 1 秒
    }

    return 0;
}

1.重定义信号 2.alarm 查看返回值

操作系统中存在大量的闹钟,对闹钟的管理转化为对的增删查改struct alarm* head,还是先组织在描述,进行抽象

时间戳--记录当前时间,超时时间,OS 周期性的检查这些闹钟,循环 PCB 对比比较

对于堆和优先队列的回忆:

堆(Heap)

堆是一种特殊的完全二叉树,它满足以下性质:

  • 最大堆(Max Heap):对于任何节点,其值都大于或等于其子节点的值。根节点是堆中的最大值。
  • 最小堆(Min Heap):对于任何节点,其值都小于或等于其子节点的值。根节点是堆中的最小值。

堆通常用于实现优先队列,也可以用于排序算法(如堆排序)。

优先队列(Priority Queue)

优先队列是一种抽象数据类型,它类似于队列或栈,但是每个元素都关联有一个优先级。在优先队列中,元素按照优先级进行排序,具有最高优先级的元素先被处理。

特性

  • 先进先出(FIFO):对于相同优先级的元素,它们按照进入队列的顺序被处理。
  • 优先级处理:具有更高优先级的元素可以先于低优先级的元素被处理。
  • 堆是实现优先队列的一种常见方式。例如通过最大堆,可以实现一个最大优先队列

信号如果不是被立即处理,那么信号是否需要暂时被进程记录下来?记录在哪里最合适呢?

man 7 signal //信号手册

Term,Core都是终止进程,有什么区别?
其实这有关于,进程退出时,核心转储问题

默认云服务器上面的 core 功能是被关闭的,防止 core dump 冲击内存,core dump 会占内存,可以通过下面命令进行打开

打开系统的 core dump 功能,一旦进程出异常,OS 会将进程在内存中的运行信息,给我 dump(转储)到进程的当前目录(磁盘)形成 core pid 文件:核心转储(core dump)

调试时显示:运行时错误,哪一行,直接复现问题之后,直接定位到出错行

测试:

int main()
{
    //核心转储
    while(true)
    {
        int a[10];
        a[10000]=10;
    }
    return 0;
}

操作:打开 core 功能后,gdb 加载,先运行,在 core-file,事后调试

core ==term +core dump,Core退出的可以被核心转储的以便于后序快递定位问题

发送

对于普通信号而言,对于进程而言,自己有还是没有,收到哪一个信号,是给进程的 PCB 发来实现管理

task_struct{
    int signal;//0000 0000... //普通信号,位图管理信号
}
  1. 比特位的内容是 0 还是 1,表面是否收到
  2. 比特位的位置(第几个),表示信号的编号
  3. 所谓的“发信号”,本质就是 os 去修改 task_struct 的信号位图对应的比特位,“写信号”!

意味 OS 是进程的管理者,只有他有资格才能修改 task_struct 内部的属性!!!

(操作系统进行操作还要考虑上层软件哦

信号保存为什么?

进程收到信号之后,可能不会立即处理这个信号,信号不会被处理,就要有一个时间窗口

34-64 实时信号,有一个实时队列

连续发了好几个 2 号信号,位图怎么处理?都是一样的信号,没必要保存

注意:kill -9还是可以杀死进程,无论你怎么修改,无法对9号信号设定捕捉,即使你做了,OS也不会给你设置。

下篇文章将继续对信号进行讲解~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

探索未来教育新形态:基于AI大模型的在线辅导平台LlamaTutor

在数字化时代,教育的边界正在被重新定义。今天,我们将深入探索一款创新的教育工具——LlamaTutor,一个基于AI大模型的在线个人辅导平台,它利用前沿技术为学习者带来前所未有的个性化学习体验。 引言 随着人工智能技术的飞速发展,AI在教育领域的应用日益广泛。LlamaTutor…

冰岛数据中心技术三巨头推出由可再生能源驱动的一体化云计算解决方案

冰岛通过国内生产的各种形式的可再生能源来满足其大部分能源需求。据三家开发新数据中心服务的公司称&#xff0c;这个北欧岛国也是关键任务云应用的理想环境。 Vespertec 公司、Sardina Systems 公司和 Borealis 公司共同开发了一种创新的 IT 解决方案&#xff0c;名为冰云综合…

MATLAB算法实战应用案例精讲-【人工智能】差分隐私(概念篇)

目录 前言 知识储备 算法原理 发展历程 差分隐私的引入 GIC 事件 ε(epsilon)-差分隐私​编辑 实现方式 什么是差分隐私 差分隐私的工作原理 数学模型 差分隐私计算公式 拉普拉斯机制 高斯机制 高斯机制满足 (ε,δ)-差分隐私的数学证明 可组合性 怎样在机…

Python版《超级玛丽+源码》-Python制作超级玛丽游戏

小时候最喜欢玩的小游戏就是超级玛丽了&#xff0c;有刺激有又技巧&#xff0c;通关真的很难&#xff0c;救下小公主还被抓走了&#xff0c;唉&#xff0c;心累&#xff0c;最后还是硬着头皮继续闯&#xff0c;终于要通关了&#xff0c;之后再玩还是没有那么容易&#xff0c;哈…

思科OSPF动态路由配置8

#路由协议实现# #任务八OSPF动态路由配置8# 开放式最短路径优先&#xff08;Open Shortest Path First,OSPF&#xff09;协议是目前网络中应用最广泛的动态路由协议之一。它也属于内部网关路由协议&#xff0c;能够适应各种规模的网络环境&#xff0c;是典型的链路状态路由协…

JavaScript初级——简介

一、什么是语言 1、计算机就是一个由人来控制的机器。 2、我们要学习的语言就是人和计算机交流的工具&#xff0c;人类通过语言来控制、操作计算机。 3、编程语言和我们说的中文、英文本质上没有区别&#xff0c;只是语法比较特殊。 4、语言的发展&#xff1a; —纸带机&#x…

“低代码” 风暴:重塑软件开发新未来

目录 引言&#xff1a; 正文&#xff1a; 方向一&#xff1a;技术概览 方向二&#xff1a;效率与质量的权衡 方向三&#xff1a;挑战与机遇 结束语&#xff1a; 引言&#xff1a; 在当今数字化高速发展的时代&#xff0c;技术的创新如同璀璨星辰不断照亮我们前行的道路。“…

Dijikstra算法(堆优化版)

当给定数据的范围不大时&#xff0c;采用朴素Dijikstra算法尚能ac&#xff0c;但若是数据范围大于10^5&#xff0c;那么朴素Dijikstra算法就会爆掉&#xff0c;所以我们需要采用堆优化版的Dijikstra算法 堆优化版Dijikstra主要是对朴素Dijikstra中找寻从距离编号 1 结点路径长…

突然肾结石了:这时候我才意识到问题

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 中午吃过饭&#xff0c;下腹剧痛&#xff0c;忍了2个小时&#xff0c;我以为是普通肚子痛&#xff0c;因为之前没有任何征兆&#xff0c;所以我忍痛拍了这个视频。 这也是为什么评论区有朋友说&#xff1a;这期视频…

BQ27441初始化配置程序,电压、SOC等参数读取程序

系列文章目录 1.元件基础 2.电路设计 3.PCB设计 4.元件焊接 5.板子调试 6.程序设计 7.算法学习 8.编写exe 9.检测标准 10.项目举例 11.职业规划 文章目录 前言一、模拟IIC二、BQ27441初始化配置程序三、学习资料 前言 送给大学毕业后找不到奋斗方向的你&#xff08;每周不定…

算法打卡 Day23(二叉树)-二叉搜索树的最小绝对差 + 二叉搜索树中的众数 + 二叉树的最近公共祖先

文章目录 Leetcode 530-二叉搜索树的最小绝对差题目描述解题思路 Leetcode 501-二叉搜索树中的众数题目描述解题思路 Leetcode 236-二叉树的最近公共祖先题目描述解题思路 Leetcode 530-二叉搜索树的最小绝对差 题目描述 https://leetcode.cn/problems/minimum-absolute-diff…

萌啦数据使用多久,萌啦数据价格表2024

在数字化浪潮汹涌的今天&#xff0c;数据已成为企业决策与业务增长的核心驱动力。在众多数据分析工具中&#xff0c;萌啦数据凭借其强大的数据处理能力、直观的数据可视化效果以及灵活的数据分析模型&#xff0c;赢得了众多企业和个人的青睐。那么&#xff0c;关于“萌啦数据使…

C++ | Leetcode C++题解之第341题扁平化嵌套列表迭代器

题目&#xff1a; 题解&#xff1a; class NestedIterator { private:vector<int> vals;vector<int>::iterator cur;void dfs(const vector<NestedInteger> &nestedList) {for (auto &nest : nestedList) {if (nest.isInteger()) {vals.push_back(n…

苍穹外卖项目DAY05

苍穹外卖项目DAY05 1、店铺营业状态设置 1.1、Redis入门 Redis简介 Redis是一个基于内存的key-value结构数据库 基于内存存储&#xff0c;读写性能高适合存储热点数据&#xff08;热点商品、咨询、新闻&#xff09;企业应用广泛 中文网&#xff1a;https://www.redis.net…

FSOP,glibc-2.23攻击IO_list_all

文章目录 FSOP介绍&#xff1a;FOSP链执行流程&#xff1a;源码调试过程 FSOP 介绍&#xff1a; FSOP 是 File Stream Oriented Programming 的缩写&#xff0c;根据前面对 FILE 的介绍得知进程内所有的 _ IO_FILE 结构会使用 _ chain 域相互连接形成一个链表&#xff0c;这个…

景联文科技:一文详解如何构建高质量SFT数据

在图像处理和计算机视觉领域中&#xff0c;将一张图像转化为可用于训练机器学习模型的数据是一项复杂而重要的任务。SFT&#xff08;Supervised Fine-Tuning&#xff0c;监督微调&#xff09;是一种常见的深度学习策略&#xff0c;在这一过程中发挥着核心作用。 SFT是指在一个预…

【云备份】服务端模块-热点管理

文章目录 0.回顾extern1.介绍2.实现思想3.代码测试代码 0.回顾extern extern cloudBackup::DataManager *_dataManager extern 关键字用于声明一个全局变量或对象&#xff0c;而不定义它。这意味着 _dataManager 是一个指向 cloudBackup::DataManager 类型的指针&#xff0c;但…

外部接入tensorboard和Jupyter Notebook

本地端打开服务器端jupyter Notebook 1:服务器端在目标文件夹下输入jupyter notebook --no-browser --port8888&#xff08;留意下token&#xff09; 2&#xff1a;本地端打开git 的bash窗口输入ssh -L 8888:localhost:8888 warren10.12.14.187 warren为用户名&#xff0c;10…

get 请求获取不到参数,但是post参数可以获取到

一&#xff1a;测试代码时发现&#xff0c;get请求一直获取不到参数。最终原因如下&#xff0c;nginx配置中需求有下面的配置 $args&#xff1a;代表接受到的参数

MemFire Cloud是否真的可以取代后端

近年来&#xff0c;随着前端技术的迅速发展&#xff0c;前端工程师们越来越多地开始思考一个问题&#xff1a;“我还能不能不依赖后端&#xff1f;” 这种想法并非空穴来风&#xff0c;尤其是随着像MemFire Cloud这样的工具出现&#xff0c;它不仅能让开发者在没有后端的情况下…