Linux进程通信之共享内存

文章目录

  • 共享内存原理
  • 申请共享内存函数(shmget)
    • 参数key
    • 生成key值示例
    • 申请共享内存
  • 挂接到进程地址空间函数(shmat)
  • 去关联函数(shmdt)
  • 控制共享内存(shmctl)
    • IPC_STAT
    • IPC_RMID
  • ipcs
  • 其余进程获取该共享内存
  • 进程间通信

进程间通信:IPC,Inter Process Communication

共享内存原理

进程之间通信的本质是:让不同的进程,看到同一份资源。
无论是匿名管道还是命名管道的通信,我们都是在内核空间里的缓冲区进行实现的进程通信,对于这种方式在用户输入时我们在内核空间缓冲区中进行通信,然后再写入物理内存,那么我们是否可以做到直接让进程在物理内存中进行进程间的通信呢?
在这里插入图片描述
每一个进程都有一个虚拟内存(地址),他们通过页表将虚拟地址映射到物理地址上,因此我们可以让操作系统帮我们在物理内存申请一段空间,然后通过页表把这份空间映射到虚拟内存的共享区,这样我们就做到了让不同的进程看到了同一份资源。

申请共享内存函数(shmget)

在这里插入图片描述

shmget:shared memory get,就是获取共享内存
参数:
key:这个共享内存段名字
size:需要创建的共享内存的大小
shmflg:通过类似位图实现的多操作整数
IPC_CREAT(单独):如果你申请的共享内存不存在,存在,就获取返回
IPC_CREAT | IPC_EXCL:如果你申请的共享内存不存在,就创建,存在,就出错返回。这是为了确保我们申请的共享内存,一定是一个新的,没有被别人使用
IPC_EXCL:不单独使用
这段共享内存的权限,千万不要忘记或上 | 权限,不然后面就会导致进程挂接不上

//申请共享内存
int shmid = shmget(key, NUM, IPC_CREAT | 0666);//返回值为共享内存标识符

返回值:
共享内存标识符

参数key

key是一个数字,这个值是随机的,关键是这个随机数在内核中具有唯一性,可以让他对不同的进程拥有唯一的标识,第一个进程创建出key之后,后面的进程只要有了这个key,就可以拿着这个key去和其它进程进行共享内存了。举个例子:这个key就相当于是一把钥匙,而这个共享内存就是一把锁,只要不同的进程拿着这个钥匙,就可以打开这个内存了。那么现在的问题是我们怎么去生成这个key值呢,又这么保证它在内核中具有唯一性呢?
系统中什么具有唯一性呢?对,就是路径,路径就有唯一性,因此我们可以通过一个函数把路径作为参数传给这个函数,函数根据一定的算法帮助我们生成这个key。
库里面给我们提供了生成key的函数:ftok()
在这里插入图片描述
参数:
pathname:文件路径
proj_id:项目id,我们给定的一个int值,目的就是为了和文件路径一起通过一系列的算法生成一个唯一key值
作用:ftok函数通过给的路径和int值就可以帮我们生成一个在内核中具有唯一性的值。
返回值:
在这里插入图片描述
成功返回key,失败返回-1并设置错误码
key存在哪里呢?系统中一定有很多的共享内存,操作系统要想把他们管理起来,就要先描述再组织,因此key一定被存在共享内存的描述对象中。

生成key值示例

#include <sys/types.h>
#include <sys/ipc.h>
#include <iostream>using namespace std;#define PATHNAME "./common.cc" 
#define PROJ_ID 0x8888int main()
{key_t key = ftok(PATHNAME, PROJ_ID);if(key == -1){perror("ftok error");exit(0);}cout << key << endl;return 0;
}

在这里插入图片描述
所给的路径,一般是当前文件的路径,当然也可以给其他文件的,但为了保证唯一性我们一般在哪个路径下生成key就使用哪个路径,但前提是所给的文件路径一定是存在的,如果不存在就会生成失败。

申请共享内存

在这里插入图片描述

//申请共享内存,方式1
int shmid = shmget(key, 1024, IPC_CREAT 0666);
//申请共享内存,方式2
int shmid = shmget(key, 1024, IPC_CREAT|IPC_EXCL 0666);

问题:我们绕了一大圈生成的这个shmid和key都是具有唯一性的,那么我们为什么不直接用key呢?非要再去通过给shmget函数传key生成呢?
key是用于在操作系统内标定唯一性,而shmid是共享内存标识符,只存在于我们的进程内,用来表示资源的唯一性
在这里插入图片描述
共享内存的大小一般是4096(4KB)的整数倍,虽然我们这里申请的是1024,其实系统给我们的就是4096,因为操作系统是按4KB为单位对我们进行分配的,需要注意的是我们只能用1024,多给的我们不可以使用,用了就是越界可能会出现错误

挂接到进程地址空间函数(shmat)

