lv5 嵌入式开发-8 内存映射

目录

1 内存映射基本使用

1.1 内存映射概念

1.2 内存映射的使用

2 共享内存(古老的 System V IPC)

2.1 基本概念

2.2 共享内存使用步骤

2.3 共享内存使用


掌握:内存映射概念、内存映射使用、内存映射注意事项、了解SYSTEM V 共享内存概念 共享内存使用步骤

1 内存映射基本使用

1.1 内存映射概念

共享内存可以通过mmap()映射普通文件, 使一个磁盘文件与内存中的一个缓冲区相映射,进程可以像访问普通内存一样对文件进行访问,不必再调用read,write。

mmap的优点

实现了用户空间和内核空间的高效交互方式

进程A与B通过共享内存,可以实现不通过磁盘,而通过内存访问,速度大大增加。

 

1.2 内存映射的使用

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);	

功能:创建共享内存映射

函数返回值:成功返回创建的映射区首地址,失败返回MAP_FAILED( ((void *) -1) ),设置errno值

参数说明:

1 addr:指定要映射的内存地址,一般设置为 NULL 让操作系统自动选择合适的内存地址。

2 length:必须>0。映射地址空间的字节数,它从被映射文件开头 offset 个字节开始算起。

3 prot:指定共享内存的访问权限。可取如下几个值的可选:PROT_READ(可读), PROT_WRITE(可写), PROT_EXEC(可执行), PROT_NONE(不可访问)

4 flags:由以下几个常值指定:MAP_SHARED(共享的) MAP_PRIVATE(私有的), MAP_FIXED(表示必须使用 start 参数作为开始地址,如果失败不进行修正),其中,MAP_SHARED , MAP_PRIVATE必选其一,而 MAP_FIXED 则不推荐使用。MAP_ANONYMOUS(匿名映射,用于血缘关系进程间通信)

5 fd:表示要映射的文件句柄。如果匿名映射写-1。

6 offset:表示映射文件的偏移量,一般设置为 0 表示从文件头部开始映射

示例:写文件

#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(){void *addr;int fd;fd =open("test",O_RDWR);  //如果只读,但是共享内存是读写,那么会报错if(fd<0){perror("open");return 0;}int len = lseek(fd,0,SEEK_END);    //取文件大小addr = mmap(NULL,2048, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);if(addr == MAP_FAILED){perror("mmap");return 0;}close(fd);int i=0;while(i<2048){memcpy((addr+i),"a",1);i++;sleep(1);}    printf("read=%s\n",(char*)(addr));}

示例:读文件

#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(){void *addr;int fd;fd =open("test",O_RDWR);if(fd<0){perror("open");return 0;}int len = lseek(fd,0,SEEK_END);    addr = mmap(NULL,2048, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);if(addr == MAP_FAILED){perror("mmap");return 0;}close(fd);
//    memcpy((addr),"99999999999999",15);while(1){printf("read=%s\n",(char*)(addr));sleep(1);}}

同时运行写、读程序,实现进程间通信 

 

内存映射注意事项(重点)

  • 创建映射区的过程中,隐含着一次对映射文件的读操作,将文件内容读取到映射区。(如上示例printf)
  • 当MAP_SHARED时,要求:映射区的权限应 <=文件打开的权限(出于对映射区的保护),如果不满足报非法参数(Invalid argument)错误
  • 当MAP_PRIVATE时候,mmap中的权限是对内存的限制,只需要文件有读权限即可,操作只在内存有效,不会写到物理磁盘,且不能在进程间共享。(可读内存,不可写入磁盘)
  • 映射区的释放与文件关闭无关,只要映射建立成功,文件可以立即关闭。(示例中close关闭文件,可读可写)
  • 用于映射的文件大小必须>0,当映射文件大小为0时,指定非0大小创建映射区,访问映射地址会报总线错误,指定0大小创建映射区,报非法参数错误(Invalid argument)(打几个空格,能分配几个空间)。
  • 文件偏移量必须为0或者4K的整数倍(不是会报非法参数Invalid argument错误,1页大小正好是4K).
  • 映射大小可以大于文件大小,但只能访问文件page的内存地址,否则报总线错误 ,超出映射的内存大小报段错误。(假如文件大小4999,实际分配了8191字节大小刚好两页,不能超;假如文件大小37字节,申请了5k,但也实际只能访问4k,总之一定要在文件大小的合理页范围)。
  • mmap创建映射区出错概率非常高,一定要检查返回值,确保映射区建立成功再进行后续操作。

 

