Linux应用---内存映射

news2024/10/5 17:20:44

写在前面:

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

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.coloradmin.cn/o/1890138.html

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

相关文章

日期和时区

日期 时区 修改时区可分为两步 删除系统自带的 localtime 文件 rm -f /etc/localtime 将系统中内置的 Shanghai 文件软连接到 /etc/localtime中 sudo ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

Softing助力工业4.0 | 通过OPC UA和MQTT访问SINUMERIK 840D CNC控制器数据

Softing uaGate 840D是用于采集西门子SINUMERIK 840D SL/PL CNC控制器数据的物联网网关&#xff0c;支持OPC UA服务器和MQTT发布功能。该网关提供对SINUMERIK 840D CNC控制器机床数据的访问&#xff0c;支持读取、处理重要的主轴和从轴数据&#xff0c;例如扭矩和功耗&#xff…

第一周题目总结

1.车尔尼有一个数组 nums &#xff0c;它只包含 正 整数&#xff0c;所有正整数的数位长度都 相同 。 两个整数的 数位不同 指的是两个整数 相同 位置上不同数字的数目。 请车尔尼返回 nums 中 所有 整数对里&#xff0c;数位不同之和。 示例 1&#xff1a; 输入&#xff1a…

16:9横屏素材去哪里找?优秀的横屏视频素材资源网站分享

在这个视频内容快速发展的时代&#xff0c;寻找合适的视频素材变得尤为重要。以下是几个优秀的16:9横屏视频素材提供网站&#xff0c;无论您是视频制作新手还是资深专家&#xff0c;这些网站都能为您的视频项目增添光彩。 蛙学网 蛙学网作为视频创作者们广为人知的一个平台&am…

茗鹤 | 如何借助APS高级计划排程系统提高汽车整车制造的效率

在我们做了详尽的市场调研及头部汽车制造企业排程需求沟通后&#xff0c;我们发现尽管企业有很多的业务系统做支撑&#xff0c;在计划排程领域&#xff0c;所有的汽车制造总装厂仍旧使用人工“Excel”做排产规划&#xff0c;其中少部分也会借助MRP、第三方辅助排产工具。鉴于我…

科研与英文学术论文写作指南——于静老师课程

看到了一个特别棒的科研与英文学术论文写作指南&#xff0c;理论框架实例。主讲人是中科院信息工程研究所的于静老师。推荐理由&#xff1a;写论文和读论文或者讲论文是完全不一样的&#xff0c;即使现在还没有发过论文&#xff0c;但是通过于老师的课程&#xff0c;会给后续再…

使用 Ollama 时遇到的问题

题意&#xff1a; ImportError: cannot import name Ollama from llama_index.llms (unknown location) - installing dependencies does not solve the problem Python 无法从 llama_index.llms 模块中导入名为 Ollama 的类或函数 问题背景&#xff1a; I want to learn LL…

车载测试之-CANoe创建仿真工程

在现代汽车工业中&#xff0c;车载测试是确保车辆电子系统可靠性和功能性的关键环节。而使用CANoe创建仿真工程&#xff0c;不仅能够模拟真实的车辆环境&#xff0c;还能大大提升测试效率和准确性。那么&#xff0c;CANoe是如何实现这些的呢&#xff1f; 车载测试中&#xff0…

2024年7月3日 (周三) 叶子游戏新闻

老板键工具来唤去: 它可以为常用程序自定义快捷键&#xff0c;实现一键唤起、一键隐藏的 Windows 工具&#xff0c;并且支持窗口动态绑定快捷键&#xff08;无需设置自动实现&#xff09;。 卸载工具 HiBitUninstaller: Windows上的软件卸载工具 《魅魔》新DLC《Elysian Fields…

Flink 窗口触发器(Trigger)(一)

Flink 窗口触发器(Trigger)(一) Flink 窗口触发器(Trigger)(二) Flink的窗口触发器&#xff08;Trigger&#xff09;是流处理中一个非常关键的概念&#xff0c;它定义了窗口何时被触发并决定触发后的行为&#xff08;如进行窗口数据的计算或清理&#xff09;。 一、基本概念 …

