Linux进程间通信之共享内存

📟作者主页:慢热的陕西人

🌴专栏链接:Linux

📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言

本博客主要内容讲解共享内存原理和相关接口的介绍,以及一个案例的展示

文章目录

  • system V共享内存
    • 1.共享内存的原理
    • 2.直接写代码--编写代码进行原理介绍
      • 2.1shmget接口的介绍
      • 2.2key值为什么需要用ftok生成
      • 2.3ftok接口
      • 2.3三个命令
      • 2.4shmat和shmdt
    • 3.通信测试
    • 4.代码

system V共享内存

共享内存属于是,操作系统单独为我们设计的进程间通信方式,最后慢慢演变成为了systemV的一种标准。它是属于文件系统之外的,但是它属于专门为了通信而设计的内核模块,我们称为systemV的IPC通信机制。

但是进程是具有独立性的,所以我们要实现进程间的通信,首先第一点就是要做到两个进程看到同一份资源

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

1.共享内存的原理

首先我们用一个例子来介绍:首先存在两个进程,我们画出对应的地址空间和页表。首先和我们之前的知识一样,进程内部的PCB内部有一个指针指向其对应的内存空间,然后地址空间和物理内存的映射关系存储在每个进程对应的页表中,唯独不同的是这时候页表映射到物理内存空间上却是同一份空间并且这个空间就是我们所谓的共享内存。

宏观控制共享内存的三个步骤:

  • ①创建
  • ②关联进程和取消关联
  • ③释放共享内存

image-20231130191729999

2.直接写代码–编写代码进行原理介绍

2.1shmget接口的介绍

  • 作用:用于申请system V的共享内存空间

  • 参数key :这个参数由我们的另一个接口生成:ftok:关于这个接口后面会介绍

  • 参数size :申请共享内存空间的大小

  • 参数 shmflg:

    这里我们介绍其中的两个:

    a. IPC_CREAT: 创建一个共享内存,如果共享内存不存在,就创建,如果已经存在,获取已经存在的共享内存并返回。

    b. IPC_EXCL:不能单独使用,一般要配合IPC_CREAT来使用,含义是创建一个共享内存,如果共享内存不存在,就创建,如果已经存在,则立马出错返回 – 如果创建成功,对应的共享内存一定是最新的共享内存!

  • 返回值:成功返回对应的共享内存标识符,失败的话返回-1,并且errno被设置。

image-20231130195042509

2.2key值为什么需要用ftok生成

首先系统中可以用shm通信,是不是只能有一对进程使用共享内存呢?可以!->所以,在任何一个时刻,可能有多个共享内存在被用来进行通信->所以系统中一定会存在很多shm同时存在->OS要不要整体管理所有的共享内存呢?要!-> OS如何管理多个shm内存呢?先描述,再组织。所以共享内存,不是我们想象的那样,只要在内存中开辟空间即可,系统也要为了关了shm,构建对应的描述共享内存的结构体对象!!

共享内存 = 共享内存的内核数据结构(伪代码:struct shm) + 真正开辟的内存空间。

2.3ftok接口

  • 参数pathname:路径字符串
  • 参数proj_id:项目id

image-20231130213324725

在操作系统内部就是将一块块共享内存通过这种方式组织起来的,那么进程调用ftok通过pathname和proj_id来生成对应的key(也就是申请对应的共享内存空间),那么我们的进程B就可以通过对应的key来寻找对应的共享内存空间。从而实现进程间通信的本质:两个进程看到同一块资源,所以这个key本质是在内核中使用的。

image-20231130215301165

2.3三个命令

  • ipcs -m

    查看系统中存在的共享内存:

    image-20231204172852789

  • ipcrm -m shmid

    删除shmid所对应的共享内存,共享内存的声明周期不随进程,随OS。

    image-20231204173020261

  • shmctl

    这是一个函数接口:用来查看共享内存的一些相关属性,前提是我们执行这个进程的用户具有查看当前共享内存属性的权限,否则无法查看,一般sudo运行即可。或者创建时权限修改为066即可.

    image-20231204203451118

    代码:

        struct shmid_ds ds;int n = shmctl(shmid, IPC_STAT, &ds);if(n == -1){cerr << errno << ":" << strerror(errno) << endl;exit(3);}cout << "prem:" << toHex(ds.shm_perm.__key) << endl;cout << "creater pid :" << ds.shm_cpid << ":" << getpid() << endl;
    

    运行效果:

    image-20231204203711729