释放内存映射

munmap函数int munmap(void *addr, size_t length);

返回值:成功返回0,失败返回-1,并设置errno值。

函数参数:

addr:调用mmap函数成功返回的映射区首地址

length:映射区大小(即:mmap函数的第二个参数)

示例:匿名映射 

#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>int main(){void *addr;//参数MAP_ANONYMOUS -1 addr = mmap(NULL,2048, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);if(addr == MAP_FAILED){perror("mmap");return 0;}pid_t pid;pid = fork();if(pid<0){perror("fork");return 0;}else if(pid>0){memcpy(addr,"1234567890",10);//sleep(10);//增加一个时间差的作用,避免同时,如果加了wait回收子进程,是阻塞函数可以不需要sleepwait(NULL); //回收子进程}else {sleep(1);printf("read father val=%s\n",(char *)addr);}munmap(addr,2048); //释放}

2 共享内存(古老的 System V IPC)

2.1 基本概念

IPC 对象包含: 共享内存、消息队列(过时)和信号灯集(过时)

每个IPC对象有唯一的ID 用Key关联

IPC对象创建后一直存在,直到被显式地删除

ipcs / ipcrm

共享内存

  • 共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝
  • 共享内存在内核空间创建,可被进程映射到用户空间访问,使用灵活
  • 由于多个进程可同时访问共享内存,因此需要同步和互斥机制配合使用

2.2 共享内存使用步骤

生成key

创建/打开共享内存 映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问

读写共享内存

撤销共享内存映射

删除共享内存对象

2.3 共享内存使用

System V IPC - key

System V IPC – ftok (非必须,通过两个参数拼接可以实现唯一)

#include  <sys/types.h>
#include <sys/ipc.h>
key_t  ftok(const char *path,  int proj_id);
  • 成功时返回合法的key值,失败时返回EOF  
  • path  存在且可访问的文件的路径  (文件的节点号)
  • proj_id  用于生成key的数字,范围1-255。

共享内存创建 – shmget

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int shmflg);
  • 成功时返回共享内存的id,失败时返回EOF  
  • key  和共享内存关联的key,IPC_PRIVATE 或 ftok生成  
  • shmflg 共享内存标志位  IPC_CREAT|0666

共享内存映射 – shmat

 #include <sys/ipc.h>#include <sys/shm.h>void  *shmat(int shmid, const void *shmaddr, int shmflg);
  • 成功时返回映射后的地址,失败时返回(void *)-1  
  • shmid   要映射的共享内存id   shmaddr  
  • 映射后的地址, NULL表示由系统自动映射  
  • shmflg   标志位  0表示可读写;SHM_RDONLY表示只读

示例

通过指针访问共享内存,指针类型取决于共享内存中存放的数据类型     例如:在共享内存中存放键盘输入的字符串

共享内存撤销映射 – shmdt

#include <sys/ipc.h>
#include <sys/shm.h>
int  shmdt(void *shmaddr);
  • 成功时返回0,失败时返回EOF  
  • 不使用共享内存时应撤销映射  
  • 进程结束时自动撤销

共享内存控制 – shmctl

#include <sys/ipc.h>
#include <sys/shm.h>
int  shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • 成功时返回0,失败时返回EOF  
  • shmid   要操作的共享内存的id  
  • cmd   要执行的操作  IPC_STAT  IPC_SET  IPC_RMID  
  • buf   保存或设置共享内存属性的地址