shmat:shared memory attach
返回值为挂接到虚拟地址中的哪个位置
上面我们只是在物理内存中申请了一份共享内存,但是我们还要去把该共享内存挂接到进程地址空间的共享区中,如何做到呢?通过shmat函数来实现
在这里插入图片描述
参数:
shmid:就是申请共享内存函数shmget的返回值,共享内存标识符
shmaddr:让申请的共享内存挂到共享区的哪一个位置(地址),设置成nullptr就是让系统进行决定
shmflg:进程以什么方式去挂接这个共享内存

shmaddr为NULL,核心自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存
返回值:返回一个void类型的指针,指向在物理内存中申请的共享内存在虚拟地址中共享内存的起始地址
共享内存:指在物理内存中申请的共享内存
共享区:指在虚拟内存中的共享区域

在这里插入图片描述

去关联函数(shmdt)

dt:delete touch
给虚拟内存共享区挂接的物理内存,如果进程结束,系统会自动取消关联,但是我们现在不想等到进程结束,想要在进程还在运行时就去关联,怎么做呢?
通过函数shmdt()就可以做到去关联
在这里插入图片描述
参数:
const void* shmaddr:就是在物理内存申请的共享内存挂接到共享区的起始地址,也就是函数shmat的返回值。
返回值:
成功返回0,失败返回-1,并设置错误码
注意:将共享内存段与当前进程脱离不等于删除共享内存段。

//去关联
shmdt(shmaddr);

控制共享内存(shmctl)

在这里插入图片描述
参数:
shmid:共享内存标识符,就是我们通过shmget函数得到的返回值。
cmd:控制这个函数去做什么
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:
成功返回0,失败返回-1
其中参数cmd有三个取值,如下:
在这里插入图片描述

IPC_STAT

用于获取管理该共享内存结构中的数据,这个共享内存的key值,关联个数等数据

struct shmid_ds shmds;
shmctl(shmid, IPC_STAT, &shmds);
cout << "shm size: " << shmds.shm_segsz << endl;
cout << "shm nattch: " << shmds.shm_nattch << endl;
printf("shm key: 0x%x\n",  shmds.shm_perm.__key);
cout << "shm mode: " << shmds.shm_perm.mode << endl;

IPC_RMID

删除共享内存选项
shmdt函数只是把进程和该共享内存的关系从也表中删除从而取消关联,但是我们在物理内存中申请的共享内存并没有被释放,因此我们可以通过shmctl来释放对应的共享内存

ipcs

如果不通过shmctl来释放对应的共享内存,那么即使进程结束,该共享内存仍然是被占用的,因此我们可以手动的来释放该共享内存,操作如下:
在这里插入图片描述
在这里插入图片描述
此时只是去关联了,但是该共享内存仍然被占用没有被释放
通过ipcrm -m选项 + 对应的共享内存标识符来释放该共享内存
在这里插入图片描述

其余进程获取该共享内存

只需要创建一次共享内存,其余的进程只需拿着这个共享内存的标识符就可以得到这个共享内存了

#include <sys/types.h>
#include <sys/ipc.h>
#include <iostream>
#include <sys/shm.h>
#include <unistd.h>#include "log.hpp"using namespace std;#define PATHNAME "/home/Linux3" 
#define PROJ_ID 0x6666
#define NUM 1024key_t GetKey()
{//生成参数唯一key值key_t key = ftok(PATHNAME, PROJ_ID);if(key == -1){perror("ftok error");exit(0);}return key;
}int GetShareMemHelper(int flag)
{key_t key = GetKey();int shmid = shmget(key, NUM, flag);//返回值为共享内存标识符return shmid;
}//创建第一个共享内存
int CreatShm()
{return GetShareMemHelper(IPC_CREAT |IPC_EXCL | 0666);
}//获取已经创建好的共享内存
int GetShm()
{return GetShareMemHelper(IPC_CREAT);
}
#include "common.hpp"int main()
{int shmid = GetShm();//自定义函数,用于获取已经创建好的共享内存char* shmaddr = (char*)shmat(shmid, nullptr, 0);sleep(5);shmdt(shmaddr);sleep(2);shmctl(shmid, IPC_RMID, nullptr);return 0;
}

进程间通信

此时我们就可以向该共享内存写入了,而不是先写入缓冲区,再讲缓冲区数据拷贝到内存,其它进程就直接从该共享内存进行读取就可以了。
但是此时会有一个问题,就是共享内存对于不同进程而言不是同步的,即写进程还没写,读进程就从共享内存中读,此时读到的为空,或者上次的,就一直在那打印,因此我们要让进程同步,等写进程写入了,读进程才开始读,这里举个例子,我们可以通过管道来实现进程同步,因为read函数,如果没有读取到就会一直等待

