Linux应用---内存映射

写在前面:

        在进程间通信中,有一种方式内存映射。内存映射也是进程间通信的方式之一,其效率高,可以直接对内存进行操作。本节我们对内存映射进行学习,并结合案例进行实践。

1、基本理论

内存映射:是将磁盘文件中的数据映射到内存,用户通过修改内存就能修改磁盘文件。

那通过内存映射如何实现进程之间的通信呢?简单来说就是将同一个文件存储映射部分分别映射到不同的进程中,两个进程通过改变文件的内容(读写内存)来实现通信,不必再使用read和write函数等系统调用,加快文件的读取和写入。

内存映射相关函数

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);//用于文件或者设备映射到内存中去
int munmap(void *addr, size_t length);//释放内存映射

1、mmap函数

涉及头文件:#include <sys/mman.h>

函数原型:void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);

功能:将一个文件或者设备的数据映射到内存中去;

参数:  - void *adder:NULL;由内核指定。

             -lenth:要映射的内存的长度,这个值不能为0;建议使用文件的长度;

                    获取文件的长度:stat lseek;

                 没有达到分页的大小,按照分页大小进行,所以是分页的整数倍。

                 所以一般情况下,申请的内存区域大于等于文件的大小。

             -prot:对申请的内存区的操作权限;

                     PROT_EXEC  可执行的权限

                     PROT_READ  读的权限

                     PROT_WRITE 写的权限

                     PROT_NONE  没有权限

                  要操作映射内存,必须要读的权限-PROT_READ、PROT_READ|PROT_WRITE

              -flags:

               MAP_SHARED:映射区的数据会自动和磁盘文件进行同步,如果要完成进程间通信,必须设置这个选项;

               MAP_PRIVATE:不同步,内存映射区的数据改变了,对原来的文件不会修改,会重新创建一个新的文件。copy on write.

                -fd:

                    需要操作的文件描述符,通过open得到,打开的是一个磁盘文件。

                    注意:文件的大小不能为0;

                          open指定的权限,不能和prot冲突;

                          port:PROT_READ; open:只读/读写;

                          port:PROT_READ|PROT_WRITE; open:读写;

                          总的来说:port的权限要小于open的权限,必须要有读的权限。

                -offset:偏移量,一般不用,必须要指定的是4k的整数倍。0表示不偏移。

        返回值:

                -成功:返回创建内存的首地址;

                -失败:返回MAP_FAILED (void *)-1;

2、munmap函数 

涉及头文件:#include<sys/mman.h>

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

功能:释放内存映射;

参数:

            void *addr:释放的内存的首地址;

            length:要释放的内存的大小,要和mmap函数中的length的值一样。

2、案例一:通过内存映射实现父子进程间的通信

实现方案:

        在没有子进程的时候,通过唯一的父进程创建内存映射区;

        有了内存映射区,再创建子进程;

        父子进程共享内存映射区;

实现流程:

        1、打开一个文件;

        2、获取文件大小(用于mmap函数的参数);

        3、创建内存映射区;fork()之后父子进程共享内存映射区。

        4、创建子进程;

               父进程读取数据,子进程发送数据;(因为子进程发送完成后,可以被父进程进行回收,避免僵尸进程的产生)。

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <stdlib.h>

int main()
{

    //1、打开一个文件
         int fd = open("text.txt",O_RDWR);
    //2、获取大小
     int size=lseek(fd,0,SEEK_END);
    //3、创建内存映射区
     void *ptr =  mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(ptr==MAP_FAILED)
    {
        perror("mmap");
        exit(0);
    }
    //4、创建子进程
        pid_t pid = fork();
        if(pid>0)
        {
            wait(NULL);
            //父进程:
            char buf[64];
            strcpy(buf,(char *)ptr );
            printf("read data:%s\n",buf);
           
        }
        else if(pid==0)
        {
            //子进程:
         strcpy((char *)ptr,"nihao,sun!!");
        }
        //关闭内存映射区:
        munmap(ptr,size);

    return 0;
}

运行结果:

3、案例二:通过内存映射实现没有关系进程之间的通信

实现方案:

        准备一个大小不为0的磁盘文件;

        进程1 通过磁盘文件(与进程2的文件相同)创建内存的映射区;

                  得到一个操作这块内存的指针。

        进程2 通过磁盘文件(与进程1的文件相同)创建内存映射区;

                  得到一个操作这块内存的指针。

                  使用内存映射区进行通信。

 注意:内存映射区通信,没有阻塞;

