Linux学习之system V

目录

一,system V共享内存

快速认识接口

 shmget(shared memory get)

shmat(shared memory attach)

shmdt(shared memory delete)

 shmctl (shared memory control)

 编写代码

综上那么共享内存与管道通信有什么区别?

system v消息队列

system v信号量


一,system V共享内存

除了管道通信方式,对于操作系统自己本身,操作系统为了内核中的通信单独设计了通信模块。

进程通信的前提:让不同进程看到同一份资源

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

首先我们从shared memory,共享内存开始,所谓的共享内存,本质上就是将同一份资源,通过加载到物理内存,同时映射到两个进程的内存空间,实现资源共享。

此外,在操作系统中一定会允许多个共享内存,此时就需要操作系统来管理这些共享内存。

快速认识接口

 shmget(shared memory get)

创建共享内存 既能创建,又能获取

参数一为创建的key,参数二为空间大小,

参数三为标记位:

 IPC_CREAT   to create a new segment.  If this flag is not used, then shmget() will find the segment associated with key and check to see . if  the user has permission to access the segment.(没有就创建shm(共享内存))

PC_EXCL    used with IPC_CREAT to ensure failure if the segment already exists.(通常与IPC_CREAT结合使用,不存在就创建,存在就出错返回不创建)。

通过这两个标志位使得我们创建的内存是全新的。

shmat(shared memory attach)

将共享内存挂接到地址空间当中

参数一位shmid,参数2一般设置位nullptr,参数三为标志位表示已什么样的方式挂在,一般也用0。

返回值位起始地址。通过引用计数来计算有多少进程挂在到当前共享内存中。

shmdt(shared memory delete)

去掉内存关联,将共享内存从进程地址空间中移除。

 shmctl (shared memory control)

控制共享内存,第一个参数位shmid,第二个位操作指令,IPC_STAT,IPC_SET, IPC_RMID(删除) ,IPC_INFO

 编写代码

和命名管道通信的方式一样,我们创建了三个文件server,client,commant.cpp,用来观察进程通信:

首先对于读端还是写端都会有同一个key,我们创建同一个key,此时进程就会把key写到地址空间当中,通过Key值操作系统可以找到两个进程的同一块地址空间,即key值指向的内存就是共享内存。

comman.hpp