共享内存  -  注意事项

每块共享内存大小有限制

查看共享内存命令

  ipcs  -lcat  /proc/sys/kernel/shmmax

 示例

#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/shm.h>
#include <string.h>int main(){key_t key;int shmid;char *buf;//1.创建keykey = ftok("keytest",100);if(key<0){perror("ftok");return 0;}printf("key=%x\n",key);//2.创建共享内存shmid = shmget(key,512,IPC_CREAT|0666);if(shmid<0){perror("shmget");return 0;}printf("shmid=%d\n",shmid);//3.映射共享内存buf = shmat(shmid,NULL,0);if(buf<0){perror("shmat");return 0;}    //拷贝strcpy(buf,"hello world");}

示例2 写共享内存 

#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/shm.h>
#include <string.h>int main(){key_t key;int shmid;char *buf;key = ftok("keytest",100);if(key<0){perror("ftok");return 0;}printf("key=%x\n",key);shmid = shmget(key,512,IPC_CREAT|0666);if(shmid<0){perror("shmget");return 0;}printf("shmid=%d\n",shmid);buf = shmat(shmid,NULL,0);if(buf<0){perror("shmat");return 0;}    strcpy(buf,"hello world");}

示例3 读共享内存 

#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>int main(){key_t key;int shmid;char *buf;key = ftok("keytest",100);if(key<0){perror("ftok");return 0;}printf("key=%x\n",key);shmid = shmget(key,512,0666);//不需要CREATEif(shmid<0){perror("shmget");return 0;}    printf("shmid=%d\n",shmid);buf = shmat(shmid,NULL,0);if(buf<0){perror("shmat");return 0;}    
//    strcpy(buf,"hello world");printf("share mem=%s\n",buf);while(1){sleep(1);}    //4.共享内存撤销映射shmdt(buf);//5.共享内存删除,防止内存泄漏shmctl(shmid, IPC_RMID, NULL);//    printf("detach mem=%s\n",buf);}

 删除后

未删除 启动两个的状态值为2,没法撤销了,使用时候需要注意

共享内存删除的时间点  

        添加删除标记  

        nattach 变成0时真正删除

3 共享内存与管道区别

共享内存和管道是进程间通信的两种机制,它们有以下区别:

  1. 数据结构:共享内存是一块被多个进程共享的内存区域,进程可以直接读写这块内存;而管道是通过内核中的缓冲区来传递数据。

  2. 通信方式:共享内存提供了一种高效的内存共享方式,进程可以直接在共享内存中读写数据,因此速度较快;而管道是一种半双工的通信方式,数据只能单向流动,需要两个管道才能实现双向通信。

  3. 同步机制:在使用共享内存进行通信时,进程需要使用一些同步机制(如信号量、互斥锁等)来确保多个进程之间对共享内存的访问不会产生冲突;而在管道中,由于数据流是单向的,多个进程之间通常不会发生冲突。

  4. 编程复杂性:共享内存在编程上相对较为复杂,需要注意进程间的同步与互斥;而管道在编程上较为简单,但由于数据流是单向的,双向通信需要使用两个管道。

综上所述,共享内存适用于需要高效共享大量数据的场景,但需要考虑同步与互斥问题;而管道适用于简单的进程间通信,但数据流是单向的。选择合适的通信机制需要根据具体的应用场景和需求来决定。

4 练习

实现两个进程使用内存映射通信程序

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>int main(int argc,char *argv[])
{pid_t pid;void *addr;int len;addr = mmap(NULL, 2048, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);if(addr == MAP_FAILED){perror("mmap");return 0;}pid = fork();if(pid < 0){perror("fork");return 0;}else if( pid > 0){printf("This is father process\n");memcpy(addr, "father write", 12);wait(NULL);}else //( pid == 0){printf("This is child process\n");sleep(1);printf("child recv:%s\n",(char *)addr);}munmap(addr,2048);return 0;
}//运行结果
linux@linux:~/Desktop$ ./mmap_test 
This is father process
This is child process
child recv:father write

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

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