进程1:写内容

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <stdlib.h>

int main()
{
   // 1、准备一个磁盘文件。
   // 2、通过磁盘文件创建内存的映射区;
    int fd = open("test.txt",O_RDWR);
    int size= lseek(fd,0,SEEK_END);
    void * prt=mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
      if( prt == MAP_FAILED)
    {
        perror("mmap");
        exit(0);
    }

    strcpy((char *)prt,"sixsixsix");
    munmap(prt,size);
    return 0;
}

进程2:读文件

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <stdlib.h>

int main()
{
   // 1、准备一个磁盘文件。
   // 2、通过磁盘文件创建内存的映射区;
    int fd = open("test.txt",O_RDWR);
    int size= lseek(fd,0,SEEK_END);
    void * prt=mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
      if( prt == MAP_FAILED)
    {
        perror("mmap");
        exit(0);
    }
    char buf[64];
    strcpy(buf,(char *)prt);
    printf("read data:%s\n",buf);
    munmap(prt,size);
    return 0;
}

运行结果: 

4、内存映射的注意事项

1、如果对mmap的返回值(ptr)做++操作,munmap能够成功吗?
    void * ptr =mmap(...)
    可以对其进行++操作,但是不建议因为释放的时候,需要把最开始的地址记录下来。
2、如果open时,O_RDONLY,mmap时prot参数指定 PROT_READ|PROT_WRITE 会怎么样?
    错误,会返回宏MAP_FAILED
    open()权限建议和prot参数保持一致,更准确的说open()的权限要大于prot参数的权限;
3、如果文件偏移量为1000会怎么样?
    偏移量必须是4k的整数倍,否则 错误,会返回宏MAP_FAILED
4、mmap什么情况下会调用失败?
    -第二个参数:length=0;
    -第三个参数:prot权限
                    -只是指定了写权限;
                    -prot参数权限为:PROT_READ|PROT_WRITE,第5个参数文件描述符fd(通过open函数打开时,O_RDONLY、O_WRONLY)
5、可以open的时候,O_CREAT一个新文件来创建映射区。
    可以的,但是创建的文件的大小如果为0,肯定不行;
    -lseek()
    -truncate()
    进行扩展;
6、mmap后关闭文件描述符,对mmap映射有没有影响?
    int fd=open("xxx");
    mmap(,,,,fd,0);
    close(fd);
    映射区还存在,创建映射区的fd被关闭,没有任何影响;
7、对ptr进行越界操作会怎么?
    void *ptr=mmap(NULL,100..);
    4k
    越界操作,操作的是非法内存,-段错误。

5、使用内存映射实现文件的拷贝

使用内存映射实现文件拷贝的功能

/*

    1、对原始的文件进行内存映射;

    2、创建一个新的文件,新文件进行扩展;

    3、把新文件的数据映射到内存中;

    4、通过内存拷贝,将第一个文件的内存数据拷贝到新的文件内存中;

    5、释放资源;

*/

#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
  // 1、对原始的文件进行内存映射;
    int fd =open("english.txt",O_RDWR);
    if(fd==-1)
    {
        perror("open");
        exit(0);
    }
  //获取原始文件的大小
    int len=lseek(fd,0,SEEK_END);
  // 2、创建一个新的文件,新文件进行扩展;
    int fd1 =open("cpy.txt",O_RDWR|O_CREAT,0664);
    if(fd1==-1)
    {
      perror("open");
      exit(0);
    }
  //对新创建的文件进行拓展
    truncate("cpy.txt",len);
    write(fd," ",1);
   //3、分别做内存映射
    void * ptr= mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    void * ptr1= mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd1,0);

  if(ptr == MAP_FAILED)
  {
    perror("mmap");
    exit(0);
  }
  if(ptr1 == MAP_FAILED)
  {
    perror("mmap");
    exit(0);
  }
  //内存拷贝
  memcmp(ptr1,ptr,len);
  //释放资源
  munmap(ptr1,len);
  munmap(ptr,len);
  close(fd1);
  close(fd);
  return 0;
}

 运行结果:

6、父子进程间匿名内存映射

  匿名映射不需要文件实体,直接进行内存映射。在父子进程中可以使用匿名映射。没有关系的进程不能进行,没有关联了。

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

