信号保存和处理

把上一篇回顾一下吧:共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,进程不再通过执行进入内核的系统调用来传递彼此的数据

共享内存的数据结构:

struct shmid_ds 
{        
    struct ipc_perm shm_perm;       /* operation perms */    
    int shm_segsz;                  /* size of segment (bytes) */    
    __kernel_time_t shm_atime;      /* last attach time */    
    __kernel_time_t shm_dtime;      /* last detach time */    
    __kernel_time_t shm_ctime;      /* last change time */    
    __kernel_ipc_pid_t shm_cpid;    /* pid of creator */    
    __kernel_ipc_pid_t shm_lpid;    /* pid of last operator */    
    unsigned short shm_nattch;      /* no. of current attaches */    
    unsigned short shm_unused;      /* compatibility */    
    void *shm_unused2;              /* ditto - used by DIPC */    
    void *shm_unused3;              /* unused */
};

shmget函数

功能:用来创建共享内存

原型

int shmget(key_t key, size_t size, int shmflg);

参数

key:这个共享内存段名字

size:共享内存大小

shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

 shmat函数

功能:将共享内存段连接到进程地址空间

原型

void *shmat(int shmid, const void *shmaddr, int shmflg);

参数

shmid: 共享内存标识

shmaddr:指定连接的地址

shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY

返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

 shmaddr为NULL,核心自动选择一个地址

shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。

shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)

shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

shmdt函数

功能:将共享内存段与当前进程脱离

原型

int shmdt(const void *shmaddr);

参数

shmaddr: 由shmat所返回的指针

返回值:成功返回0;失败返回-1

注意:将共享内存段与当前进程脱离不等于删除共享内存段

 shmctl函数

功能:用于控制共享内存

原型

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数

shmid:由shmget返回的共享内存标识码

cmd:将要采取的动作(有三个可取值)

buf:指向一个保存着共享内存的模式状态和访问权限的数据结构返回值:成功返回0;失败返回-1

 

消息队列 

消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法

每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值

IPC资源必须删除,否则不会自动清除(重启可以)system V IPC资源的生命周期随内核

信号量

五个概念捏

多个执行流(进程)能看到的一份资源是共享资源

被保护起来的资源是临界资源(同步和互斥)

进程互斥

由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥

系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源

在进程中涉及到互斥资源的程序段叫临界区

IPC资源必须删除,否则不会自动清除(重启可以)system V IPC资源的生命周期随内核

互斥:任何时刻只有一个进程在访问共享资源

 资源要被程序员访问的,通过代码访问,代码 = 访问共享资源的代码(临界区) + 不访问共享资源的代码(非临界区)

所谓的对共享资源进行保护(临界资源),本质是对访问共享资源的代码进行保护(临界区)

进入临界区加锁,出了临界区要解锁捏

对于信号量理论的理解

信号量,信号灯是为了保护临界资源的(code)

信号量:本质是一个计数器

看电影买票的本质是对资源的预定机制

所以流程就很明了了

先买票

再让执行流和资源进行一一对应(程序员编码实现)

电影院相当于临界资源(共享资源)

买票相当于申请信号量

总的票数:信号量的初始值

申请信号量的本质是对公共资源的一种预定机制

申请信号量 -- 访问共享内存 -- 释放信号量

对共享资源的整体使用是资源只有一个

计数只有1 or 0的被称作二元信号量,互斥

不能用一个全局变量来当做计数器

为什么捏?

全局变量能被所有进程看到么?

不能捏

根本不能实现我们的要求捏,不是原子的

IPC信号量和共享内存、消息队列一样,要让不同的进程看到同一个计数器

信号量也是一个公共资源,保护临界资源安全的前提是自己是安全的

信号量操作

信号量--要是安全的 --> P

信号量++也要是安全的 --> V

PV操作通过安全保证原子性(以结果为导向,就比如鸡婆的棕褐色猎犬打炉石,打了一晚上,但是最后switch只看他是否上去分了,不会因为他哪局运气不好操作失误卡掉而同情它,也不会做出干预,只看最后的结果)

申请信号量用什么接口捏?

int semget(key_t key,int nsems,int semflg);

信号量集由数组来维护 

信号量不用了怎么办呢?

int semctl(int semid, int semnum, int cmd, ...);

op是对PV操作的封装 

int semop(int semid, struct sembuf *sops, size_t nsops);

 

