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

news2025/1/17 3:17:28

目录

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回收子进程,是阻塞函数可以不需要sleep
        wait(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  -l
  cat  /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.创建key
    key = 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);//不需要CREATE

    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");
    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.coloradmin.cn/o/1047268.html

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

相关文章

OV2640图像出现细小条纹问题

说明&#xff1a;条纹是随机生成的&#xff0c;有时候几根线&#xff0c;有时候10多根线 怀疑点&#xff1a; 1-走线&#xff0c;看网上说所有的时钟线和同步线不要交叉&#xff0c;不要太长没有验证 2-电源&#xff0c;1.2V和2.8的电源 刚开始以为是自己电路问题&#xff0c…

栈(Stack)和队列(Queue)

栈&#xff08;Stack&#xff09;和队列&#xff08;Queue&#xff09;都是常见的数据结构&#xff0c;用于存储和操作一组元素。 栈是一种后进先出&#xff08;Last-In-First-Out&#xff0c;LIFO&#xff09;的数据结构&#xff0c;类似于把元素堆在一起形成的一堆物体&…

PTA程序辅助实验平台——2023年软件设计综合实践_3(分支与循环)

第一题&#xff1a;7-1 印第安男孩 - C/C 分支与循环 朵拉编程的时候也想顺便练习英语。她编程从键盘读入一个整数n&#xff0c;如果n值为0或者1&#xff0c;向屏幕输出“0 indian boy.”或“1 indian boy.”&#xff1b;如果n大于1&#xff0c;比如9&#xff0c;则输出“9 in…

Ctfshow web入门 XSS篇 web316-web333 详细题解 全

CTFshow XSS web316 是反射型 XSS 法一&#xff1a; 利用现成平台 法二&#xff1a; 自己搭服务器 先在服务器上面放一个接受Cookie的文件。 文件内容&#xff1a; <?php$cookie $_GET[cookie];$time date(Y-m-d h:i:s, time());$log fopen("cookie.txt"…

SSM - Springboot - MyBatis-Plus 全栈体系(十四)

第三章 MyBatis 二、MyBatis 基本使用 1. 向 SQL 语句传参 1.1 mybatis 日志输出配置 mybatis配置文件设计标签和顶层结构如下&#xff1a; configuration&#xff08;配置&#xff09; properties&#xff08;属性&#xff09;settings&#xff08;设置&#xff09;typeAl…

7、SpringBoot_高级配置

一、配置高级 1.临时属性设置 1.1引出问题 如果目标电脑上8080端口已经使用&#xff0c;再次使用该端口会出现端口占用问题 解决方式 重新更换配置文件修改端口打包通过临时属性配置新端口更换配置文件 1.2添加临时属性配置 通过临时属性修改8080端口 java -jar 项目.jar…

zemax目镜调焦演示

目镜的焦距应当是可变的&#xff0c;就像我们使用显微镜时&#xff0c;可以通过旋转调节旋钮&#xff0c;使得图像变得清晰&#xff0c;目镜的焦距也可以调整&#xff0c;使得要被观察的像出现在合适的位置。 这样&#xff0c;不同的人都可以使用同一台仪器&#xff0c;不管近…

Spring5应用之AOP额外功能详解

作者简介&#xff1a;☕️大家好&#xff0c;我是Aomsir&#xff0c;一个爱折腾的开发者&#xff01; 个人主页&#xff1a;Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客 当前专栏&#xff1a;Spring5应用专栏_Aomsir的博客-CSDN博客 文章目录 前言MethodBefore…

Doris数据库FE——启动流程源码详细解析

Doris中FE主要负责接收和返回客户端请求、元数据以及集群管理、查询计划生成等工作。代码路径&#xff1a;doris/fe/fe-core/src/main/java/org/apache/doris/DorisFE.java 环境检查 在启动FE的时候&#xff0c;主要做环境检查。检查一些启动时必要的环境变量以及初始化配置…

社区团购新零售搭伙拼团小程序源码(前后端)

社区团购新零售搭伙拼团小程序源码是一款非常实用的电商小程序&#xff0c;它包含了前后端文件&#xff0c; 可以快速地进行部署和使用。该小程序是基于微信小程序开发的&#xff0c;支持社区团购、新零售、搭伙拼团等多种功能。 该小程序具有良好的用户体验&#xff0c;包括…

