当前位置: 首页 > news >正文

Linux -- SysremV 共享内存通信

一、共享内存通信直接原理

1.1 进程间通信的本质是:先让不同的进程看到同一份资源;
1.2 共享内存通信图解:

①:第一步进程A向物理内存中申请创建一块资源空间;

②:第二步申请成功后把这块内存对应的虚拟地址挂载到进程A的地址空间中;

③:第三步进程B向物理内存中获取进程A已创建好的资源,

④:进程B获取成功后,把对应的虚拟地址挂载到进程B的地址空间中;

以上步骤实现完就已经建立了通信的前提:两个不同的进程看到同一块资源,可以开始通信了!

⑥:通信结束后进程A取消对这块共享内存的关联,进程B也取消对这块共享内存的关联;

⑦:最后一步:谁把这块共享内存创建出来的谁就来释放,这里明显是进程A来创建的所以只需要进程A来释放,进程B啥事也不用做;

1.3如何保证不同进程看到同一块共享内存?怎么知道这块共享内存存在还是不存在??

介绍一个接口:

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char* pathname,int proj_id);
//这是一个用来获取key的接口,这个key具有唯一性
// pathname、proj_id :通过这两货运用算法生成一个重复概率比较低的key值,pathname和proj_id参数可以随便传
// 返回值key_t :如果获取失败返回值小于0,如果成功返回key值

通过这个key值向内存中申请一块空间,确保了这块共享内存空间的唯一性!

有了key值那就来创建一块共享内存空间吧!

介绍一个接口:

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key,size_t size,int shmflg);
//1. 这是一个通过key值来创建或者获取共享内存的接口,注意这里创建跟获取的意思:
创建:申请的这块共享内存空间必须不存在,存在就出错返回,确保这块共享内存一定是新的,确保了‘我’一定是第一个创建的!!
获取: 获取的意思是这块共享内存不存在就创建,存在就获取并返回,即获得的这一块共享内存并不一定是新的!!有可能是已经被别人创建好的了!!
//2. key -> 一个已经通过接口函数创建好的key值;size -> 想要创建内存空间的大小,这里最好是4096的整数倍,因为你申请4097系统会给你8192shmflg-> 通过这个参数传参使这个接口函数的功能发生改变,如果单单传的是IPC_CREAT功能就是获取,如果传IPC_CREAT|IPC_EXCL|0666  功能等于创建,这里的0666是权限根据自己需求写;int返回值 -> 创建失败会返回<0;创建成功返回一个值,这个值就是shmid作用相当于key,只不过shmid是给用户层使用的,用户可以通过shmid找到这块共享内存对其进行操作,而key是共享内存的内核数据结构,用来标志这块内存的唯一性,是操作系统内核数据结构用的;

OK,接口有了,那我们就来创建一块共享内存吧!

二、共享内存的创建

2.1 在.hpp文件中实现创建跟获取方法,在进程中直接调用
//.hpp
#ifndef __COMM__
#define __COMM__
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define PATH_NAME "/home/LDCWD/learncode"
#define PROJ_ID 12345
#include <cstdio>
#include <cstdlib>
#define SHMSIZE 4096
key_t GetKey()
{key_t key=ftok(PATH_NAME,PROJ_ID);if(key<0){perror("ftok");exit(-1);}return key;
}
int GetSHMmethod(int shmflg)
{key_t key=GetKey();int shmid=shmget(key,SHMSIZE,shmflg);if(shmid<0){perror("shmget");exit(-2);}return shmid;
}
int CreatSHM()//创建
{return GetSHMmethod(IPC_CREAT|IPC_EXCL|0666);
}
int GetSHM()//获取
{return GetSHMmethod(IPC_CREAT);
}#endif
//进程A
#include "comm.hpp"
int main()
{int shmid=CreatSHM();return 0;
}

编译运行后查看共享内存:(命令:ipcs -m)

运行前:

编译ProcessA并运行:

可以看到我们成功创建好一块共享内存,shmid为13,key为0x39010dd7 ,权限为666,大小为4096字节;

2.2 共享内存的生命周期

当程序运行结束后,我们发现这块共享内存依然存在!

