Linux——详解共享内存shared memory

news2024/11/19 3:28:19

目录

一.共享内存介绍

(一).什么是共享内存

(二).共享内存优点

(三).共享内存缺点

二.共享内存使用

(一).创建—shmget

①key

②size

③shmflg

④返回值

(二).连接—shmat

(三).分离—shmdt

(四).销毁—shmctl

(五).查看—ipcs

(六).删除—ipcrm

(七).读取与写入 

三.共享内存与访问控制

(一).添加访问控制

(二).可能的陷阱


一.共享内存介绍

(一).什么是共享内存

共享内存本质上就是内存中的一块区域,用于进程间通信使用。该内存空间由操作系统分配与管理。与文件系统类似的是,操作系统在管理共享内存时,不仅仅有内存数据块,同时还会创建相应结构体来记录该共享内存属性,以便于管理。

因此,共享内存不只有一份,可以根据需求申请多个。

进程之间进行通信的时候,会获取 到共享内存的地址,写端进程写入数据,读端进程通过直接访问内存完成数据读取。

(二).共享内存优点

相比于管道而言,共享内存不仅能够用于非父子进程之间的通信,而且访问数据的速度也比管道要快。这得益于通信直接访问内存,而管道则需要先通过操作系统访问文件再获得内存数据。

(三).共享内存缺点

用于进程间通信时,共享内存本身不支持阻塞等待操作。这是因为当读端读取数据后,数据并不会在内存中清空。因此读端和写端可以同时访问内存空间,即全双工。因为共享内存本质是进程直接访问内存,无法主动停止读取,如果读端不加以限制,那么将持续读取数据。同理,写端也会持续写入数据。换句话说,共享内存本身没有访问控制。 

二.共享内存使用


(一).创建—shmget

想要使用共享内存首先要建立共享内存。

①key

shmget会根据key值创建一个共享内存,因此当创建多个共享内存时,每一个key值要独一无二。

获得key值可以使用库函数ftok专门获取一个独一无二的key_t类型值。

参数pathname为路径,必须是真实存在且可以访问的路径。

参数proj_id是int类型数字,且必须传入非零值。

成功返回key_t值,失败返回-1。

ftok函数内部会根据路径和proj_id通过算法生成一个独一无二的key_t返回值。

多进程通信时,需要通信双方使用同一个key值,因此双方使用的ftok参数应该一致。 

②size

该参数用于确定共享内存大小。

一般而言是4096的整数倍,因为内存的块的大小就是4KB即4096B。因此即便我们需要的空间大小不是块大小的整数倍,操作系统实际上也还是分配块的倍数个。但在使用时,那些超过size大小的多余分配空间不能访问。  

③shmflg

 该参数用于确定共享内存属性。

使用上为:标志位 | 内存权限

标志位参数有两种:IPC_CREAT、IPC_EXCL

常用使用方式有两种:

方式含义
shmget(..., IPC_CREAT | 权限)创建失败不报错返回已有shmid
shmget(..., IPC_CREAT | IPC_EXCL | 权限)创建失败报错返回-1

值得注意PC_EXCL无法单独使用。

通常情况下在多进程通信时,创建方使用IPC_CREAT | IPC_EXCL,接收方使用0即可。

④返回值

返回值为int类型,称为shmid。每一个共享内存都会有一个shmid,用于连接与分离时传递参数。 

(二).连接—shmat

创建共享内存后还不能直接使用,需要找到内存地址后才能使用,即连接。 

 shmid即shmget返回值。

shmaddr用于确定将共享内存挂在进程虚拟地址哪个位置,一般填nullptr即可代表让内核自己确定位置。

shmflg用于确定挂接方式,一般填0

连接成功返回共享内存在进程中的起始地址,失败返回-1。 

(三).分离—shmdt

当使用完毕后,需要分离挂接的共享内存。

 shmaddr与shmat的相同,为共享内存在进程中地址位置,一般填nullptr。

分离成功返回0,失败返回-1。 

(四).销毁—shmctl

该接口本身用于控制共享内存,可用于销毁。 