char c = 'c';
while(1)
{fgets(shmaddr, 1024, stdin);write(fd, &c, 1);//向命名管道写入信息,用来通知读进程可以从共享内存中读取了//通过命名管道使得进程a,b同步}
Init init;
int fd = open(FIFO_FILE, O_RDONLY);
char c;
while (1)
{ssize_t n = read(fd, &c, 1);//等待通知信息,如果没有就退出if(n <= 0){break;}cout << shmaddr;
}

创建一个管道,当向共享内存写入时,该进程通过write函数向管道中写入数据,对于读进程如果一直没有read到,就会一直等待,当读到了才会向下执行代码,这里管道的作用就是为了实现进程同步。

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

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

相关文章

OpenStack云计算平台-镜像服务

目录 一、镜像服务概览 二、安装和配置 1、先决条件 2、安全并配置组件 3、完成安装 三、验证操作 一、镜像服务概览 OpenStack镜像服务是IaaS的核心服务&#xff0c;如同 :ref:get_started_conceptual_architecture所示。它接受磁盘镜像或服务器镜像API请求&#xff0c;…

Redis深入理解-Socket连接建立流程以及文件事件处理机制

Redis Server 运行原理图 Redis 服务器中 Socket 网络建立以及文件事件模型 一个 redis 单机&#xff0c;可以抗几百上千的并发&#xff0c;这里的并发指的就是同时可以有几百个 client 对这个 redis server 发起请求&#xff0c;都需要去建立网络连接&#xff0c;同时间可能会…

C语言——用递归函数计算n!

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 比别人多一点努力&#xff0c;你…

量子计算概述

目录 1.量子计算介绍 2.量子计算应用 3.量子计算研究机构 1.量子计算介绍 量子计算是一种遵循量子力学规律调控量子信息单元进行计算的新型计算模式。经典计算使用2进制进行运算&#xff0c;但2进制只有0和1两种状态&#xff0c;而量子计算除了包含0和1两种状…

记一次docker服务启动失败解决过程

环境&#xff1a;centos 7.6 报错&#xff1a;start request repeated too quickly for docker.service 由于服务器修复了内核漏洞&#xff0c;需要重启&#xff0c;没想到重启后&#xff0c;docker启动失败了 查看状态 systemctl status docker如下图 里面有一行提示&…

前端学习--React(3)

一、Redux 集中状态管理工具&#xff0c;不需要react即可使用&#xff0c;每个store的数据都是独立于组件之外的 vue小链接&#xff1a;vuex/pinia 基本使用 Redux将数据修改流程分成三个概念&#xff0c;state、action和reducer state - 一个对象 存放我们管理的数据状态 a…

51单片机IO口的四种工作状态切换

51单片机IO口的四种工作状态切换 1.概述 这篇文章介绍单片机IO引脚的四种工作模式&#xff0c;每个模式都有各自的用武之地&#xff0c;后面在驱动外设硬件时会用它不同的模式。 2.IO口四种工作模式介绍 PnM1PnM0I/O口工作模式00准双向口&#xff1a;灌电流达20mA&#xff…

java--static的应用知识:单例设计模式

1.什么是设计模式(Design pattern) ①一个问题通常有n中解法&#xff0c;其中肯定有一种解法最优的&#xff0c;这个最优的解法被人总结出来了&#xff0c;称之为设计模式。 ②设计模式有20多种&#xff0c;对应20多种软件开发中会遇到的问题。 2.单例设计模式 确保一个类只…

2024东北师范大学计算机考研分析

24计算机考研|上岸指南 东北师范大学 信息科学与技术学院位于长春净月国家高新技术产业开发区&#xff0c;毗邻风光秀美的净月潭国家森林公园。 信息科学与技术学院由原“计算机科学与信息技术学院”和“信息与软件工程学院”于2017年根据学校事业发展需要整合形成。学院设有…

SPS简单对应分析

前言&#xff1a; 本专栏参考教材为《SPSS22.0从入门到精通》&#xff0c;由于软件版本原因&#xff0c;部分内容有所改变&#xff0c;为适应软件版本的变化&#xff0c;特此创作此专栏便于大家学习。本专栏使用软件为&#xff1a;SPSS25.0 本专栏所有的数据文件请点击此链接下…

ChatGPT有效开通方法*建议收藏*

ChatGPT Plus和API开通方法*建议收藏* 前期准备&#xff1a; 一个ChatGPT账户 一张虚拟卡 开通ChatGPT Plus会员&#xff0c;很多朋友担心虚拟卡订阅会封号&#xff0c;只要不勤换登陆IP不会使出现问题。而且目前大部分用户都是使用虚拟卡开通的会员 第一步&#xff1a;登…

【SpringCloud】什么是微服务?

一、单体架构 单体架构就是把所有业务模块编写在一个项目&#xff0c;最终打包成一个 war 包&#xff0c;进行部署 单体架构的优点&#xff1a; 部署简单&#xff1a;由于是完整的结构体&#xff0c;可以直接部署在一个服务器上即可 技术单一&#xff1a;项目不需要复杂的…