c++内存映射文件

news2024/11/16 3:26:46

概念

将一个文件直接映射到进程的进程空间中(“映射”就是建立一种对应关系,这里指硬盘上文件的位置与进程逻辑地址空间中一块相同区域之间一 一对应,这种关系纯属是逻辑上的概念,物理上是不存在的),这样可以通过内存指针用读写内存的办法直接存取文件内容。

特点

文件数据可以用内存读/写指令来访问,而不是用Read和Write这样的I/O系统函数,从而提高了文件存取速度。

流程

  • 打开文件,创建文件句柄;
  • 为文件创建内存映射内核对象,返回内存映射文件句柄;
  • 映射整个文件或一部分到进程的虚拟地址空间,返回文件映射到内存后的起始地址;
  • 解除文件映射;
  • 关闭内存映射文件句柄;
  • 关闭文件句柄;

函数

1)创建文件句柄。

  • windows
# 函数
HANDLE CreateFile(LPCTSTR lpFileName,           //普通文件名或者设备文件名
	DWORD dwDesiredAccess,                      //访问模式(写/读)
	DWORD dwShareMode,                          //共享模式
	LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全属性的指针
	DWORD dwCreationDisposition,                //如何创建
	DWORD dwFlagsAndAttributes,                 //文件属性
	HANDLE hTemplateFile                        //用于复制文件句柄
);
  • linux
# 函数
int open(const char *pathname, int flags, mode_t mode);

2)创建内存映射内核对象。

  • windows
# 函数
HANDLE CreateFileMapping(
  HANDLE hFile,                                      // 文件句柄,填写 INVALID_HANDLE_VALUE
  LPSECURITY_ATTRIBUTES lpFileMappingAttributes,     // 安全描述符,填写 NULL             
  DWORD flProtect,                                   // 映射对象保护属性
  DWORD dwMaximumSizeHigh,                           // 文件映射的最大长度的高32位
  DWORD dwMaximumSizeLow,                            // 文件映射的最大长度的低32位
  LPCTSTR lpName                                     // 文件映射对象名称
);
  • linux
# 函数
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);

# 参数说明
## start:映射区的开始地址
## length:映射区的长度
## prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起
## flags:指定映射对象的类型,映射选项和映射页是否可以共享。
## fd:有效的文件描述词。如果MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1
## offset:被映射对象内容的起点。

3)映射文件到进程的虚拟地址空间。

  • windows
# 函数
LPVOID MapViewOfFile(
  HANDLE hFileMappingObject,  // CreateFileMapping()返回的文件映像对象句柄
  DWORD dwDesiredAccess,      // 数据的访问方式
  DWORD dwFileOffsetHigh,     // 文件映射起始偏移的高32位
  DWORD dwFileOffsetLow,      // 文件映射起始偏移的低32位
  DWORD dwNumberOfBytesToMap  // 文件中要映射的字节数,为0表示映射整个文件映射对象
);

4)在接收进程中打开对应的内存映射对象。

# 函数
HANDLE OpenFileMapping(
  DWORD dwDesiredAccess,  // 数据的访问方式
  BOOL bInheritHandle,    // 是否继承句柄
  LPCTSTR lpName          // 要打开的文件映射对象名称
);

5)回写内存映射文件。

  • windows
# 函数
BOOL FlushViewOfFile(
	LPCVOID lpBaseAddress, // 开始的地址
	SIZE_T dwNumberOfBytesToFlush // 数据块的大小
);
  • linux
#include <sys/mman.h>

# 函数
int msync(void *addr, size_t length, int flags);

# 参数说明
## 内存段需要修改的部分作为参数传递过来的起始地址addr和长度len确定。
## flags 参数控制着执行修改的具体方式:
#### MS_ASYNC      采用异步写方式
#### MS_SYNC       采用同步写方式
#### MS_INVALIDATE 从文件中读回数据

6)解除文件映射。

  • windows
# 函数
BOOL UnmapViewOfFile(LPCVOID lpBaseAddress);

# 参数说明
## lpBaseAddress: 指向要取消映射的文件的映射视图基址的指针。此值必须与上一次调用 MapViewOfFile或 MapViewOfFileEx 函数返回的值相同。
  • linux
# 函数
int munmap(void * addr, size_t len);

# 参数说明
## addr:映射内存起始地址
## len: 欲取消的内存大小

# 返回值
执行成功时,munmap()返回0。失败时,munmap返回-1。

示例

  • windows实例
#include <iostream>
#include <stdio.h>
#include <windows.h>
#include <chrono>
#include <ctime>

using namespace std;