node与npm安装教程

node与npm的下载安装教程&#xff1a; 文章目录 node与npm的下载安装教程&#xff1a;---Node.js 介绍NPM 介绍 一&#xff1a;下载&#xff08;node与npm的安装包是在一起的&#xff09;二&#xff1a;安装1&#xff1a;双击运行安装文件1-node-v14.15.0-x64.msi,点击下一步。…

区块链加载解析方法

一.区块链加载解析 对于数据的下载主要包括三种方式&#xff1a; 1.实现比特币网络协议&#xff0c;通过该协议和其他比特币全节点建立联系&#xff0c;然后同步区块数据。 2.通过比特币节点提供的API服务下载区块链数据。 3.通过blickchain.com提供的rest服务下载区块数据…

windows 屏幕录制录屏;gif工具推荐;滑动截屏

1、gif工具推荐gif123 参考&#xff1a;https://gif123.aardio.com/ 很小&#xff0c;很简洁 2、滑动截屏 参考&#xff1a;https://shipinzan.com/ll-gd-jp.html 通过google、edge浏览器 3、windows 屏幕录制录屏 1&#xff09;WinG 2)笔记本 prtsc 按键 可以截图&…

ResNet50V2

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、ResNetV1和ResNetV2的区别 ResNetV2 和 ResNetV1 都是深度残差网络&#xff08;ResNet&#xff09;的变体&#xff0c;它们的主要区别在于残差块的设计和…

如何对低代码平台进行分类?

现在市面上的低代码平台就像雨后春笋一样冒出来&#xff0c;而且源源不绝&#xff0c;但总结下来&#xff0c;大致的也就以下三类。 一、 aPaaS多引擎类&#xff08;有很多成熟引擎、做好东西要一起用&#xff09; 这类产品包括&#xff1a;织信Informat&#xff08;国内&…

多模态图像生成的突破:Image Anything一种无需训练的智能框架

多模态图像生成是内容创作领域的热点技术&#xff0c;尤其在媒体、艺术和元宇宙等领域。该技术旨在模拟人类的想象力&#xff0c;将视觉、文本和音频等多种模态属性相关联&#xff0c;以生成图像。早期的方法主要侧重于单一模态输入的图像生成&#xff0c;例如基于图像、文本或…

C++部分复习笔记下

7. C11 范围for 使用格式 vector<int> v { 1,2,3,4,5 }; for (auto e : v) {cout << e << " "; } cout << endl;底层原理&#xff0c;使用迭代器 vector<int> v { 1,2,3,4,5 }; auto it v.begin(); while (it ! v.end()) {cout…

项目2:API Hunter 细节回顾 -1

一. 接口调用 对于开发者来说&#xff0c;接口的调用应当是方便快捷的&#xff0c;而且出于安全考虑&#xff0c;通常会选择在后端调用第三方 API&#xff0c;避免在前端暴露诸如密码的敏感信息。 若采用 HTTP 调用方式&#xff1a; HttpClientRestTemplate第三方库&#xf…

kaggle量化赛金牌方案(第七名解决方案)(下)

— 无特征工程的神经网络模型&#xff08;得分 5.34X&#xff09; 比赛进入最后阶段&#xff0c;现在是时候深入了解一些关于神经网络模型的见解了。由于 Kaggle 讨论区的需求&#xff0c;我在这里分享两个神经网络模型。第一个是 LSTM 模型&#xff0c;第二个是卷积网络&…

PyPDF2指定范围拆分PDF文件为单个页面

本文目录 前言一、指定范围拆分PDF1、过程讲解2、拆分效果图3、完整代码二、其他问题1、更改页码索引值前言 上一篇文章讲解了怎么讲一个PDF文档分割为多个单页面PDF,本文来讲解一下进阶,就是指定范围拆分PDF页面,有的时候,我们只想把PDF文档中的某几页拆分出来,而不是全…