信号量指令
ipcs -s

 删掉指定的信号量:

ipcrm -s semid

 OS是如何把共享内存,消息队列,信号量统一管理起来的呢?

struct ipc_id_ary
{
    int size;
    struct kern_ipc_pern* p[0];
    ...
}

把它们的第一个元素都存到struct kern_ipc_perm *XXX[n],就意味着我们把IPC的资源统一管理了

shmid就是数组的下标

所以检测是否冲突只需要遍历数组来检查

这种技术叫多态!

IPC_perm里必须要有字段指向对应的类型

信号

信号和信号量有什么区别。。。

有什么关系

两个没什么关系,就跟鸡婆的棕褐色猎犬和炉石传说高手没什么关系一样,八竿子打不到一块

信号在生活中随时可以产生,信号的产生和进程是异步的

进程能认识信号,可以识别并处理信号

可以把到来的信号暂不处理

在合适的时候再处理

1~31是普通信号,后面是实时信号

信号是Linux系统提供的一种向指定进程发送特定事件的方式,做识别和处理

信号的产生是异步的

异步就是,比如墨墨酱正在上课,但是她的外卖到了,为了避免别人偷她外卖,所以她派遣励志轩去帮她取外卖,墨墨酱上她的课,同时励志轩帮她取外卖,互不耽误

所以总结一下就是信号是Linux提供的一种,向指定进程发送特定事件的方式,做识别和处理

信号处理有三个动作:默认动作、忽略动作、自定义处理(信号的处理)

使用信号最直观的一个接口就是

man 7 signal

sighandler_t signal(int signum,sighandler_t handler);

 

对信号的自定义捕捉只需要捕捉一次,后续就会一直有效

#include<iostream>
#include<signal.h>
#include<unistd.h>

void hander(int sig)
{
    std::cout << "get a sig" << sig <<std::endl;
}

int main()
{
    signal(2,hander);
    while (true)
    {
        std::cout << "hello world,pid: " << getpid() <<std::endl;
        sleep(1);
    }
    return 0;
}

 

可以发现捏

kill -2就相当于是Ctrl+c了 

信号产生是通过kill命令向进程发送信号捏

键盘也可以发送信号

还有一种方式可以产生信号:系统调用

我们如何理解信号的发送与保存呢?

进程有对应的task_struct,这是成员变量,而我们是通过位图来保存收到的信号的

uint32_t signals;

 发送信号就是修改指定进程PCB中的信号的指定位图

内核数据结构对象,只有OS有资格修改

祝贺我姐喜提新键盘咯

看着感觉很像那种敲起来像什么奶油轴的声音

 使用kill也是可以实现系统调用的捏:

 来演示一下:

testsig.cc:

#include<iostream>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>

//./mykill 2 pid
int main(int argc,char *argv[])
{
    if(argc != 3)
    {
        std::cerr << "Usage: " << argv[0] << "signum pid" << std::endl;
        return 1;
    }
    pid_t pid = std::stoi(argv[2]);
    int signum = std::stoi(argv[1]);

    kill(pid,signum);
}

process.cc:

#include<iostream>
#include<unistd.h>
#include<signal.h>

void hander(int sig)
{
    std::cout << "get a sig" << sig <<std::endl;
}

int main()
{
    signal(2,hander);
    while (true)
    {
        std::cout << "hello world,pid: " << getpid() << std::endl;
        sleep(1);
    }
    
    return 0;
}

makefile:

 其实每次感觉很没必要,但是那是代码哎

交一下吧,谁说

makefile不算代码呢

.PHONY:all
all: client server

client:client.cc
	g++ -o $@ $^ -std=c++11
server:server.cc
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -rf client server

 除了kill之外还有别的接口:

raise是给自己当前的进程发送信号:

#include<iostream>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>

void hander(int sig)
{
    std::cout << "get a sig" << sig <<std::endl;
}

int main()
{
    int cnt = 0;
    signal(3,hander);
    while (true)
    {
        sleep(2);
        raise(3);
    }
}

 

还有个接口:abort

 

abort是终止进程,可以向进程发送指定信号

 

#include<iostream>
#include<cstdlib>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>

void hander(int sig)
{
    std::cout << "get a sig" << sig <<std::endl;
}

int main()
{
    int cnt = 0;
    signal(3,hander);
    while (true)
    {
        sleep(2);
        abort();
    }
}

 