int main()
{
	// MyData为测试文件,大小为1G
	// linux可执行"truncate -s 1G MyData"命令生成大文件
	uint64_t start = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
	// 创建文件
	HANDLE hFile = CreateFile(L"D://MyData",
		GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);
	if (INVALID_HANDLE_VALUE == hFile)
	{
		cout << " CreateFile fail" << endl;
		return -1;
	}

	// 创建一个文件映射内核对象
	HANDLE hFileMapping = CreateFileMapping(hFile,
		nullptr,
		PAGE_READWRITE,
		0,
		0,
		nullptr);
	if (nullptr == hFileMapping)
	{
		cout << "CreateFileMapping fail" << endl;
		CloseHandle(hFile);
		return -1;
	}

	// 将文件数据映射到进程的地址空间
	char* mapData = (char*)MapViewOfFile(hFileMapping,
		FILE_MAP_ALL_ACCESS,
		0,
		0,
		0);
	if (nullptr == mapData)
	{
		cout << "MapViewOfFile fail" << endl;
		CloseHandle(hFileMapping);
		CloseHandle(hFile);
		return -1;
	}

	// 数据拷贝
	char* myBuf = mapData;

	// 计算时间
	uint64_t end = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); 
	uint64_t duration = end - start;   // 微妙差
	cout << "duration:" << duration << endl;

	UnmapViewOfFile(myBuf);
	CloseHandle(hFileMapping);
	CloseHandle(hFile);

    system("pause");
    return 0;
}

执行结果:

1G的文件完成文件映射需要143微妙。

在这里插入图片描述

  • linux实例
#include <iostream>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <chrono>
#include <ctime>
using namespace std;

#define MY_FILE_PATH "./MyData"

size_t get_file_size(const char *file_path) 
{
    struct stat buf;
    if (stat(file_path, &buf) < 0) 
	{
        printf("%s[%d]:%s", __FUNCTION__, __LINE__, strerror(errno));
        return -1;
    }
    return buf.st_size;
}