关键在于:port需要用到MAP_ANONYMOUS,此参数是匿名映射所需要的。

#define _DEFAULT_SOURCE 
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    //1、创建匿名内存映射区
    int len =4096;
   void *ptr = mmap(NULL,len,PROT_READ|PROT_WRITE ,MAP_SHARED | MAP_ANONYMOUS,-1,0);//
    if(ptr==MAP_FAILED)
    {
        perror("mmap");
        exit(0);
    }
    //2、父子进程通信
    pid_t pid=fork();

    if(pid>0)
    {
        //父进程
        strcmp((char*)ptr,"hello,world");
        wait(NULL);
    }
    else if(pid==0)
    {
        //子进程
        sleep(1);
        printf("%s\n",(char*)ptr);
    }

    //释放内存映射区
    int ret=munmap(ptr,len);
    if(ret==-1)
    {
        perror("munmap");
        exit(0);
    }
     int ret1=munmap(ptr,len);
    if(ret1==-1)
    {
        perror("munmap");
        exit(0);
    }

}

运行结果:

 以上便是进程间内存映射的相关知识,结合案例进行了分析,大家学习后一定要多多练习!!

创作不易,还请多多点赞支持!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ODN网络弱光聚类定界与整治

01 ODN网络弱光运维现状 ODN网络是家庭宽带连接系统-无源光网络 (PON) 的重要组成部分&#xff0c;是连接局端 OLT 和用户 ONT 之间的光路通道&#xff0c;其质量直接影响整个PON系统的性能及可靠性。ODN光纤链路包括OLT PON口、ODF、主干光纤、一级分光器、分支光纤、二级分光…

登录功能和校验

基础版 controller package com.web.management.controller;import com.web.management.pojo.Emp; import com.web.management.pojo.Result; import com.web.management.service.EmpService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.anno…

如何用Vue3和Plotly.js绘制交互式漏斗图

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 Plotly.js 绘制漏斗图 应用场景 漏斗图常用于可视化业务流程中的各个阶段的转换率&#xff0c;例如销售漏斗或营销漏斗。它可以帮助用户识别流程中的瓶颈和改进机会。 基本功能 本代码使用 Plotly.js 库绘制…

【微机原理及接口技术】中断控制器8259A

【微机原理及接口技术】中断控制器8259A 文章目录 【微机原理及接口技术】中断控制器8259A前言一、介绍二、8259A的内部结构和引脚三、8259A的中断工作过程四、8259A的工作方式五、8259A的编程六、外部中断服务程序总结 前言 本篇文章将就8259芯片展开介绍&#xff0c;8259A的…

【多媒体】富客户端应用程序GUI框架 JavaFX 2.0 简介

JavaFX 最初是由 Oracle 推出的一个用于开发富客户端应用程序的框架&#xff0c;它提供了丰富的用户界面控件、布局容器、3D图形绘制、媒体播放和动画等功能&#xff0c;旨在取代较旧的 Swing 框架。JavaFX 于 2007 年推出&#xff0c;2011 年 10 月发布了2.0 版本。JavaFX 2.0…

OpenLayers使用

初学ol&#xff0c;实现了高德地图不同图层的切换、交互性地图飞行以及加载本地JSON数据。 说一下不同图层切换的想法&#xff1a; 1.对于标准地图和卫星地图&#xff1a;二者最初便挂载到map上&#xff0c;两个图层是叠加显示的&#xff1b;当点击按钮时&#xff0c;其实是使…

VSCode里python代码不扩展/级联了的解决办法

如图 解决办法&#xff1a;重新下载新的扩展工具 步骤如下 1、在左边工具栏打开Extensions 2、搜索框输入python&#xff0c;选择别的扩展工具&#xff0c;点击Install - 3在扩展工具所在的目录下&#xff0c;新建一个文件&#xff0c;就可以用了

指定IP地址通过远程桌面访问WINDOWS10

1:登录Windows10系统&#xff0c;在控制面板找到系统和安全&#xff0c;打开Windows Defender防火墙。 2&#xff1a;点击感觉设置。 3&#xff1a;在入站规则中&#xff0c;找到远程桌面。查看自己的网络现在是公用&#xff0c;域&#xff0c;还是专用。选择对应的网络。 4&am…

Oracle EBS PO采购订单预审批状态处理