2.4shmat和shmdt

这两个接口相关的是nttach参数,他表示的有多少个进程和当前的共享内存挂载的。

  • shmat

用于进程和共享内存产生关联,非常类似于malloc,返回一个void指针,一般我们把这个指针强制转换成char*;

image-20231205133313831

它内部也有一些参数,值得我们去学习和研究:

第一个参数我们已经很熟悉了共享内存描述符是个整数。

  • shmaddr:

    文档中的大概含义是,假如我们传入的是nullptr,那么操作系统就会自动帮我们分配一个未被使用过的合适的地址去链接这份共享内存

    如果shmaddr不为空,并且shmflg中指定了SHM_RND,则连接发生在地址上,并且该地址等于shmaddr,并四舍五入到最接近SHMLBA的倍数。否则shmadr必须是一个与页面对齐的地址,在这个地址上发生附加操作。

    image-20231205134727851

  • shmflg中的一些:

比如:SHM_RDONLY

image-20231205134520832

  • shmdt

    用于进程和对应的共享内存取消关联,相当于我们c语言当时学习的动态内存管理部分的free函数。

    image-20231205170541704

3.通信测试

那么好了总体来说我们要做四步:我们直接把这四步封装成一个类Init;

①获取key

②通过k获取shmid

③链接共享内存

④断开共享内存