由此可得出结论:共享内存的生命周期是随内核的!用户不主动关闭,共享内存会一直存在!(除非内核重启或者用户释放!)

注意:这里的nattch是关联的意思,已经就是当前的共享内存有0个进程关联,即当前共享内存挂载到0个进程当中,ok!接下来我们把共享内存挂载到进程中看看nattch是否会发生变化?

2.3 共享内存的关联

介绍两个接口:

#include <sys/types.h>
#include <sys/shm.h>
void* shmat(int shmid,const void* shmaddr,int shmflg);
//1. 这是一个把共享内存挂载到进程地址空间中的函数接口
//2. shmid -> 用key创建内存时的返回值
//3. shmaddr -> 设为null让操作系统去做,shmflg也设为0int shmdt(const void* shmaddr);
//1. 这是一个取消共享内存关联的函数接口
//2 . shmaddr -> 只要告诉起始地址
//3. int -> 返回值如果取消失败返回-1

接下来我们要做的是:

①挂载到进程A;

②取消进程A与共享内存的关联;

查看nattch的变化!

代码:

int main()
{int shmid=CreatSHM();sleep(3);char *shmptr=(char*)shmat(shmid,NULL,0);//挂载sleep(3);int n=shmdt(shmptr);//取消关联if(n==-1){perror("shmdt");exit(-3);}return 0;
}

运行3s后nattch由0变1,接下来3s后nattch由1变0

所以关联与取消关联成功!

接下来再介绍一个接口:

#include<sys/ipc.h>
#inlcude <sys/shm.h>
int shmctl(int shmid, int cmd ,struct shmid_ds* buf);
//1. 这是一个释放共享内存的接口函数,也可以是一个获取共享内存属性的接口函数
//2. 当需要释放内存的时候,shmid传入的还是那个shmid,cmd需要设置为 IPC_RMID,并且 buf设空
//3. 当需要获取共享内存属性时,shmid还是那个shmid ,cmd需要设置为 IPC_STAT,buf需要设为共享内存数据结构的地址;获取后再通过访问这个内存数据结构体的成员变量获得;
2.4共享内存的属性获取与共享内存的释放

增加代码后编译运行:

int main()
{int shmid=CreatSHM();//创建共享内存sleep(3);char *shmptr=(char*)shmat(shmid,NULL,0);//关联sleep(3);int n=shmdt(shmptr);//取消关联if(n==-1){perror("shmdt");exit(-3);}//获取属性并打印struct shmid_ds shmds;shmctl(shmid,IPC_STAT,&shmds);std::cout<<"atime:"<<shmds.shm_atime<<" "<<"nattch:"<<shmds.shm_nattch<<" "<<std::endl;//释放共享内存shmctl(shmid,IPC_RMID,NULL);return 0;
}

共享内存属性成功打印出来,并成功释放掉共享内存!

以上进程A的创建共享内存并关联还有释放工作已经就绪,接下来就是进程B去获取进程A已创建好的共享内存,并挂载到自己的进程空间地址中,然后就可以开始通信了!!!

三、进程间通信

3.1进程B的工作
//进程B
#include "comm.hpp"
int main()
{int shmid=GetSHM();//获取共享内存char *shmptr=(char*)shmat(shmid,NULL,0);//关联sleep(3);int n=shmdt(shmptr);//取消关联if(n==-1){perror("shmdt");exit(-3);}return 0;
}

我们让进程A创建好后休眠两秒再关联然后休眠10s后再取消关联,进程B获取后直接关联,然后休眠三秒后再取消关联,先运行进程A再运行进程B,同时查看nattch的变化:

写一下makefile:

//makefile
.PHONY:all
all:ProcessA ProcessB
ProcessA:ProcessA.ccg++ -o $@ $^ -std=c++11
ProcessB:ProcessB.ccg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -f ProcessA ProcessB

make之后生成两个进程:

先运行A再运行B:

while :; do ipcs -m ;sleep 1;done 查看:

可以看到nattch从A创建出来从 0,到A挂载-> 1 到B挂载 -> 2 到B 取关 -> 1 到A取关 -> 0 到A释放内存  -> 空的过程!!

3.2开始通信