系统版本 RDBMS : 12.1.0.2.0 Oracle Applications : 12.2.6 问题症状: 采购订单状态:预审批 采购订单流程报错如下: po.plsql.PO_DOCUMENT_ACTION_AUTH.approve:90:archive_po not successful - po.plsql.PO_DOCUMENT_ACTION_PVT.do_action:110:unexpected error in acti…

js生成器,迭代器和可迭代对象详解

1.生成器函数和生成器 生成器函数是可以返回一个可迭代对象的特殊函数&#xff0c; 生成器是一个特殊的迭代器&#xff0c; 在js中可以使用function*来定义一个非连续执行的函数作为迭代算法&#xff0c; function* name() {yield value;yield value;yield value; }name: 函…

基于YOLOv5的人脸目标检测

本文是在之前的基于yolov5的人脸关键点检测项目上扩展来的。因为人脸目标检测的效果将直接影响到人脸关键点检测的效果&#xff0c;因此本文主要讲解利用yolov5训练人脸目标检测(关键点检测可以看我人脸关键点检测文章) 基于yolov5的人脸关键点检测&#xff1a;人脸关键点检测…

ROS学习笔记(18):建图与定位(2)

0.前言 上文提到现在的我们已经进入到了SLAM领域的学习&#xff0c;会涉及到大量专业知识&#xff0c;作为一个自学的大三&#xff08;好吧也快大四了&#xff09;萌新并不能保证每次文章的专业性和准确性&#xff0c;所以&#xff0c;本人推荐大家能自己去查阅一些相关书籍和…

TOB传输、承载网拓扑图

1、用户面&#xff1a;GNODEB>UPE>SPE>NPE>UPF>CMNET网 2、控制面&#xff1a;GNODEB>UPE>SPE>NPE>IP承载网>核心网

充分利用智慧校园人事系统,提升党政职务管理

智慧校园人事系统中的党政职务管理功能&#xff0c;是专为高校及教育机构设计的&#xff0c;旨在高效、精确地处理与党政职务相关的各类事务&#xff0c;包括职务任命、任期管理、职责分配、考核评估等&#xff0c;以信息化手段促进党务及行政工作的透明化、规范化。 该模块首先…

redis主从复制哨兵模式集群管理

主从复制&#xff1a; 主从复制是高可用Redis的基础&#xff0c;哨兵和集群都是在主从复制基础上实现高可用的。主从复制主要实现了数据的多机备份&#xff0c;以及对于读操作的负载均衡和简单的故障恢复。缺陷&#xff1a;故障恢复无法自动化&#xff1b;写操作无法负载均衡&…

像学Excel 一样学 Pandas系列-创建数据分析维度

嗨&#xff0c;小伙伴们。又到喜闻乐见的Python 数据分析王牌库 Pandas 的学习时间。按照数据分析处理过程&#xff0c;这次轮到了新增维度的部分了。 老样子&#xff0c;我们先来回忆一下&#xff0c;一个完整数据分析的过程&#xff0c;包含哪些部分内容。 其中&#xff0c…

好久不见!写了一个自动截图神器~【附源码】

文章目录 前言新增功能介绍截图功能快捷键设置 程序设计和使用介绍操作菜单栏选择点击坐标点选择图片选择截图区域快捷键设置 表格循环次数状态栏 使用案例源代码 前言 好久没更新文章了。上一次更新是在4月16日差不多&#xff0c;也只是写了一个错误集&#xff0c;没什么太多…

【Python机器学习】模型评估与改进——在模型选择中使用评估指标

我们通常希望&#xff0c;在使用GridSearchCV或cross_val_score进行模型选择时能够使用AUC等指标。scikit-learn提供了一种非常简单的实现方法&#xff0c;那就是scoring参数&#xff0c;它可以同时用于GridSearchCV和cross_val_score。你只需要提供一个字符串&#xff0c;用于…

基于Vue的MOBA类游戏攻略分享平台

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;Java技术、SpringBoot框架、B/S模式、Vue.js 工具&#xff1a;MyEclipse、MySQL 系统展示 首页 用…

大模型技术在辅助学习中的应用

大模型技术在辅助学习中的应用场景非常广泛&#xff0c;以下是一些典型示例。大模型技术在辅助学习中具有广阔的应用前景&#xff0c;可以为学生提供更加个性化、智能化和高效的学习体验。随着大模型技术的不断发展&#xff0c;我们可以期待在未来看到更多创新应用。北京木奇移…