c++内存映射文件

概念

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

特点

文件数据可以用内存读/写指令来访问,而不是用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]# 
  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用 C++ 实现的内存映射文件的代码示例: ```c++ #include <iostream> #include <fstream> #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main() { int fd = open("example.txt", O_RDWR); if (fd == -1) { std::cerr << "Failed to open file." << std::endl; return 1; } struct stat sb; if (fstat(fd, &sb) == -1) { std::cerr << "Failed to stat file." << std::endl; close(fd); return 1; } void* addr = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (addr == MAP_FAILED) { std::cerr << "Failed to mmap file." << std::endl; close(fd); return 1; } std::cout << "File content: " << std::endl; std::cout << static_cast<char*>(addr) << std::endl; // 修改文件内容 char* content = static_cast<char*>(addr); content[0] = 'H'; // 刷新内存映射 if (msync(addr, sb.st_size, MS_SYNC) == -1) { std::cerr << "Failed to msync." << std::endl; munmap(addr, sb.st_size); close(fd); return 1; } munmap(addr, sb.st_size); close(fd); return 0; } ``` 在上面的代码中,首先使用 `open()` 函数打开文件,并获取文件描述符。然后使用 `fstat()` 函数获取文件的信息,包括文件大小。接着使用 `mmap()` 函数将文件映射到进程地址空间中,并返回映射的起始地址。在访问文件内容时,可以直接使用映射的地址。在修改完文件内容后,需要通过 `msync()` 函数刷新内存映射,将修改结果写入磁盘。最后,需要使用 `munmap()` 函数关闭内存映射,并使用 `close()` 函数关闭文件描述符。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值