abort允许捕捉,但是进程还是要终止(因为abort就是用于异常终止的)

 那如果我把所有信号都捕捉完,是不是就不能终止我的进程了?

铁子你在想什么

9号信号不允许自动捕捉啊

直接

kill -9 pid

就老实了

 信号是由他们产生的,那么真正发送信号的是谁?

只有一个:操作系统!

因为发送信号的本质是修改进程PCB中的位图,而只有操作系统有这个资格这样做

软件条件要是管道的读关闭,写一直进行的话

操作系统就会向写进程发送sigpipe信号

我们还可以通过一个函数:alarm,来充当一个闹钟的作用

#include<iostream>
#include<cstdlib>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>

void hander(int sig)
{
    std::cout << "get a sig" << sig <<std::endl;
    exit(0);
}

int main()
{
    int cnt = 1;

    signal(SIGALRM,hander);
    alarm(1);       //设置一秒后的闹钟
    while (true)
    {
        std::cout << "cnt: " << cnt << std::endl;
        cnt++;
    }
    return 0;
}

 

又是新bug

假如不打印呢?

#include<iostream>
#include<cstdlib>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>

int cnt = 1;

void hander(int sig)
{
    std::cout << "cnt: " << cnt << "get a sig: " << sig << std::endl;
    exit(1);
}

int main()
{
    signal(SIGALRM,hander);
    alarm(1);       
    while (true)
    {
        cnt++;
    }
    return 0;
}

 

我们可以发现,IO是一件很慢的事情 

如果单纯++是内存级的数据递增,但是要是打印到显示器上,显示器毕竟是外设,我们还用的云服务器,当云服务器上代码跑完再推送到本地,这个工作量是不可估量的

就拿中指举例,我竖中指再拍照片,蓝色米老鼠存照片,再添加为表情包并发送,这个工作量是难以估量的,所以建议大家直接竖中指

我们的电脑里面有一块小电池,电脑没电了它都有电,专门用来看时间的

而针对于闹钟,操作系统要对它做管理

闹钟是个结构体对象

struct alarm
{
    time_t expired;    //未来的时间 = seconds + Now();
    pid_t pid;
    fuc_t f;
    ...
}

 用最小堆进行管理捏

alarm(0)表示取消闹钟,返回值表示上一个闹钟的剩余时间

闹钟默认只触发一次

#include<iostream>

int main()
{
    int *p = nullptr;
    *p = 100;
    return 0;
}

致命三连问:程序为什么会崩溃?崩溃了为什么会退出?可不可以不退出?

是因为非法访问导致OS给进程发送信号啦!!!

那我们怎么证明呢?

还是老样子,对信号做自定义捕捉就好啦

#include<iostream>
#include<cstdlib>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>

void hander(int sig)
{
    std::cout << "get a sig: " << sig << std::endl;
    exit(1);
}

int main()
{
    signal(SIGSEGV,hander);
    // int a = 10;
    // a /= 0;
    int *p = nullptr;
    *p = 100;
    return 0;
}

对于野指针,信号是11

对于非法的计算(比如除以0),信号是8 

程序崩溃的时候也会向进程发送信号

我崩溃的时候也会向chat发送信号

进程会给OS发信号,最好是退出捏

CPU会帮我们进行两种计算

有种寄存器叫eflag,状态寄存器,是 x86 架构中用于保存处理器标志的寄存器

它是一个 32 位寄存器,其中的每一位代表了处理器的某个状态或控制标志

MMU 主要指的是 “内存管理单元”(Memory Management Unit),它是计算机中的一个重要硬件组件,负责管理计算机系统中的内存。MMU 主要有以下功能:

  1. 地址转换:将虚拟地址转换为物理地址。这对于虚拟内存系统非常重要,因为程序通常使用虚拟地址,而计算机硬件则使用物理地址
  2. 内存保护:通过设置访问权限,防止程序或进程非法访问或修改其他程序或进程的内存区域
  3. 缓存控制:优化内存访问速度,减少对主内存的访问需求

OS是软硬件资源的管理者,要随时处理这种硬件问题,所以会向进程发送信号

出问题了告知你

CR3寄存器保存的是页表的起始地址,还有个寄存器是CR2,当虚拟地址向物理内存中转化失败的时候,失败信息会存放到CR2寄存器里(CR2:页故障线性地址寄存器)

栈溢出是越界捏

core和term

term是异常终止,core也是异常终止,但是会帮我们形成一个debug文件