shmid不再介绍,cmd传入IPC_RMID,buf传nullptr。 

成功返回0,失败返回-1。 

(五).查看—ipcs

该指令为系统指令。

使用时可以查看当前全部共享内存。

ipcs -m 

(六).删除—ipcrm

通过指定共享内存shmid,进行删除。

ipcrm -m [shmid] 

 

(七).读取与写入 

调用shmat后会返回一个地址,读端直接读取该地址数据,写端直接向该地址写入即可。

//读端, 将共享内存数据读取到文件,此处为显示器文件
char* p = (char*)shmat(...);
write(1, p, sizeof p);
//写端,将文件中数据写入共享内存,此处为键盘文件
char* p = (char*)shmat(...);
read(0, p, 4096);

 

三.共享内存与访问控制

(一).添加访问控制

通过博客第一部分我们知道,共享内存不支持访问控制,那么我们可不可以添加访问控制给共享内存呢——完全可以。

方式是借用命名管道的访问控制,即阻塞。 

首先我们有如下代码,该代码是读端一直读取写端数据,直到写端输入quit为止。

//写端
int main()
{
  key_t key = ftok(".", 131);
  int shmid = shmget(key, 4096, IPC_CREAT|0660);//获取shmid
  char* p = (char*)shmat(shmid, nullptr, 0);//连接
  while(1){
    ssize_t s = read(0, p, 4096);//写入shm
    p[s - 1] = 0;
    assert(s > 0);
    (void)s;
  }
  shmdt(p);//分离
  return 0;
}
//读端
int main()
{
  key_t key = ftok(".", 131);
  int shmid = shmget(key, 4096, IPC_CREAT|IPC_EXCL|0660);//创建
  char* p = (char*)shmat(shmid, nullptr, 0);//连接
  while(1){
    assert(p != nullptr);
    if(strcmp(p, "quit") == 0)break;
    printf("%s\n", p);//读取shm中数据
    sleep(1);
  }
  shmdt(p);//分离
  shmctl(shmid, IPC_RMID, nullptr);//销毁
  return 0;
}

 但是因为共享内存无法访问控制,读端会一直读取数据,即便我们添加sleep函数也不能从根本解决问题。

解决方式是,在读端和写端分别加上管道的读端和写端。因为我们知道管道读端在读取到来自写端的数据前会阻塞,因此,将管道读端放在共享内存读端之前,将管道写端放在共享内存写端之后。

这样一来,当shm写端写入数据后会触发管道写端写数据,当管道写端写入数据后,管道读端才会停止阻塞,进而执行shm读端。

图例如下:


代码如下: 

//写端
int main()
{
  key_t key = ftok(".", 131);
  int shmid = shmget(key, 4096, IPC_CREAT|0660);//获取shmid
  char* p = (char*)shmat(shmid, nullptr, 0);//连接
  int fd = open(..., O_WRONLY);//打开命名管道
  while(1){
    ssize_t s = read(0, p, 4096);//写入shm
    p[s - 1] = 0;
    assert(s > 0);
    (void)s;
    char i[4] = { 0 };
    write(fd, i, sizeof i);//写入管道
  }
  shmdt(p);//分离
  close(fd);
  return 0;
}
//读端
int main()
{
  int i = mkfifo(PATH_FIFO, 0660);//创建管道
  assert(i >= 0);
  key_t key = ftok(".", 131);
  int shmid = shmget(key, 4096, IPC_CREAT|IPC_EXCL|0660);//创建
  char* p = (char*)shmat(shmid, nullptr, 0);//连接shm
  int fd = open(..., O_RDONLY);//连接管道
  while(1){
    char buf[4];
    read(fd, buf, sizeof buf);//管道等待读取,阻塞
    assert(p != nullptr);
    if(strcmp(p, "quit") == 0)break;
    printf("%s\n", p);//读取shm中数据
    sleep(1);
  }
  shmdt(p);//分离
  shmctl(shmid, IPC_RMID, nullptr);//销毁
  close(fd);
  return 0;
}

(二).可能的陷阱