我们让B进程往共享内存里写,A进程从共享内存中读取

进程A通信代码:

进程B通信代码:

运行 A 后运行 B:

注意此时我还没开始运行B ,A已经开始读起来了!

运行B后输入内容:

进程B:

进程A:

B进程:

A进程:

OK,到这里进程A跟进程B已经实现通信了!!

四、共享内存通信特点

4.1、共享内存没有同步与互斥之类的保护机制;
4.2、共享内存是所有通信中速度最快的,原因:拷贝少

例:管道通信:

共享内存通信直接往内存里写或者读就行,不需要拷贝!

4.3、共享内存内部的数据由用户自己维护;

五、利用管道实现共享内存通信的同步

5.1、实现方法,思路

利用管道实现同步的思路:①首先要由一个进程负责管道的创建与删除工作;

②让读端在从共享内存里读取数据前先从管道里读内容,这个内容是写端写入的内容,如果读取到说明写端已经向共享内存完成写入,就可以向共享内存中读取数据了!!

③让写端在往共享内存中写好数据后,接着往管道里写入内容,表面共享内存写端已写入完成,可以开始读取了!!

由以上三个步骤可以实现同步!

5.2 代码,测试

向头文件中加入Init类,用来对管道的创建与删除工作:

class Init
{public:Init(){//创建命名管道int retmkfifo=mkfifo(FIFOFILE_NAME,MODE);if(retmkfifo==-1){perror("mkfifo");exit(-6);}}~Init(){//删除管道文件int retunlink=unlink(FIFOFILE_NAME);if(retunlink==-1){perror("unlink");exit(-7);}}
};

在A进程中实例化对象,并在读共享内存之前打开管道文件并读取:

在进程B中写入共享内存完成后向管道写入内容,表示可以读了:

可以看到A进程运行后没有立马开始读,在等待B进程输入:

B进程输入一条消息后A进程不会一直输出,只会打印一条消息!!

OK!!!以上关于实现共享内存通信的所有基本步骤完成!!!

如果对您有所帮助麻烦点赞收藏+关注哦!!谢谢!!!

咱下期见!!!

http://www.xdnf.cn/news/223363.html

相关文章:

  • 环形链表的约瑟夫问题
  • 从生成到上线:飞算JavaAl工程化能力拆解(含K8s部署脚本自动生成)
  • STM32printf重定向到串口含armcc和gcc两种方案
  • 高并发内存池(五):性能测试与性能优化
  • Java实现归并排序算法
  • 《机器学习中的过拟合与模型复杂性:理解与应对策略》
  • 量化交易之数学与统计学基础2.3——线性代数与矩阵运算 | 线性方程组
  • windows 下 oracle 数据库的备份与还原
  • SQL Server连接异常 证书链是由不受信任的颁发机构颁发的
  • 垃圾收集GC的基本理解
  • 服务容错治理框架resilience4jsentinel基础应用---微服务的限流/熔断/降级解决方案
  • 通过IP计算分析归属地
  • 知识图谱系列(1):基础概念与发展历程
  • ubuntu22.04出现VFS: Unable to mount root fs on unknown-block(0,0)
  • 网络规划和设计
  • ceph存储原理
  • 人格伤疤测试:发现内心深处的情感创伤
  • 【今日三题】kotori和气球(排列) / 走迷宫(BFS最短路) / 主持人调度(二)(贪心+优先级队列)
  • 服务端字符过滤 与 SQL PDO防注入
  • [C语言]猜数字游戏
  • 软件系统容量管理:反模式剖析与模式应用
  • 什么是环境变量,main函数的命令行参数的概念和作用,以及进程地址空间详解
  • antd中的表格穿梭框(Transfer)如何使用
  • 类和对象 (拷贝构造函数和运算符重载)上
  • MySQL学习总结
  • 华锐视点历经十八年沉淀所形成的产品特性
  • 【AI平台】n8n入门4:n8n云创建工作流(无须搭建,快速试用14天)
  • TypeScript 全局类型声明文件规范性分析与归纳
  • 赛事季突围!备战2025全国信息素养大赛 python挑战赛~
  • JavaScript 相关知识点整理