内存映射文件原理

内存映射文件是通过在虚拟地址空间中预留一块区域,然后通过从磁盘中已存在的文件为其调度物理存储器,访问此虚拟内存空间就相当于访问此磁盘文件了。

内存映射文件实现过程

HANDLE hFile = CreateFile(...);                              //创建文件对象
HANDLE hFileMapping = CreateFileMapping(hFile, ...); //创建文件映射对象
MapViewOfFile(hFileMapping, ...); //在虚拟地址空间上建立映射 //其他操作 UnmapViewOfFile(hFileMapping); //撤销在虚拟地址空间上的映射
CloseHandle(hFileMapping); //关闭文件映射对象句柄
CloseHandle(hFile); //关闭文件对象句柄

以上代码就是建立内存映射文件的基本思路,注意在MapViewOfFile()在虚拟地址空间上建立映射的时候磁盘中的文件并没有加载到内存中,只有当访问此虚拟地址空间时会往实际物理内存中寻找对应数据,如果没有就发生缺页中断从而让磁盘中的文件加载到物理内存中。

内存映射文件实现进程间通讯

以磁盘中的文件为物理存储器的内存映射文件

通过在多个不同的进程中使用同一个文件映射对象在各自的进程空间中建立映射。

使用写时复制

所谓的写时复制是指,当在虚拟地址空间上建立文件映射后,如果对此地址空间进行写操作时,其不直接更改对应在物理内存中的数据,而是将对应物理内存中的内存页复制到虚拟内存的页交换文件中,然后修改页交换文件的内容。(也就是不改变磁盘中的文件)

//进程A
HANDLE hFile = CreateFile(...);
HANDLE hFileMapping = CreateFileMapping(hFile, , PAGE_WRITECOPY, , , TEXT("MapFile")); //PAGE_WRITECOPY写时复制标志
LPVOID lpAddress = MapViewOfFile(hFileMapping, , FILE_MAP_COPY, ...); //FILE_MAP_COPY写时复制标志 UnmapViewOfFile(hFileMapping);
CloseHandle(hFileMapping);
CloseHandle(hFile); //进程B
HANDLE hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, TEXT("MapFile"));
LPVOID lpAddress = MapViewOfFile(hFileMapping, , FILE_MAP_COPY, ...); UnmapViewOfFile(hFileMapping);
CloseHandle(hFileMapping);

上述代码就是设置了写时复制标志,当进程A和进程B修改自己进程空间对应映射地址的内存时,其实际是会修改对应的(A修改A的,B修改B的)页面交换文件的数据,并没有修改对应的物理内存中的数据所以不能实现进程间通讯。

不使用写时复制

不使用写时复制也就是不使用PAGE_WRITECOPY和FILE_MAP_COPY标志,对地址空间进行写操作时,其直接更改对应在物理内存中的数据。因为在不同进程中是通过同一文件映射对象建立的映射,所以其修改的是同一物理内存,可以实现进程间通讯。

//进程A
HANDLE hFile = CreateFile(...);
HANDLE hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, TEXT("MapFile"));
LPVOID lpAddress = MapViewOfFile(hFileMapping, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0); UnmapViewOfFile(hFileMapping);
CloseHandle(hFileMapping);
CloseHandle(hFile); //进程B
HANDLE hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, TEXT("MapFile")); //权限为FILE_MAP_ALL_ACCESS
LPVOID lpAddress = MapViewOfFile(hFileMapping, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0); UnmapViewOfFile(hFileMapping);
CloseHandle(hFileMapping);

上述代码在没有使用写时复制标志,所以当A/B进程修改自己进程中的文件映射时实际就是在修改同一块物理内存的数据,可以实现进程间通讯。

同时注意:上述代码是利用给文件映射对象句柄命名从而跨进程使用句柄的,还可以使用句柄继承和句柄复制来实现跨进程共享句柄。

以页交换文件为物理存储器的内存映射文件

因为以磁盘中已存在的文件作为物理存储器的内存映射文件在实现进程间通讯时是通过更改文件中的数据通信的,结果是会使更改的数据保存到文件中。为了避免更改文件,Windows实现了利用虚拟内存的页交换文件为物理存储器的内存映射文件实现进程间通讯,其通过更改页交换文件实现通信,所以不会更改任何磁盘文件。

//进程A
HANDLE hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024, TEXT("MapFile"));
LPVOID lpAddress = MapViewOfFile(hFileMapping, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0); UnmapViewOfFile(hFileMapping);
CloseHandle(hFileMapping); //进程B
HANDLE hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, TEXT("MapFile"));
LPVOID lpAddress = MapViewOfFile(hFileMapping, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0); UnmapViewOfFile(hFileMapping);
CloseHandle(hFileMapping);

注意:CreateFileMapping第一个参数为INVALID_HANDLE_VALUE,其指定的内存映射文件的大小需要时1024的倍数。