但是我们运行有问题的程序,却查不到core

这是因为我们核心转储可能被禁用了:

ulimit -c

这个可以查看当前核心转储功能有没被启用

 

为0就是没有启用,可以通过这个命令来启用:

ulimit -c unlimited

 

几个梗图:

 

我们的编译要是在-g下的,这样可以获得更多调试信息

而core的生成不是在当前目录下的,这样可以让core生成在当前目录:

echo "core.%e.%p" | sudo tee /proc/sys/kernel/core_pattern

 

这样就生成了 ,可以用gdb辅助我们进行错误的排查

gdb的具体使用方法都总结在这篇博客里了:

炫酷gdb-CSDN博客icon-default.png?t=O83Ahttps://blog.csdn.net/chestnut_orenge/article/details/138551058 为什么我的显示出来是这个

我真的

懒得喷了

这谁能看得懂啊,我讲个蛋我也不会我下了

我真红温了

爱谁用谁用

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

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

相关文章

Vant 按需引入导致 Typescript,eslint 报错问题

目录 1&#xff0c;按需引入问题2&#xff0c;Typescript 报错解决3&#xff0c;eslint 报错解决 1&#xff0c;按需引入问题 vant4 通过 按需引入的配置 使用组件时&#xff0c;会同时将样式也自动导入。所以可直接使用相关的 API 和组件&#xff0c;不会有问题。比如&#x…

Elasticsearch基础(七):Logstash如何开启死信队列

文章目录 Logstash如何开启死信队列 一、确保 Elasticsearch 输出插件启用 DLQ 支持 二、配置 Logstash DLQ 设置 三、查看死信队列 四、排查 CSV 到 Elasticsearch 数据量不一致的问题 Logstash如何开启死信队列 在 Logstash 中&#xff0c;死信队列&#xff08;Dead Le…

QT 联合opencv 易错点

https://blog.csdn.net/qq_51699436/article/details/135777911 网上已经有大量优秀切详尽的文章来讲述QT联合opencv了&#xff0c;我把容易出错的点列出来备忘 1、在进行opencv进行编译时&#xff0c;要确认好是32位还是64位&#xff0c;因为在创建QT项目的时候QT和opencv要匹…

基于R语言的统计分析基础:使用ggplot2包进行绘图

安装ggplot2包并查看官方文档 ggplot2是一个基于图形语法的R包&#xff0c;它允许用户通过声明式方式指定数据、美学映射和图形元素来灵活创建复杂且美观的可视化图表。 ggplot2包官方教学文档&#xff1a;ggplot2官方文档 在R语言中安装ggplot2有两种方法&#xff1a; 安装整…

【自动驾驶】控制算法(八)横向控制Ⅱ | Carsim 与 Matlab 联合仿真基本操作

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

GEE 将本地 GeoJSON 文件上传到谷歌资产

在地理信息系统&#xff08;GIS&#xff09;领域&#xff0c;Google Earth Engine&#xff08;GEE&#xff09;是一个强大的平台&#xff0c;它允许用户处理和分析大规模地理空间数据。本文将介绍如何使用 Python 脚本批量上传本地 GeoJSON 文件到 GEE 资产存储&#xff0c;这对…

初识C++|继承

&#x1f36c; mooridy-CSDN博客 &#x1f9c1;C专栏&#xff08;更新中&#xff01;&#xff09; 目录 1. 继承的概念及定义 1.1 继承的概念 1.2 继承定义 1.2.1 定义格式 1.2.2 继承父类成员访问方式的变化 1.3继承类模板 2. 父类和子类对象赋值兼容转换 3. 继承中的…

国内外大模型汇总(包括科大星火、文心一言、通义千问、智普清言、华为大模型)

国内外大模型汇总 1. 科大讯飞星火认知大模型 主要特点&#xff1a; 多语言能力&#xff1a;以中文为核心&#xff0c;同时支持多语言处理&#xff0c;能够进行跨语种的语言理解和生成。 广泛的任务能力&#xff1a;具备内容生成、语言理解、知识问答、推理、数学计算、代码…

数学建模笔记—— 主成分分析(PCA)

数学建模笔记—— 主成分分析 主成分分析1. 基本原理1.1 主成分分析方法1.2 数据降维1.3 主成分分析原理1.4 主成分分析思想 2. PCA的计算步骤3. 典型例题4. 主成分分析说明5. python代码实现 主成分分析 1. 基本原理 在实际问题研究中,多变量问题是经常会遇到的。变量太多,无…