在添加访问控制时,会有一个可能的陷阱,就是命名管道可不可以在创建shm之前打开(open)呢?

不可以,因为打开管道要求读端和写端同时打开才能继续,否则就会阻塞。

如果阻塞的是写端还好,当读端创建完shm后写端创建失败返回shmid,但是如果阻塞的是读端,那么写端创建shm后,读端创建时因为加上IPC_EXCL的缘故,失败返回-1,之后shmat也失败返回nullptr,进而读端获取到的地址是空。

简单模块注意封装,复杂模块注意分层——未名


如有错误,敬请斧正

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

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

相关文章

CRMEB电商商城系统腾讯云ECS服务器安装配置搭建教程文档

一、推荐使用宝塔Linux面板,简单好用。二、放行服务器端口。详细步骤: 1.登录腾讯云服务器,点击右上角“控制台” 2.我的资源,点击进入云服务器 3.进入实例列表,选择您要安装的服务器,点击更多 4.选择重装…

Hadoop 如何保证自己的江湖地位?Yarn 功不可没

前言 任何计算任务的运行都离不开计算资源,比如 CPU、内存等,那么如何对于计算资源的管理调度就成为了一个重点。大数据领域中的 Hadoop 之所以一家独大,深受市场的欢迎,和他们设计了一个通用的资源管理调度平台 Yarn 密不可分&a…

Metal每日分享,四维向量偏移滤镜效果

本案例的目的是理解如何用Metal实现图像4维向量颜色效果滤镜,通过对像素点颜色进行4维向量叠加运算得到新的像素点; Demo HarbethDemo地址实操代码 // 暖色系 let filter = C7ColorVector4(vector: Vector4.Color.warm)// 方案1: ImageView.image = try? BoxxIO(element: o…

浅谈字节码增强技术系列1-字节码增强概览

作者:董子龙 前言 前段时间一直想参照lombok的实现原理写一篇可以生成业务单据修改记录插件的专利,再查阅资料的过程中,偶然了解到了字节码增强工具-byteBuddy。但是由于当时时间紧促,所以没有深入的对该组件进行了解。其实再我…

一文搞定Pandas核心概念之DataFrame

