信号保存和处理

把上一篇回顾一下吧:共享内存区是最快的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 serverclient:client.ccg++ -o $@ $^ -std=c++11
server:server.ccg++ -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 为什么我的显示出来是这个

我真的

懒得喷了

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

我真红温了

爱谁用谁用

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

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

相关文章

QT实现TCP/UDP通信

服务器端&#xff1a; 客户端&#xff1a; 服务器&#xff1a; widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> #include <QTcpSocket> #include <QList> #include <QMessageBox> #include <QDebug&…

point transformer v3复现及核心代码详解

point transformer v3复现及核心代码详解 1. 复现1.1 复现1.2 数据预处理1.3 跑通 2. 核心代码详解2.1 读取数据2.2 dataloder2.3 模型读取数据的逻辑2.4 forward2.4.1 Point2.4.2 backbone2.4.2.1 point.serialization2.4.2.2 稀疏化2.4.2.3 embedding2.4.2.4 encoder 1. 复现…

Emlog程序屏蔽用户IP拉黑名单插件

插件介绍 在很多时候我们需要得到用户的真实IP地址&#xff0c;例如&#xff0c;日志记录&#xff0c;地理定位&#xff0c;将用户信息&#xff0c;网站数据分析等,其实获取IP地址很简单&#xff0c;感兴趣的可以参考一下。 今天给大家带来舍力写的emlog插件&#xff1a;屏蔽…

wakenet尾迹

1、数据集介绍SWIM_Dataset_1.0.0 1.1标注文件介绍 标注文件介绍&#xff0c; 第一种&#xff1a;角度和框的坐标 <annotation><folder>Positive</folder><filename>00001</filename>文件名字<format>jpg</format>图片后缀<s…

自掘坟墓?开源正在卷爆程序员!

前端训练营&#xff1a;1v1私教&#xff0c;终身辅导计划&#xff0c;帮你拿到满意的 offer。 已帮助数百位同学拿到了中大厂 offer Hello&#xff0c;大家好&#xff0c;我是 Sunday。 今天这篇文章其实我想了好久&#xff0c;因为这并不是一个 和光同尘 的话题&#xff0c;它…

第143天:内网安全-权限维持自启动映像劫持粘滞键辅助屏保后门WinLogon

案例一&#xff1a; 权限维持-域环境&单机版-自启动 自启动路径加载 路径地址 C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\StartMenu\Programs\Startup\ ##英文C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\开始菜单\程序\启动\ ##中文…

OpenHarmony鸿蒙( Beta5.0)智能窗户通风设备开发详解

鸿蒙开发往期必看&#xff1a; 一分钟了解”纯血版&#xff01;鸿蒙HarmonyOS Next应用开发&#xff01; “非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线&#xff01;&#xff08;从零基础入门到精通&#xff09; “一杯冰美式的时间” 了解鸿蒙HarmonyOS Next应用开发路…

如何逆转Instagram账号流量减少?实用技巧分享

Instagram作为全球十大社媒之一&#xff0c;不仅是个人分享生活的平台&#xff0c;还是跨境卖家进行宣传推广和客户开发的关键工具。在运营Instagram的过程中&#xff0c;稍有不慎就容易出现账号被限流的情况&#xff0c;对于账号状态和运营工作的进行都十分不利。 一、如何判断…

isis与ospf高级属性

文章目录 前言一、基础配置(配置各设备的IP地址)二、配置各设备的ospf与isis三、检查ospf与isis邻居是否建立成功1.实现快速重路由2.流量过滤方法3.引入默认路由4.配置等价路由 前言 在下面实验中&#xff0c;蓝色区域运行ospf&#xff0c;为了控制ospf的lsdb数量&#xff0c;…

vue页面使用自定义字体

一、准备好字体文件 一般字体问价格式为 .tff&#xff0c;可以去包图网等等网站去下载&#xff0c;好看的太多了&#xff01;&#xff01;&#xff01; 下载下来就是单个的 .tff文件&#xff0c;下载下来后可以进行重命名&#xff0c;但是不要改变他的后缀名&#xff0c;我把他…

