概念
将一个文件直接映射到进程的进程空间中(“映射”就是建立一种对应关系,这里指硬盘上文件的位置与进程逻辑地址空间中一块相同区域之间一 一对应,这种关系纯属是逻辑上的概念,物理上是不存在的),这样可以通过内存指针用读写内存的办法直接存取文件内容。
特点
文件数据可以用内存读/写指令来访问,而不是用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_VALUELPSECURITY_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]#