Windows进程间通讯(IPC)----内存映射文件的更多相关文章

  1. QSharedMemory共享内存实现进程间通讯(IPC)及禁止程序多开

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:QSharedMemory共享内存实现进程间通讯(IPC)及禁止程序多开     本文地址:h ...

  2. High Performance Networking in Google Chrome 进程间通讯(IPC) 多进程资源加载

    小结: 1. 小文件存储于一个文件中: 在内部,磁盘缓存(disk cache)实现了它自己的一组数据结构, 它们被存储在一个单独的缓存目录里.其中有索引文件(在浏览器启动时加载到内存中),数据文件( ...

  3. Android AIDL 进行进程间通讯(IPC)

    编写AIDL文件时,需要注意: 1.接口名和aidl文件名相同. 2.接口和方法前不用加访问权限修饰符 (public.private.protected等,也不能用final.static). 3. ...

  4. Windows进程间通讯(IPC)----共享内存

    Windows中同一个EXE文件多次加载过程 Windows中EXE文件加载是基于内存映射文件的. 当EXE文件第一次被加载. 首先系统会先创建一个进程内核对象,并创建一个新的进程地址空间. 系统调用 ...

  5. Windows进程间通讯(IPC)----消息队列

    消息队列 windows系统是通过消息驱动的,每移动一下鼠标,点击一下屏幕都会产生一个消息.这些消息会先被放在windows的一个系统消息队列(先进先出)中,windows系统会为每一个GUI线程创建 ...

  6. Windows进程间通讯(IPC)----WM_COPYDATA

    WM_COPYDATA通讯思路 通过向其他进程的窗口过程发送WM_COPYDATA消息可以实现进程间通讯. 只能通过SendMessage发送WM_COPYDATA消息,而不能通过PostMessag ...

  7. windows 进程间通讯方法

    Windows平台为我们提供了多种进程间通信的机制,主要包括:注册表方式.共享文件方式.共享内存方式.共享数据段.映射文件方式.管道方式. 剪贴板方式.消息方式.其中注册表方式需要增加注册表表项,而注 ...

  8. win32进程间通讯--共享内存

    小白一枚,如有不对,请各位大神多多指教! 最近看了看win32进程间通讯.简单写了写利用共享内存实现进程间通讯 使用共享内存实现进程间通讯: 1.在WM_CREATE消息下创建文件映射内核对象 hMa ...

  9. 服务 远程服务 AIDL 进程间通讯 IPC

    Activity aidl接口文件 package com.bqt.aidlservice;  interface IBinderInterface {     /* 更改文件后缀为[.aidl]去掉 ...

随机推荐

  1. 使用jhipster 加速java web开发

    jhipster,中文释义: Java 热爱者! JHipster is a development platform to quickly generate, develop, & depl ...

  2. PTA 求链式表的表长

    6-1 求链式表的表长 (10 分)   本题要求实现一个函数,求链式表的表长. 函数接口定义: int Length( List L ); 其中List结构定义如下: typedef struct ...

  3. [源码解析] 并行分布式框架 Celery 之架构 (2)

    [源码解析] 并行分布式框架 Celery 之架构 (2) 目录 [源码解析] 并行分布式框架 Celery 之架构 (2) 0x00 摘要 0x01 上文回顾 0x02 worker的思考 2.1 ...

  4. Java之常用API

    API概述 什么是API API (Application Programming Interface) :应用程序编程接口 java中的API 指的就是 JDK 中提供的各种功能的 Java类,这些 ...

  5. Android Studio 报错:你的主机中的软件中止了一个已建立的连接

    •解决方案 关闭电脑的移动热点 关闭后,build 就不会报错了. 等 build 好了后,重新打开移动热点,再次 build 一就不会报错.

  6. Python 实现自动化 Excel 报表

    Py 实现自动化Excel报表 好几个月没有写笔记了, 并非没有积累, 而是有点懒了. 想想还是要续上, 作为工作成长的一部分哦. 最近有做一些报表, 但一直找不到一个合适的报表工具, 又实在不想写前 ...

  7. 推荐一份Web 工程师的前端书单

    014年一月以来,自己接触web前端开发已经两年多了,记录一下自己前端学习路上看过的,以及道听途说的一些书,基本上按照由浅入深来介绍. JavaScript 入门 <JavaScript权威指南 ...

  8. EFCore3.1+编写自定义的EF.Functions扩展方法

    前言 本文主要是讲解EF Core3.0+ 如何实现自定义的数据库扩展函数 虽然EF.Functions 提供了很多数据库函数,但是并不全面.比如加密解密.. 这样的话 我们就需要自己扩展这些数据库函 ...

  9. Array.prototype.fill 填充值被复用的问题

    考察如下示例代码: // 创建二维数组 const arr = Array(2).fill([]); // 操作第一个元素 arr[0].push(1); // 结果是操作了所有数组 console. ...

  10. Kubernetes 学习笔记-- kafka往couchdb里倒东西

    首先吐槽下国内这些论坛的技术精神,不是我崇洋媚外,有些复读机烦不烦啊,别人的东西吃进去吐出来好玩么? 还有一些不懂装懂,这种最可恶,明明自己都不明白自己在写什么,还是往精华区发,简直离谱,知道自己多挣 ...