#pragma once
#include<iostream>
#include<string>
#include<cstdlib>
#include<unistd.h>
const std::string pathname="/home/danchengwei/myfile/file7";
const int proj_id=0x11223344;
const int size=4096;
//管道文件
const std::string filename = "fifo";key_t getkey(){//转换pathname为keykey_t key=ftok(pathname.c_str(),proj_id);//convert a pathname and a project identifier to a System V IPC keyif(key<0){std::cerr<<"errno:"<<errno<<",strerror"<<strerror(errno)<<std::endl;return 1;}std::cout<<"key:"<<key<<std::endl;return key;}std::string tohex(int id){char buffer[1024];snprintf(buffer,sizeof(buffer),"0x%x",id);return buffer;}int creatshmHLPER(key_t key,int flag){int shmid=shmget(key,size,flag); //成光返回id,错误返回-1if(shmid<0){std::cerr<<"errno:"<<errno<<",strerror"<<strerror(errno)<<std::endl;exit(2);}std::cout<<"shmid:"<<shmid<<std::endl;return shmid;}int getshm(key_t key){return creatshmHLPER(key,IPC_CREAT | IPC_EXCL | 0644);}int creatshm(key_t key){return creatshmHLPER(key,IPC_CREAT);}bool MakeFifo()
{int n = mkfifo(filename.c_str(), 0666);if(n < 0){std::cerr << "errno: " << errno << ", errstring: " << strerror(errno) << std::endl;return false;}std::cout << "mkfifo success... read" << std::endl;return true;
}

server

#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include"comman.hpp"
using namespace std;int main()
{//创建共享内存key_t key=getkey();//key在应用层我们一般不去使用,我们使用的是shmid int shmid=creatshm(key);sleep(10);//挂载 ,即将共享内存映射到进程的地址空间当中。char*s=(char*)shmat(shmid,nullptr,0);//打开管道Start:int rfd=open(filename.c_str(),O_RDONLY);if(rfd<0){//cerr<<"errno:"<<errno<<",errstring:"<<strerror(errno)<<endl;if(MakeFifo()){goto Start;}else{return 1 ;}//return 2;}cout<<"打开管道成功..."<<endl;char buffer[1024];while(true){ssize_t s=read(rfd,buffer,sizeof(buffer)-1);if(s>0){buffer[s]=0;//表示以/0结尾cout<<"cilent reply: "<<buffer<<endl;}if(s<=0){cout<<"no data had reciveed"<<endl;//没有数据可读了,退出进程break;}}//关闭管道close(rfd);cout<<"关闭管道成功..."<<endl;sleep(5);//在地址空间当中移除共享内存shmdt(s); sleep(5);//按照id删除shmctl(shmid,IPC_RMID,nullptr);sleep(5);return 0;}

创建完成可以通过指令查看共享内存:

ipcs -m  //查看共享内存
ipcrm -m shmid  //删除共享内存

其中natch表示有几个进程挂接到该共享内存。

程序结束,共享内存被创建之后并没有释放。它的生命周期时内核决定的。

之后我们再完成与server一样的client的编写:

#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include"comman.hpp"
using namespace std;int main()
{//与server一样//获取key值key_t key=getkey();//创建共享内存int shmid=creatshm(key);sleep(10);//把共享内存挂载到地址空间当中char *s=(char*)shmat(shmid,nullptr,0);sleep(5);//打开管道int wfd = open(filename.c_str(), O_WRONLY);//数据写入string message;while(true){cout<<"请输入你要发送的数据:"<<endl;getline(cin,message);//这里再写入管道时,我们使用c++string再转化为c字符串ssize_t t=write(wfd,message.c_str(),message.size());if(t<0){cerr<<"错误码:"<<errno<<",错误原因:"<<strerror(errno)<<endl;break;}}//关闭管道close(wfd);cout<<"关闭管道成功..."<<endl;//取消共享内存的挂载shmdt(s);return 0;
}

之后我们就可以同时执行两个进程并观察会发现 ,刚开始创建共享内存,server与client挂载到地址空间,natch增加,之后移除,之后删除.

有了共享内存,那么我们现在通过共享内存就可以实现进程之间的通信,和管道类似,我们可以增加一个管道文件;之后用一端作为读端,一端作为写端。

综上那么共享内存与管道通信有什么区别?

其实共享内存是比管道通信更加快速的,对于共享内存,考虑硬件:

第一次拷贝是从外设到共享内存,第二次是从共享内存写到显示器上,只需要拷贝两次,对于管道.

而对于管道,先是用户到键盘(外设)把数据读到缓冲区中,再把数据拷贝到管道当中(写入),之后再将管道的数据写道别的用户的缓冲区(读),最后再将数据给到显示器(外设)。对于管道考虑硬件需要拷贝四次数据。

system v消息队列

所谓的消息队列是提供让一个进程给另一个进程发送数据块的能力,使用的接口msgctl,使用方法基本和共享内存大致相同。

参数一为msqid即这里的key,参数二为如何去操作消息队列的指令,参数三一般为nullptr.

创建完成之后可以用指令ipcs -q来查看消息对列。根据接口的第二参数我们可以还选择删除消息队列。可以看到使用与共享内存非常的相似。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<iostream>
#include"comman.hpp"//消息队列
int main()
{//获取key值key_t key=getkey();//创建消息队列int msgid=msgget(key,IPC_CREAT | IPC_EXCL);std::cout<<"msgid:"<<msgid<<std::endl;sleep(10);//删除消息队列msgctl(msgid,IPC_RMID,nullptr);rteurn 0;
}

其次消息队列也可以存在多个,两个进程用多个消息队列进行数据交互,因此在内核当中,操作系统需要管理这些消息队列,先描述在组织,因此消息队列的管理就是消息队列加自身的属性。

关于消息队列大家感兴趣可以查看手册的使用。

system v信号量

信号量主要用于同步和互斥的,当进程们面对同一份资源时,我们的资源就需要被保护起来,而信号量就是为了保护我们的共享资源。

因此我么先来了解同步与互斥:

进程互斥
程互斥则是一种防止多个进程同时访问同一资源的机制。在进程互斥的机制下,当一个进程正在访问某个共享资源时,其他进程需要等待该进程释放该资源之后才能访问。实现进程互斥的主要方法有互斥锁和信号量。

进程同步

指的是多个进程在共享资源的过程中保持一致性的机制。其主要目的是避免进程间的竞争和冲突,确保多个进程对共享资源的访问按照一定的顺序和规则进行,从而避免资源的竞争和冲突。

进程同步的实现方法包括信号量、互斥锁、条件变量等。
 

对于linux系统就是用洗脑两盒互斥锁来解决同步与互斥的问题。

信号量(semaphore),我们在linux中也有对应的函数接口:

semget 获取创建信号量

semctl 控制信号量

数1为semid,参数二为编号(起始从0开始),参数三为指令。

semop 操作信号量

对信号量做加加减减操作

 

可以看到无论是共享内存,还是消息对列或者信号量,他们都是有统一的标准的接口,因此这里的函数我们也能使用了。

返回值一般我们叫做semid,参数一为key值,参数二为创建的个数,参数三为标志.

利用指令ipcs -s 可以查看创建的信号量,可以看到创建完进程结束,但信号量还在,因此生命周期还是随内核。

当然对于信号量,也是需要被操作系统管理,管理信号量及其信号量属性。

其次内核是怎样看待IPC资源(共享资源):

首先肯定有两部分:1.会有单独设计的一个模块。2.会有特定的保护机制。

所谓的信号量本身就是一个计数器,为了让进程通信,多个执行流共享同一份资源,而公共资源被并发访问就会产生数据不一致的问题,因此我们就需要保护我们的共享资源来面对同步与互斥的两个情况。

解决同步与互斥简单来说就是,互斥:任何一个时刻只允许一个执行流来使用该资源。同步:多个执行流都来使用该资源时,让他按照一定的顺序来执行。

信号量通俗点说,就是资源数目的计数器,每一个执行流想要访问资源内的某一份资源,不应该执行流直接访问,而是先申请信号量资源,(其实就是对计数器信号量--操作),申请成功后,就完成了对资源的预定机制,如果申请不成功,则执行流就进行阻塞。

我们在编写代码时如果想要访问这一份共享资源,先要申请信号量资源,如果成功,则返回之后的信号量,失败就阻塞。而阻塞的这一部分资源就是临界资源。

但也有特殊情况,只有一个信号量即只允许一个人访问的资源,这种信号量被叫做二元信号量,即就是一个锁的功能---互斥锁。

原子性:只有两种状态,要么不做,要么做完。

本次我们主要先 认识一下。

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

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

相关文章

《Effective C++》- 极精简版 41-55条

本文章属于专栏《业界Cpp进阶建议整理》 继续上篇《Effective C》- 极精简版 31-40条。本文列出《Effective C》的41-55条的个人理解的极精简版本。 40、谨慎使用多重继承 个人见解&#xff1a;这是一个性价比不高的功能&#xff0c;容易出错&#xff0c;且收益不高。java语言…

代码随想录训练营总结篇

1:刚开始学习了数据结构的时候,碰到一些树图等有点畏难,感觉刷力扣跟是一件恐怖的事情 2:在网上找了很多关于数据结构与算法的视频讲解,有听进去,但是自己动手写有点困难 3:碰到一些比较复杂的问题时需要与人讨论,加了一些代码随想录中的群,发现这里是学习算法 的天堂,大家都…

【深度学习笔记】深度卷积神经网络——NiN

网络中的网络&#xff08;NiN&#xff09; LeNet、AlexNet和VGG都有一个共同的设计模式&#xff1a;通过一系列的卷积层与汇聚层来提取空间结构特征&#xff1b;然后通过全连接层对特征的表征进行处理。 AlexNet和VGG对LeNet的改进主要在于如何扩大和加深这两个模块。 或者&am…

Android 14 权限

问题Android 14 按照视频播放类的应用 无法获取到权限。 原因是从 Android 13 开始&#xff0c;如果你的应用程序 targetSdk 指定到了 33 或以上&#xff0c;那么 READ_EXTRERNAL_STORGE 权限就完全失去了作用&#xff0c;申请它将不会产生任何效果。 与此相对应的&#xff0c…

C编程学习资源汇总

Study Soures Pool 基础:推荐 C Primer Plus 第6版 中文版 + C Primer Plus 第6版 中文版习题解答 进阶:推荐使用小甲鱼的学习视频,课程细致,而且免费,资源很丰富。 首页:鱼C工作室-免费编程视频教学|Python教学|Web开发教学|全栈开发教学|C语言教学|汇编教学|Win32开发…

诉诸存储和传输的编码

用脑补而不是重传对有损传输进行纠错 后&#xff0c;有朋友评论&#xff1a; 可现代的图像压缩、数据编码已经很大程度上把可以脑补的空间从传输载荷中沥干了——完美编码下所传输的数据是近乎噪声的没有任何特点的分布。在这个框架下&#xff0c;被压缩的载荷如果随便丢了一点…

Linux------进程地址空间

目录 一、进程地址空间 二、地址空间本质 三、什么是区域划分 四、为什么要有地址空间 1.让进程以统一的视角看到内存 2.进程访问内存的安全检查 3.将进程管理与内存管理进行解耦 一、进程地址空间 在我们学习C/C的时候&#xff0c;一定经常听到数据存放在堆区、栈区、…

4、正则表达式、本地存储

一、正则表达式 1、定义 用事先定义好的一些特定字符&#xff0c;这样的字符组合&#xff0c;组合成一个“规则字符串” 2、正则的组成 特殊字符 字母、数字、下划线、中文、特殊字符… 元字符&#xff08;常用&#xff09; 1、\d 匹配至少有一个数字 var reg /\d/ /…

2024Java面试题一

目录 问题&#xff1a; 答案&#xff1a; 问题&#xff1a; 什么是Java中的多态性&#xff08;Polymorphism&#xff09;&#xff1f;Java中的重载&#xff08;Overloading&#xff09;和重写&#xff08;Overriding&#xff09;有什么区别&#xff1f;什么是Java中的抽象类&…

XGB-13:使用 XGBoost 外部内存版本

在处理大型数据集时&#xff0c;训练 XGBoost 模型可能会面临挑战&#xff0c;因为整个数据集需要加载到内存中。这可能成本高昂&#xff0c;有时也难以实现。从版本 1.5 开始&#xff0c;用户可以定义自定义迭代器以按块加载数据来运行 XGBoost 算法。外部内存可以用于训练和预…

每日一题Acwing-借教室

503. 借教室 - AcWing题库 想到了差分没想到二分法。同时注意INT的范围是10位。 #include<iostream> using namespace std; int n,m; const int N 1e610; int r[N],s[N],d[N],t[N]; long long b[N]; bool check(int mid){for(int i1;i<n;i){b[i]r[i]-r[i-1];}for(…

SpringBoot整合rabbitmq-直连交换机队列(二)

说明&#xff1a;本文章主要是Direct定向/直连类型交换机的使用&#xff0c;它的大致流程是将一个队列绑定到一个直连交换机上&#xff0c;并赋予一个路由键 routingkey&#xff0c;当一个消息携带着路由值为routingkey&#xff0c;这个消息通过生产者发送给交换机时&#xff0…

【冲击蓝桥篇】动态规划(下):你还在怕动态规划!?进来!答题模板+思路解析+真题实战

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《数据结构与算法&#xff1a;初学者入门指南》&#x1f4d8;&am…

【vue】vue 是怎么把 template 模版编译成 render 函数的,什么是AST抽象语法树

什么是AST 抽象语法树 是一个对象/或者json是一个数据结构 AST通常是由多个节点组成的树状结构&#xff0c;每个节点代表一个语法单位或表达式。节点之间的关系通过父子关系或兄弟关系来表示程序的结构。在不同的编程语言和工具中&#xff0c;AST可能有不同的表示方式和节点类…

Python中检查一个数字是否是科技数的完整指南

目录 前言 什么是科技数&#xff1f; 如何判断一个数字是否是科技数&#xff1f; 分割数字并计算平方 Python实现科技数检测的示例代码 科技数的应用场景 1. 数字游戏 2. 数据处理 3. 算法优化 4. 数据结构设计 总结 前言 科技数&#xff08;Tech Number&#xff09;是一…

(二十三)Flask之高频面试点

目录&#xff1a; 每篇前言&#xff1a;Q1&#xff1a;为什么把request和session放在一起&#xff1f;Q2&#xff1a;Local对象的作用&#xff1f;Q3:&#xff1a;LocalStack对象的作用&#xff1f;Q4&#xff1a;一个运行中的Flask应用程序分别包括几个Local/LocalStack&#…

若依前后端分离版开源项目学习

前言&#xff1a;vscode中vue代码没有高亮显示&#xff0c;可以下载vetur插件解决&#xff0c;ctrl点击无法跳转函数定义问题&#xff0c;可以下载vue-helper插件解决&#xff1b;idea中ctrl点击函数即可跳转函数定义。 一、登录 1.生成验证码 基本思路&#xff1a; 后端生…

vue a-table 实现指定字段相同数据合并行

vue a-table 实现相同数据合并行 实现效果代码实现cloums数据格式数据源格式合并代码 实现效果 代码实现 cloums数据格式 const getColumns function () {return [{title: "分类",dataIndex: "checked",width: "150px",customRender: (text, …

JMeter--9.录制脚本

录制步骤 1.新建线程组&#xff1a;测试计划->线程->线程组 测试计划下&#xff0c;至少要有1个线程组&#xff0c;因为在录制器中需要选择【目标控制器】 2. 新建录制器&#xff1a;测试计划->非测试原件->HTTP(S)测试脚本记录器&#xff08;HTTP代理服务器&…