小怡分享之栈和队列

前言&#xff1a; &#x1f308;✨前面小怡给大家分享了顺序表和链表&#xff0c;今天小怡给大家分享一下栈和队列。 1.栈 1.1 概念 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#x…

WPF中依赖属性或附加属性的继承

引言 我们可以轻易的编写一个附加属性类&#xff0c;增加任意类型的附加属性并编写一定的逻辑来处理附加值的变化通知。假如控件是我们自定义的一个label、button 、textbox等&#xff0c;自定义控件当然是其他基础类型元素的组合&#xff0c;如shape、line、rectangle、geome…

针对SVM算法初步研究

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd;心态决定高度&#xff0c;细节决定成败…

从OracleCloudWorld和财报看Oracle的转变

2024年9月9-12日Oracle Cloud World在美国拉斯维加斯盛大开幕 押注AI和云 Oracle 创始人Larry Ellison做了对Oracle战略和未来愿景的主旨演讲&#xff0c;在演讲中Larry将AI技术和云战略推到了前所未有的高度&#xff0c;从新的Oracle 23c改名到Oracle23ai&#xff0c;到Oracl…

活动|华院计算宣晓华受邀出席“AI引领新工业革命”大会,探讨全球科技的最新趋势

8月31日&#xff0c;“AI引领新工业革命”大会于上海图书馆圆满落幕。本次大会由TAA校联会和台协科创工委会联合主办&#xff0c;得到上海市台办、上海市台联、康师傅的大力支持。大会邀请了NVIDIA全球副总裁、亚太区企业营销负责人刘念宁&#xff0c;元禾厚望资本创始合伙人潘…

828华为云征文|华为云Flexus X实例docker部署Jitsi构建属于自己的音视频会议系统

828华为云征文&#xff5c;华为云Flexus X实例docker部署Jitsi构建属于自己的音视频会议系统 华为云最近正在举办828 B2B企业节&#xff0c;Flexus X实例的促销力度非常大&#xff0c;特别适合那些对算力性能有高要求的小伙伴。如果你有自建MySQL、Redis、Nginx等服务的需求&a…

[Web安全 网络安全]-文件包含漏洞

文章目录&#xff1a; 一&#xff1a;前言 1.什么是文件包含漏洞 2.文件包含漏洞的成因 3.文件包含漏洞的分类 4.文件包含漏洞的防御策略 5.文件包含函数&#xff08;触发点Sink&#xff09; 6.环境 6.1 靶场 6.2 其他工具 二&#xff1a;文件包含LFI labs靶场实验…

docker-01 创建一个自己的镜像并运行容器

docker-01 创建一个自己的镜像并运行容器 前言 我们都知道使用Docker的镜像可以快速创建和部署应用&#xff0c;大大的节约了部署的时间。并且Docker 的镜像提供了除内核外完整的运行时环境&#xff0c;确保代码的环境一致性&#xff0c;从而不会在出现这段代码在我机器上没问…

YoloV10改进策略:上采样改进|动态上采样|轻量高效,即插即用(适用于分类、分割、检测等多种场景)

摘要 本文使用动态上采样改进YoloV10,动态上采样是今天最新的上采样改进方法,具有轻量高效的特点,经过验证,在多个场景上均有大幅度的涨点,而且改进方法简单,即插即用! 论文:《DySample:Learning to Upsample by Learning to Sample》 论文:https://arxiv.org/pdf/…

老旧电力系统安全隐患增加 该如何预防电气线路老化等因素引发的电气火灾呢?

为应对我国电气火灾事故频发的挑战&#xff0c;安科瑞电气股份有限公司开发了AcrelCloud-6000安全用电管理云平台。这一平台依托移动互联网和云计算技术&#xff0c;结合物联网传感器&#xff0c;将办公楼、学校、医院、工厂、体育场馆、宾馆及福利院等人员密集场所的电气安全数…

0x07 Nginx越界读取缓存漏洞 CVE-2017-7529 复现

参考&#xff1a; Nginx越界读取缓存漏洞 CVE-2017-7529 | PeiQi文库 (wgpsec.org)Nginx越界读取缓存漏洞&#xff08;CVE-2017-7529&#xff09;复现分析 - qweg_focus - 博客园 (cnblogs.com) 一、fofa 搜索 nginx && port"80" 我这里写了个脚本将ip保存…