DataFrame概述 DataFrame 是一个表格型的数据结构,它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔型值)。DataFrame 既有行索引也有列索引,它可以被看做由 Series 组成的字典(共同用一个索…

麒麟系统下基于卫星的NTP网络授时服务器方案

麒麟系统下基于卫星的NTP网络授时服务器方案 1、 麒麟系统NTP授时方案 设计思路: 在通用的麒麟服务器内部固定一块北斗卫星接收模块并引出卫星天线接口,卫星模块接收北斗卫星数据并解码输出时间数据(NMEA0183串口数据)&#xff…

Linux编译静态库.a脚本(很low)

比如目录下有这几个源文件,我们要把其中带箭头的三个源文件编译打包成静态库文件 然后在当前目录创建脚本make_lib.sh,并赋可执行权限chmod 777 make_lib.sh #!/bin/bash # 在下面将需要编译成静态库的源文件名填进去 list"ky_ai_api ky_ai_pars…

基于PHP的旅游网站的开发与设计

目录 第1章 绪论 3 1.1 课题背景 3 1.2 电子商务的发展趋势 3 1.3企业网站的建立及电子商务的意义 4 第2章 电子商务简介 6 2.1 电子商务的来临 6 2.2 电子商务的概念 6 2.3 电子商务的分类 7 2.4 电子商务的特性 8 2.5 电子商务的结构 11 2.6 电子商务在中国的发展 11 2.7 本章…

Vue生命周期概述

Vue生命周期概述1 概述2 初始阶段3 挂载阶段4 更新阶段5 销毁阶段6 总结1 概述 每个Vue组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到DOM,以及在数据改变时更新DOM。在此过程中&#xf…

微服务门神-网关了解

引言 书接上篇 微服务守护神-Sentinel-其他 ,讲完微服务守护神-Sentinel之后,接下来就是微服务门神-网关组件:Gateway 问题引入 小伙伴们都知道在微服务架构中,一个系统会被拆分为很多个微服务,每一个微服务都能对外…

风电场数字孪生的应用案例

在我国“十四五”现代能源中明确规划,要大规模发展风电能源。与此同时电力行业也在加紧通过数字孪生等新一代信息技术推动电力能源行业智能化改造和数字化建设,不夸张地说数字孪生技术,数字孪生的应用不仅能够提高风电场项目建设的设计、施工…

PPa-GO/NPs/PEG/DSPE焦脱镁叶绿酸-a修饰氧化石墨烯/纳米粒子/聚乙二醇/磷脂/细胞膜合成

小编分享了PPa-GO/NPs/PEG/DSPE焦脱镁叶绿酸-a修饰氧化石墨烯/纳米粒子/聚乙二醇/磷脂/细胞膜合成方法相关知识,来学习! 焦脱镁叶绿酸-a衍生物合成方法: 通过酸解反应从叶绿素a得到焦脱镁叶绿酸a,羧基保护后插入Zn2形成金属配合物,采用2,3-二氯-5,6-二氰…

window.open跳转页面传参接参

<el-table-column fixed"right" header-align"center" align"center" prop"action" label"操作" width"180px"><template slot-scope"scope"><el-button type"primary" size&…

QT学习笔记(上)

QT学习笔记&#xff08;上&#xff09; 文章目录QT学习笔记&#xff08;上&#xff09;1. 窗口和按钮2. 创建一个自定义的QPushButton2.1 mypushbutton.h2.2 mypushbuttion.cpp2.3 mainwindow.cpp引用mypushbutton3. QT坐标原点4. 信号与槽5. 自定义信号和槽6. 信号和槽的重载P…

基于java(SSH)的数字迎新系统的设计与实现

目 录 摘 要 i Abstract ii 1 绪论 1 1.1 选题背景 1 1.2研究现状 1 1.3课题目的 1 1.4本文结构 2 2 设计技术与开发环境 3 2.1 相关技术介绍 3 2.1.1 Struts简介 3 2.1.2 Hibernate简介 3 2.1.3 spring简介 3 2.2.4 SSH的简介 3 2.2 开发环境介绍 5 2.2.1 Myeclipse简介 5 2.2…

linux只W25Q256驱动,使用m25p80,支持w25q系列nor flash

1.内核编译选项增加 (1&#xff09;Device Drivers/Memory Technology Device (MTD) support ---> (2)Device Drivers/Memory Technology Device (MTD) support /SPI-NOR device support ---> (3)Device Drivers/Memory Technology Device (MTD) support /SPI-NOR dev…

机器学习——期末复习

文章目录填空题第一章 机器学习基础第二章 数据预处理KNN算法支持向量机集成学习决策树聚类算法联结学习三种池化操作选择题计算题数据正规化Hopfield网络能量函数计算卷积、池化操作应用题决策树、朴素贝叶斯、聚类算法单层感知器构造&#xff08;连接神经元部分&#xff09;填…

unix 域套接字实现进程间通信

目录 1、认识域套接字 2、unix域套接字相关API及地址结构介绍 (1) 创建unix域套接字 (2) 填充地址结构 sockaddr_un 3、unix域套接字实现进程间通信&#xff08;以UDP为例&#xff09; 1、认识域套接字 和之前TCP / UDP 编程使用的套接字不同&#xff0c;域套接字常用于同…

01入门及简单应用-ReentrantReadWriteLock原理-AQS-并发编程(Java)

文章目录1 概述2 性质3 简单测试4 模拟数据缓存4.1 应用初始化无缓存4.2 加入缓存改造5 后记1 概述 ReentrantReadWriteLock 是读写锁&#xff0c;和ReentrantLock会有所不同&#xff0c;对于读多写少的场景使用ReentrantReadWriteLock 性能会比ReentrantLock高出不少。在多线程…

技术分享 | 测试平台开发-前端开发之数据展示与分析

测试平台的数据展示与分析&#xff0c;我们主要使用开源工具ECharts来进行数据的展示与分析。 ECharts简介与安装 ECharts是一款基于JavaScript的数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表&#xff…