#define SERVER 0x1
#define CLIENT 0x2class Init
{//构造函数
public:Init(int T) :type(T){//1.创建keykey_t k = getKey();//2.获取shmidif(type == SERVER) shmid = creatShm(k, gsize);else shmid = getShm(k, gsize);//3.链接共享内存start = attachShm(shmid);}~Init(){//1.切断共享内存dettachShm(start);//2.释放共享内存if(type == SERVER) delShm(shmid);}char* getstart(){return start;}private:char* start; //共享内存的地址int type;  //辨别对象是server还是clientint shmid; //共享内存标识符
};

运行结果:

在这里插入图片描述

4.代码

server.cc

#include"comm.hpp"int main()
{Init server = Init(SERVER);//通信char* start = server.getstart();//输出从共享内存中读取到的信息int n = 0;while(n <= 30){cout << "client->server #" << start << endl;sleep(1);n++; }//拓展内容//1.client写完了,才通知让server读取,刚开始,一定先让client运行,一个管道//2.命名管道带进来//3.client写完了,才通知让server读取,读取完了,才让client进行写入,两个管道return 0;
}

client.cc

#include"comm.hpp"int main()
{Init client = Init(CLIENT);//通信char* start = client.getstart();char c = 'A';while(c <= 'Z'){start[c - 'A'] = c;c++;start[c - 'A'] = '\0';sleep(1);}return 0;
}

comm.cpp

#ifndef COMM_HPP
#define COMM_HPP#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<cerrno>
#include<string.h>
#include<unistd.h>
#include<sys/stat.h>
#include<cassert>
#include<sys/types.h>using namespace std;//路径名称
#define PATHNAME "."
//项目id
#define PROJID 0x6666
//共享内存大小
#define gsize 4096//获取key
key_t getKey()
{//用ftok函数获取keykey_t k = ftok(PATHNAME, PROJID);if(k == -1){cerr << errno << ":" << strerror(errno) << endl;exit(1);}return k;
}string toHex(key_t k)
{char buffer[64];snprintf(buffer, sizeof(buffer),"0x%x", k);return buffer;
}static int creatShmHeaper(key_t k, int size, int flag)
{ int shmid = shmget(k, size, flag);if(shmid == -1){cerr << errno << ":" << strerror(errno) << endl;exit(2);}return shmid;
}//创建共享内存
int creatShm(key_t k, size_t size)
{umask(0);return creatShmHeaper(k, size, IPC_CREAT | IPC_EXCL | 0666);
}//获取共享内存
int getShm(key_t k, size_t size)
{return creatShmHeaper(k, size, IPC_CREAT);
}//删除shmid
void delShm(int shmid)
{int n = shmctl(shmid, IPC_RMID, nullptr);assert(n != -1);(void)n;
}char* attachShm(int shmid)
{char* start = (char*)shmat(shmid, nullptr, 0);return start;
}void dettachShm(char* start)
{int n = shmdt(start);assert(n != -1);(void)n;
}#define SERVER 0x1
#define CLIENT 0x2class Init
{//构造函数
public:Init(int T) :type(T){//1.创建keykey_t k = getKey();//2.获取shmidif(type == SERVER) shmid = creatShm(k, gsize);else shmid = getShm(k, gsize);//3.链接共享内存start = attachShm(shmid);}~Init(){//1.切断共享内存dettachShm(start);//2.释放共享内存if(type == SERVER) delShm(shmid);}char* getstart(){return start;}private:char* start; //共享内存的地址int type;  //辨别对象是server还是clientint shmid; //共享内存标识符
};#endif

到这本篇博客的内容就到此结束了。
如果觉得本篇博客内容对你有所帮助的话,可以点赞,收藏,顺便关注一下!
如果文章内容有错误,欢迎在评论区指正

在这里插入图片描述

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

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

相关文章

更换cmd下默认选择Python解释器

问题 我的电脑里有多个Python解释器&#xff0c;一个是自己下载的python37&#xff0c;版本是3.7.0&#xff0c;一个是anaconda的base环境&#xff0c;版本是3.7.4&#xff0c;还有虚拟环境里的python解释器。 最近发现&#xff0c;在cmd下输入python&#xff0c;使用的是anac…

肺是人体的第一道防线,流感频发季节,最有效的养肺方法你得知道!

肺脏是人体的第一道防线&#xff0c;牵动着整个呼吸道的健康&#xff0c;一旦肺脏受损&#xff0c;易引发咳嗽、气喘甚至肺炎。在流感、呼吸道疾病高发的冬季&#xff0c;如何呵护肺脏&#xff0c;保持身体健康&#xff1f; 全民养肺&#xff0c;刻不容缓 养肺不仅仅是中老年朋…

深入浅出之中央空调体系架构及楼宇自控系统

一、关于建筑节能 1、建筑能耗 在中国&#xff0c;建筑能耗占社会总能耗45.5%。来源&#xff1a;《中国建筑能耗研究报告&#xff08;2022&#xff09;》 2、空调、采暖、照明占比最高 建筑节能是指在保证、提高建筑舒适性和生活质量的条件下&#xff0c;在建筑物使用的全过…

12.5 作业

1&#xff0c; 以下是一个简单的比喻&#xff0c;将多态概念与生活中的实际情况相联系&#xff1a; 比喻&#xff1a;动物园的讲解员和动物表演 想象一下你去了一家动物园&#xff0c;看到了许多不同种类的动物&#xff0c;如狮子、大象、猴子等。现在&#xff0c;动物园里有…

RocketMQTemplate 发送消息的高级用法

Apache RocketMQ 是一款强大的分布式消息中间件&#xff0c;与 Spring Boot 集成后&#xff0c;通过 RocketMQTemplate 可以实现在应用程序中方便地发送消息。在本文中&#xff0c;我们将深入探讨 RocketMQTemplate 的一些高级用法&#xff0c;以提供更灵活的消息发送和控制。 …

7天快速学习计算机基础必考八股文day02:操作系统

day02操作系统目录一览图 请简述对进程的理解——操作系统的进程详解请简述同步与异步的区别——进程状态模型详解请简述进程和线程的区别——操作系统线程详解请简述什么是操作系统的内核态——用户态与内核态详解IO密集型任务部署需要注意什么——程序运行类型分析协程是什么…

找鞍点(PTA)

先找出每一行的max&#xff0c;然后在判断这个数是不是这一列的min #include <stdio.h> int main() { int i 0; int i1 0; int j1 0; int k 0; int j 0; int arr[6][6] { 0 }; int n 0; int i2 0; int max 0; int min…

【Qt开发流程】之对象模型2:属性系统

描述 Qt提供了一个复杂的属性系统&#xff0c;类似于一些编译器供应商提供的属性系统。然而&#xff0c;作为一个独立于编译器和平台的库&#xff0c;Qt不依赖于非标准的编译器特性&#xff0c;如__property或[property]。 Qt解决方案适用于Qt支持的所有平台上的任何标准c编译…

C++ vector建立无向图并遍历

如果题目中&#xff0c;以[[1,2],[1,3],[1,4],[2,3],....]这种方式给出边。可用使用vector建图。 首先定义一个二维的vector vector<vector<int>>g(n1);//n为顶点数 然后遍历每一条边&#xff0c;假设遍历时两边的顶点分别为a,b。如果是无向边&#xff0c;则可添加…

操作系统选择错题

编译器实质是程序指令,不由操作系统管理 **闲逛进程:**当系统中没有就绪进程时,闲逛进程idle一直运行,优先级最低,不需要除cpu以外的任何资源 多任务操作系统具有并发和并行的特点 操作系统通过硬件地址机制保护进程的地址空间,使得每个进程只能访问自己的地址空间。 不同…

力扣第374场周赛题解

这一场周赛的题目是比较难的一次&#xff0c;写了1个多小时就写了两个题目。 首先第一题&#xff1a; 纯水题&#xff0c;遍历然后进行一下判断就可以解决了。这边就不放代码了。 第二题&#xff1a; 这个题目&#xff0c;我觉得难度非常大&#xff0c;其实代码量也不大都是很…

IOday3作业

1> 使用文件IO完成对图像的读写操作 #include<myhead.h>int main(int argc, const char *argv[]) {//只读打开图片int fd-1;if((fd open("./R-C.bmp",O_RDWR))-1){perror("open");return -1;}//向后便宜两个字节找到大小的起始地址lseek(fd,2,S…

【数据结构】二叉树的实现

目录 1. 前言2. 二叉树的实现2.1 创建一棵树2.2 前序遍历2.2.1 分析2.2.2 代码实现2.2.3 递归展开图 2.3 中序遍历2.3.1 分析2.3.2 代码实现2.3.3 递归展开图 2.4 后序遍历2.4.1 分析2.4.2 代码实现2.4.3 递归展开图 2.5 求节点个数2.5.1 分析2.5.2 代码实现 2.6 求叶子节点个数…

Linux 调试器 --- g d b 使用

目录 一&#xff1a;gdb简介 二&#xff1a;示例代码 三&#xff1a;使用 1.启动gdb 2.各种指令 <1>: 查看源代码 <2>:设置断点 <3>:查看断点信息 <4>:删除断点 <5>: run <6>:逐过程调试 <7>:逐语句调试 <8>:查…

TrustZone​之在安全状态之间切换

如果处理器处于NS.EL1,而软件想要转移到S.EL1,应该如何实现呢? 要改变安全状态,无论是向上还是向下,执行都必须经过EL3,如下图所示: 前面的图表显示了在不同安全状态之间移动涉及的步骤的示例序列。逐步进行解释: 进入较高的异常级别需要一个异常。通常,此异常…

【JavaWeb】项目后端部分统一解决方案

项目后端部分统一解决方案 文章目录 项目后端部分统一解决方案一、异步响应规范格式类二、MD5加密工具类三、JwtHelper工具类四、CrosFilter过滤器 一、异步响应规范格式类 Result类 package com.pro.common;/*** 全局统一返回结果类**/ public class Result<T> {// 返…

HTML可以使用属性访问AJAX、CSS过渡、WebSocket和服务器发送事件?

最近发现一个有意思的开源库&#xff1a;Htmx 在 GitHub 的描述中: htmx 被称为“允许您直接在HTML中使用属性访问AJAX、CSS过渡、WebSocket和服务器发送事件&#xff0c;以便您可以以超文本的简洁和强大之处构建现代用户界面”。 什么是htmx&#xff1f; 简而言之&#xff0c…

相关基础知识

本文引注&#xff1a; https://zhuanlan.zhihu.com/p/447221519 1.方差 2.自协方差矩阵 3.自相关矩阵 4.互协方差矩阵 5.互相关矩阵 6.相关系数 7.自相关函数、自协方差函数与功率谱密度 8.互相关函数、互协方差函数与互功率谱密度

强大JavaScript 技巧

浏览器 01、实现全屏 当您需要将当前屏幕显示为全屏时 function fullScreen() { const el document.documentElement const rfs el.requestFullScreen || el.webkitRequestFullScreen || el.mozRequestFullScreen || el.msRequestFullscreen i…

时间选择器

<el-form-item label"时间范围"><!-- <el-date-picker size"small"v-model"createTime"type"daterange"range-separator"至"start-placeholder"请输入起始创建时间"end-placeholder"请输入终止创…