int main()
{
    // 执行"truncate -s 1G MyData"命令生成大文件
	uint64_t start = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
	
    // 打开文件
	int fd = open(MY_FILE_PATH, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
    if (-1 == fd) 
	{
        cout << "open failed,error:" << strerror(errno) << endl;;
        return -1;
    }

    // 获取文件大小
    size_t filelen = get_file_size(MY_FILE_PATH);
    if (-1 == (int)filelen) 
	{
        cout << "get file size failed" << endl;
        return -1;
    }
    cout << "filesize = " << filelen << endl;
 
    // 开始映射
    void* result = mmap(0, filelen, 
                PROT_READ|PROT_WRITE, 
                MAP_SHARED, 
                fd, 0);
    if (result == (void *)-1) 
	{
        cout << "mmap failed,error:" << strerror(errno) << endl;
        return -1;
    }
	
	// 计算时间
	uint64_t end = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
	uint64_t duration = end - start;   // 微妙差
	cout << "duration:" << duration << endl; 
	
    // 取消文件映射
    munmap(result, filelen);
	
	// 关闭文件句柄
    close(fd);

	return 0;
}

执行结果:

# 1G文件完成映射耗时270微妙
[root@localhost debug.x64-linux]# ./testFileMapping
filesize = 1073741824
duration:270
[root@localhost debug.x64-linux]# 

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

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

相关文章

Web-登录功能实现(含JWT令牌)

登录功能 这个登陆功能先不返回JWT令牌 登陆会返回JWT令牌 一会在登陆验证时讲解JWT令牌&#xff08;返回的data就是它&#xff09; 登录校验 概述 就是你比如复制一个url 用一个未曾登陆对应url系统的浏览器访问 他会先进入登陆页面 登陆校验就是实现这个功能 简而言之…

基于EasyExcel的单元格合并自定义算法处理

基于EasyExcel导出Excel后&#xff0c;通过对合并单元格的简单规则配置&#xff0c;实现如下图所示的单元格合并效果&#xff1a; 效果截图 原表格数据如下&#xff1a; 通过配置单元格合并规则后&#xff0c;生成的合并后的表格如下&#xff1a; 注&#xff1a;其中第三列&a…

Android Studio连接安卓手机

1. 创建项目 2. 下载Google USB Driver 点击右上角红框的【SDK Manager】->【SDK Tools】。 也可以在 【tools】->【SDK Manager】->【SDK Tools】下进入。 点击Google USB Driver&#xff0c;下载后点ok。 3. 环境变量 右键【我的电脑】->【高级系统设置】-&g…

基于微信小程序的高校新生自助报道系统设计与实现(Java+spring boot+MySQL+小程序)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于微信小程序的高校新生自助报道系统设计与实现&#xff08;Javaspring bootMySQL微信小程序&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1…

123、仿真-基于51单片机的电流控制仿真系统设计(Proteus仿真+程序+原理图+参考论文+配套资料等)

方案选择 单片机的选择 方案一&#xff1a;STM32系列单片机控制&#xff0c;该型号单片机为LQFP44封装&#xff0c;内部资源足够用于本次设计。STM32F103系列芯片最高工作频率可达72MHZ&#xff0c;在存储器的01等等待周期仿真时可达到1.25Mip/MHZ(Dhrystone2.1)。内部128k字节…

java报错- 类文件具有错误的版本 61.0, 应为 52.0 请删除该文件或确保该文件位于正确的类路径子目录中。

SpringBoot使用了3.0或者3.0以上&#xff0c;因为Spring官方发布从Spring6以及SprinBoot3.0开始最低支持JDK17&#xff0c;所以仅需将SpringBoot版本降低为3.0以下即可。

ES6类-继承-Symbol-模版字符串

目录 类 继承 ES5 如何继承 ES6继承 Symbol 用途 可以产生唯一的值&#xff0c;独一无二的值 解决命名冲突 getOwnPropertySymbols() 作为全局注册表 缓存 Symbol.for() 消除魔术字符串 模版字符串 类 在javascript语言中&#xff0c;生成实例对象使用构造函数&#xf…

数据库基本操作-----数据库用户管理和授权

目录 一、数据库用户管理 1&#xff0e;新建用户 2&#xff0e;查看用户信息 3&#xff0e;重命名用户 4&#xff0e;删除用户 ​编辑5&#xff0e;修改当前登录用户密码 6&#xff0e;修改其他用户密码 7&#xff0e;忘记 root 密码的解决办法 &#xff08;1&#xff09;修…

Redis数据类型 — List

List 列表是简单的字符串列表&#xff0c;按照插入顺序排序&#xff0c;可以从头部或尾部向 List 列表添加元素。 List内部实现 List 类型的底层数据结构是由双向链表或压缩列表实现的&#xff1a; 如果列表的元素个数小于 512 个&#xff08;默认值&#xff0c;可由 list-m…

详解Single-Shot Alignment Network (S2A-Net) 基于遥感图像的特征对齐旋转目标检测

引言 目标检测&#xff1a;把图像中的物体使用方框标记起来&#xff0c;不同类别物体应使用不同颜色 目标检测其实是寻找物体边界框(bounding box)回归问题(regression)和对物体分类问题(classification)的统一 遥感目标检测&#xff1a;普通的目标检测是日常生活中的横向的图…

2.4 线性表的插入删除

1. 链表的插入删除 1. 单链表插入删除 图1. 单链表插入结点 图2. 单链表删除结点 #include <iostream>typedef struct LNode {int data;struct LNode* next; }LNode;/// <summary> /// 判断链表是否非空 /// </summary> /// <param name"p">…

常见关于数组的函数的介绍

关于字符串函数的介绍 求字符串长度 strlen函数 用于计算字符串的长度的函数&#xff0c;需要使用的库函数是string.h 函数声明 size_t strlen(const char *str)函数模拟实现 #include<stdio.h> #include<assert.h> size_t my_strlen(const char* arr) {asse…

review回文子串

给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是 回文串 。返回 s 所有可能的分割方案。 回文串 是正着读和反着读都一样的字符串。 class Solution {List<List<String>> lists new ArrayList<>(); // 用于存储所有可能…

阿里瓴羊One推出背后,零售企业迎数字化新解

配图来自Canva可画 近年来随着数字经济的高速发展&#xff0c;各式各样的SaaS应用服务更是层出不穷&#xff0c;但本质上SaaS大多局限于单一业务流层面&#xff0c;对用户核心关切的增长问题等则没有提供更好的解法。在SaaS赛道日渐拥挤、企业增长焦虑愈演愈烈之下&#xff0c…

Midjourney助力交互设计师设计网站主页

Midjourney的一大核心优势是提供创意设计&#xff0c;这个功能也可以用在网站主页设计上&#xff0c;使用Midjourney prompt 应尽量简单&#xff0c;只需要以"web design for..." or "modern web design for..."开头即可 比如设计一个通用SAAS服务的初创企…

单片机第一季:零基础5——LED点阵

1&#xff0c;第八章-LED点阵 如何驱动LED点阵&#xff1a; (1)单片机端口直接驱动。要驱动8*8的点阵需要2个IO端口&#xff08;16个IO口&#xff09;、要驱动16*16的点阵需要4个IO端口&#xff08;32个IO口&#xff09;。 (2)使用串转并移位锁存器驱动。要驱动16*16点阵只需要…

Linux 系统编程-开发环境(二)

目录 7 压缩包管理 7.1 tar 7.2 rar 7.3 zip 8 进程管理 8.1 who 8.2 ps 8.3 jobs 8.4 fg 8.5 bg 8.6 kill 8.7 env 8.8 top 9 用户管理 9.1 创建用户 9.2 设置用户组 9.3 设置密码 9.4 切换用户 9.5 root用户 9.6 删除用户 10 网络管理 10.1 i…

MySQL-分库分表详解(七)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xf…

【熬夜送书 | 第二期】清华社赞助 | 《前端系列丛书》

前端是什么? 前端&#xff08;Front-End&#xff09;&#xff0c;又称浏览器端、客户端等&#xff0c;是指 Web 应用程序中负责用户交互、界面展示和数据展示的部分。前端技术体系主要包括 HTML、CSS 和 JavaScript 等内容。 其中&#xff0c;HTML&#xff08;Hypertext Mar…

❀如何获得铁粉❀

文章目录 引言一、提供独特的价值1.1 分享专业知识和经验1.2 提供独特的产品或服务1.3 展示个人风格和个性 二、构建真实的关系2.1 回应评论和互动2.2 分享个人故事和经历2.3 建立信任和互信关系 三、提供独家福利3.1 提供折扣和促销3.2 推出限量版产品或服务3.3 独家活动和会员…