范数Norm-衡量向量大小的方法

性质 非负性: 范数的值总是非负的,且当且仅当向量全为零时,范数的值为零。 齐次性: 对于任意实数α,有 三角不等式: 对于任意向量x和y,有 常见范数 L1: 向量所有元素绝对值的和,权重稀疏 L2:欧几里得范数,权重平滑 无穷范数:表示向量中最大的元素 为什么使用范…

英飞凌 Tricore 架构中断系统详解

本文以TC3系列MCU为例&#xff0c;先来了解中断源是如何产生的&#xff0c;再看一下CPU是如何处理中断源的。 AURIX TC3XX的中断路由模块 Interrupt Router (IR) 在TC3中&#xff0c;中断既可以被CPU处理&#xff0c;也可以被DMA处理&#xff0c;所以手册中不再把中断称为中断…

vue3硅谷甄选02 | 封装svg组件 - axios二次封装

文章目录 vue3硅谷甄选02功能1&#xff1a;封装svg组件SVG图标配置svg封装成组件svg组件注册为全局组件自定义统一注册全局组件的插件自定义插件的原理插件的使用 app.use(plugin, [options]) 功能2&#xff1a;axios二次封装使用mock插件构造数据axios二次封装api接口统一管理…

【大数据开发技术】实验01-Hadoop安装部署

文章目录 Hadoop安装部署一、实验目标二、实验要求三、实验内容四、实验步骤附&#xff1a;系列文章 Hadoop安装部署 虚拟机数量&#xff1a;3 系统版本&#xff1a;Centos 7.5 Hadoop版本&#xff1a; Apache Hadoop 2.7.3 主节点信息&#xff1a; 操作系统&#xff1a;Cen…

Tomcat 与 JDK 对应版本关系

对应关系 Tomcat版本 jdk版本11.0.x JDK 21及以后10.1.x JDK11及以后10.0.xJDK1.8及以后9.0.x JDK1.8及以后8.5.xJDK1.7及以后8.0.x JDK1.7及以后 查看对应关系方法&#xff1a; 登陆Tomcat官网&#xff1a;Apache Tomcat - Welcome! 结果&#xff1a;

Arthas:Java调试利器使用

Arthas:Java调试利器使用 1. Arthas是什么2. Arthas可以解决什么问题Arthas启动方式1. jar启动2. 在线安装 远程连接命令使用- 退出threadclassloaderscsm watchtrace修改日志级别 1. Arthas是什么 Arthas(阿尔萨斯)是阿里开源的一个Java在线分析诊断工具. 2. Arthas可以解决…

C#(CSharp)入门实践项目(简易回合制游戏)

项目名称 木木夕营救公主 项目介绍 这是一个小游戏&#xff0c;你将扮演一个英雄&#xff08;木木夕&#xff09;&#xff0c;去打败恶龙&#xff0c;拯救出公主&#xff0c;该项目采用回合制战斗模式&#xff0c;由于角色的血量和攻击为随机数&#xff0c;所以需要靠运气才…

YOLOv7改进:CBAM注意力机制

目录 1.介绍 1.1、论文的出发点 1.2、论文的主要工作 1.3、CBAM模块的具体介绍 2.YOLOv7改进 2.1yaml 配置文件如下 2.2common.py配置 2.3yolo.py配置 1.介绍 1.1、论文的出发点 cnn基于其丰富的表征能力&#xff0c;极大地推动了视觉任务的完成&#xff0c;为了提高…

【MySql】3- 实践篇(一)

文章目录 1. 普通索引和唯一索引的选择1.1 查询过程1.2 更新过程1.2.1 change buffer1.2.2 change buffer 的使用场景 1.3 索引选择和实践1.4 change buffer 和 redo log2. MySQL为何有时会选错索引?2.1 优化器的逻辑2.1.1 扫描行数是怎么判断的?2.1.2 重新统计索引信息 2.2 …

一站式吃鸡利器,提升游戏战斗力,助您稳坐鸡王宝座!

各位吃鸡玩家们&#xff0c;听说过绝地求生作图工具吗&#xff1f;想知道如何提高游戏战斗力、分享顶级作战干货、查询装备皮肤库存&#xff1f;还在为游戏账号安全而担心吗&#xff1f;别急&#xff0c;今天就为您介绍一款一站式吃鸡利器&#xff0c;满足您的所有需求&#xf…