【c++】类和对象详解

✅博客主页:爆打维c-CSDN博客​​​​​​ &#x1f43e; &#x1f539;分享c语言知识及代码 来都来了! 点个赞给博主个支持再走吧~&#xff01; 一.类的定义 &#xff08;1&#xff09;类定义格式 class为类定义的关键字&#xff0c;定义一个类格式如下: class 类名{//代码…

turtle.circle() 函数绘制弧形规律助记图 ← Python

【Python 之 turtle.circle() 函数定义】 定义&#xff1a;turtle.circle(radius, extent)作用&#xff1a;根据半径 radius 绘制 extent 角度的弧形参数&#xff1a;radius &#xff1a;弧形半径当 radius 值为正数时&#xff0c;圆心在当前位置/小海龟左侧。当 radius 值为负…

9月美联储决策前哨战——美国CPI数据来袭

随着本周关键CPI数据的即将发布&#xff0c;市场正翘首以待&#xff0c;这将是美联储在9月17日至18日议息会议前获取的最后一块重要经济拼图。鉴于美联储官员已进入传统的政策静默期&#xff0c;8月份的CPI报告无疑将成为交易员们评估未来货币政策走向的重要标尺。 欧洲央行降…

[000-01-002].第03节:Git基础命令

我的博客大纲 我的GIT学习大纲 1、Git的常用命令 2、Git操作步骤&#xff1a; 2.1.操作Git第一步&#xff1a;设置全局的用户签名 1.设置用户名&#xff1a; 格式&#xff1a;git config --global user.name 用户名命令&#xff1a;git config --global user.name root 2.设置…

Taro + Vue 的 CSS Module 解决方案

一、开启模块化配置 Taro 中内置了 CSS Modules 的支持&#xff0c;但默认是关闭的。如果需要开启使用&#xff0c;请先在编译配置中添加如下配置&#xff1a; weapp: {module: {postcss: {// css modules 功能开关与相关配置cssModules: {enable: true, // 默认为 false&…

如何解决户用光伏项目管理难题?

户用光伏作为分布式能源的重要组成部分&#xff0c;正迎来前所未有的发展机遇。户用光伏项目的复杂性和多样性也给项目管理带来了诸多挑战&#xff0c;包括客户分散、安装周期长、运维难度大、数据监控不及时等问题。为解决这些难题&#xff0c;构建一套高效、智能的户用光伏业…

SpringMVC基于注解使用:国际化

01-国际化介绍 首先在bootstrap下载个页面 下载后把登录页面的代码粘上去 然后再登录页面代码上有些超链接需要再spring-mvc.xml里面配置下&#xff0c;登录页面才能正常显示 配置静态资源 国际化-根据浏览器语言国际化 现在是中文的情况&#xff0c;要改为英文 1.配置下属…

OFDM信号PARP的CCDF图

文章目录 引言代码代码疑难解答参考文献 引言 本书主要参考了文献1&#xff0c;但实际上该书中符号和表述的错误非常多&#xff08;只能说棒子是这样的&#xff09;&#xff1b;同时因为发表时间的关系&#xff0c;很多MATLAB代码进行了更新&#xff0c;原书提供的代码已经无法…

Flutter中自定义气泡框效果的实现

在用户界面的设计中&#xff0c;气泡框&#xff08;Bubble&#xff09;是一种非常有效的视觉工具&#xff0c;它可以用来突出显示信息或提示用户。气泡框广泛应用于聊天应用、通知提示等场景。在 Flutter 中&#xff0c;虽然有很多现成的气泡框组件&#xff0c;但如果你想要更多…

使用豆包MarsCode 编写 Node.js 全栈应用开发实践

以下是「豆包MarsCode 体验官」优秀文章&#xff0c;作者狼叔。 欢迎更多用户使用豆包MarsCode 并分享您的产品使用心得及反馈、创意项目开发等&#xff0c;【有奖征集&#xff5c;人人都是豆包MarsCode 测评官&#xff01;】活动正在火热进行中&#xff0c;欢迎大家投稿参加&a…