相关文章

nodejs+vue中国非物质文化遗产网站设计与实现elementui

前端页面&#xff1a; 导航栏借鉴下面的 1首页&#xff1a;带有一个全屏轮播图和其他的内容 2咨询页&#xff1a;有关中国非物质文化遗产的一些新闻咨询网站对于记录非遗这种无形的、动态的文化资源有着其他技术无可替代的优势。用户可以在该网站浏览、了解和学习非遗文化&…

uni-app:canvas-绘制图形4(获取画布宽高,根据画布宽高进行图形绘制)

效果 代码 var width ; var height ; const query uni.createSelectorQuery(); //获取宽度 query.select(#firstCanvas).fields({ size: true }, (res) > { width res.width; height res.height; }).exec(); console.log(宽度width); console.log(高…

关于Pod的内存使用率一直很高的问题分析

生产环境中在流量高峰期出现pod内存使用率很高&#xff0c;pod批量重启&#xff0c;错误日志中还有OOM相关信息。 查看堆内存的使用值 Pod使用的内存不能直接在pod中通过top命令查看&#xff0c;这种方式看到的是pod所在node的资源使用情况。想查看pod的资源使用情况需要用ku…

SEO的优化教程(百度SEO的介绍和优化)

百度SEO关键字介绍&#xff1a; 百度SEO关键字是指用户在搜索引擎上输入的词语&#xff0c;是搜索引擎了解网站内容和相关性的重要因素。百度SEO关键字可以分为短尾词、中尾词和长尾词&#xff0c;其中长尾词更具有针对性和精准性&#xff0c;更易于获得高质量的流量。蘑菇号-…

【Matplotlib画图】使用Python Matplotlib画三维的子图

文章目录 1. 代码2. 画图效果写在最后 1. 代码 在matlab转过来&#xff0c;之前一直不知道python的写法&#xff0c;以为是像matlab一样返回一个句柄然后在上面添加元素&#xff1b; 其实是应该先创建一个画布&#xff0c;然后再在上面添加子图&#xff0c;然后再使用返回的句…

解决 MyBatis-Plus 中增加修改时,对应时间的更新问题

问题&#xff1a;在添加修改时&#xff0c;对应的 create_time 与 insert_time 不会随着添加修改而自动的更新时间 第一步&#xff1a;首先在对应的属性上&#xff0c;加上以下注解 如果只添加以下注解&#xff0c;在增加或者修改时&#xff0c;可能对应的 LocalDateTime 会出…

基于微信小程序的公交信息在线查询系统小程序设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言系统主要功能&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计…

支付宝支付模块开发

生成二维码 使用Hutool工具类生成二维码 引入对应的依赖 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.5</version> </dependency><dependency><groupId>com.go…

数码产品数码配件无线键盘等出口欧盟CE-RED认证测试办理

数码产品数码配件无线键盘CE-RED认证测试办理 无线产品CE-RED认证进入东欧市场规定&#xff1a; 在通信终端设备和无线产品在这些/地区合法销售之前&#xff0c;必须按照 RED 指令进行测试&#xff0c;并且还必须提供 CE 标志。无线远程控制产品必须符合 RED 指令的 REDEU 要…

华为云HECS云服务器docker环境下安装nginx

前提&#xff1a;有一台华为云服务器。 华为云HECS云服务器&#xff0c;安装docker环境&#xff0c;查看如下文章。 华为云HECS安装docker-CSDN博客 一、拉取镜像 下载最新版Nginx镜像 (其实此命令就等同于 : docker pull nginx:latest ) docker pull nginx查看镜像 dock…

JS对象数组去重

JS对象数组去重 一、数组去重1.使用 new Set()2.使用 indexOf 去重3.使用 includes 去重4.使用 hasOwnProperty5.使用 filter6.使用递归7.利用 Map 数据结构去重8.使用用 reduce includes9.使用 new Set() 的简化 二、对象数组去重1.使用 new Map() 和 filter2.使用reduce3.使…

【JVM】第四篇 垃圾收集器ParNewCMS底层三色标记算法详解

导航 一. 垃圾收集算法详解1. 分代收集算法2. 标记-复制算法3. 标记-清除算法4. 标记-整理算法二. 垃圾收集器详解1. Serial收集器2. Parallel Scavenge收集器3. ParNew收集器4. CMS收集器三. 垃圾收集底层三色标记算法实现原理1. 垃圾收集底层使用三色标记算法的原因?2. 垃圾…

计算机竞赛 深度学习图像分类算法研究与实现 - 卷积神经网络图像分类

文章目录 0 前言1 常用的分类网络介绍1.1 CNN1.2 VGG1.3 GoogleNet 2 图像分类部分代码实现2.1 环境依赖2.2 需要导入的包2.3 参数设置(路径&#xff0c;图像尺寸&#xff0c;数据集分割比例)2.4 从preprocessedFolder读取图片并返回numpy格式(便于在神经网络中训练)2.5 数据预…

1.centos7 安装显卡驱动、cuda、cudnn

安装conda 参考 python包 2.安装conda python库-CSDN博客 1.安装显卡驱动 步骤1&#xff1a;安装依赖 yum -y install kernel-devel yum -y install epel-release yum -y install gcc 步骤2&#xff1a;查询显卡版本 lspci | grep VGA 找到2230号码&#xff0c;进入如下网…

Swift data范围截取问题

文章目录 一、截取字符串的几种方法1. 截取前几位2. 截取后几位3. subData4. 下标截取 二、subData(in:) 报错 EXC_BREAKPOINT 一、截取字符串的几种方法 1. 截取前几位 mobileID.prefix(32)2. 截取后几位 mobileID.suffix(3)3. subData data.subdata(in: 0..<4)4. 下标…

大学生登记国家证书软件著作权提升就业资质

大学生登记国家证书软件著作权提升就业资质 随着信息技术的快速发展&#xff0c;软件行业成为了许多大学生就业的热门选择之一。然而&#xff0c;在竞争激烈的就业市场中&#xff0c;除了掌握专业知识和技能外&#xff0c;如何提升自己的就业资质也显得尤为重要。其中&#xff…

postman接口测试系列: 时间戳和加密

在使用postman进行接口测试的时候&#xff0c;对于有些接口字段需要时间戳加密&#xff0c;这个时候我们就遇到2个问题&#xff0c;其一是接口中的时间戳如何得到&#xff1f;其二就是对于现在常用的md5加密操作如何在postman中使用代码实现呢&#xff1f; 下面我们以一个具体的…

selenium不定位元素直接使用键盘操作(如弹框操作)

今天在使用selenium进行定位时&#xff0c;发现直接定位不了chrome的弹框&#xff0c;如这种弹框&#xff1a; 使用的是下面这行代码 driver.switch_to.alert.accept() 运行报错&#xff0c;说是没有 alert windown。。。。 啊&#xff1f;难道chrome的弹框不是用alert写的&…

冲刺十五届蓝桥杯P0001阶乘求和

文章目录 题目描述思路分析代码解析 题目描述 思路分析 阶乘是蓝桥杯中常考的知识。 首先我们需要知道 int 和long的最大值是多少。 我们可以知道19的阶乘就已经超过了long的最大值&#xff0c;所以让我们直接计算202320232023&#xff01;的阶乘是不现实的。 所以我们需要…

WhatsOnChain中的sCrypt合约验证插件

我们很高兴地宣布在 WhatsOnChain 上集成了 sCrypt 智能合约验证插件。该插件允许任何人为已部署的智能合约提交和验证 sCrypt 代码。 智能合约验证 在与智能合约交互之前&#xff0c;用户需要确保智能合约按照其声称的方式行事。理论上&#xff0c;每个人都可以查看合约并验…