内存映射文件测试报告windows文档格式.docx
《内存映射文件测试报告windows文档格式.docx》由会员分享,可在线阅读,更多相关《内存映射文件测试报告windows文档格式.docx(13页珍藏版)》请在冰豆网上搜索。
![内存映射文件测试报告windows文档格式.docx](https://file1.bdocx.com/fileroot1/2023-1/7/b719512b-8a35-4e57-a3fa-7d8203c56639/b719512b-8a35-4e57-a3fa-7d8203c566391.gif)
通过内存映射文件函数可以将磁盘上的文件全部或者部分映射到进程的虚拟地址空间的某个位置。
一旦完成映射,对磁盘文件的访问就可以像访问内存文件一样便捷。
1.2内存映射文件优点
文件操作是应用程序最为基本的功能之一,Win32API和MFC均提供有支持文件处理的函数和类,常用的有Win32API的CreateFile()、WriteFile()、ReadFile()和MFC提供的CFile类等。
一般来说,以上这些函数可以满足大多数场合的要求,但是对于某些特殊应用领域所需要的动辄几十GB、几百GB、乃至几TB的海量存储,再以通常的文件处理方法进行处理显然是行不通的。
因此提出内存映射文件,它的优点为:
(1)系统使用内存映射文件,以便加载和执行.exe和DLL文件。
这可以大大节省页文件空间和应用程序启动运行所需的时间。
(2)可以使用内存映射文件来访问磁盘上的数据文件。
可以不必对文件执行I/O操作,并且可以不必对文件内容进行缓存。
(3)可以使用内存映射文件,使同一台计算机上运行的多个进程能够相互之间共享数据。
注:
使用内存映射文件数据传输是通过4k大小的数据页面实现的。
1.3内存映射文件原理简述
内存映射文件是将硬盘上文件的位置与进程逻辑地址空间中一块大小相同的区域之间的一一对应。
这种对应关系纯属是逻辑上的概念,物理上是不存在的,原因是进程的逻辑地址空间本身就是不存在的。
在内存映射的过程中,并没有实际的数据拷贝,文件没有被载入内存,只是逻辑上被放入了内存。
内存映射文件是把数据文件的一部分映射到虚拟地址空间(映射到的区域是在0x800000000~0xBFFFFFFF内),但不提交RAM,存取这段内存的指令同样会产生页面异常。
操作系统捕获到这个异常后,分配一页RAM,并把它映射到当前进程发生异常的地址处,然后系统把文件中相应的数据独到这个页面中,继续执行刚才产生异常的指令。
它使应用程序可以通过内存指针对磁盘上的文件进行访问,其过程就如同对加载了文件的内存的访问。
通过文件映射这种使磁盘文件的全部或部分内容与进程虚拟地址空间的某个区域建立映射关联的能力,可以直接对被映射的文件进行访问,而不必执行文件I/O操作也无需对文件内容进行缓冲处理。
内存文件映射的这种特性是非常适合于用来管理大尺寸文件的。
需要说明的是,在系统的正常的分页操作过程中,内存映射文件并非一成不变的,它将被定期更新。
如果系统要使用的页面目前正被某个内存映射文件所占用,系统将释放此页面,如果页面数据尚未保存,系统将在释放页面之前自动完成页面数据到磁盘的写入。
2内存映射文件函数(windows)
2.1内存映射文件操作流程
(1)调用CreatFile打开想要映射的文件,获得句柄hFile;
(2)调用CreatFileMapping函数生成一个建立在CreatFile函数创建的文件对象基础上的内存映射对象,得到句柄hFileMap;
(3)调用MapViewOfFile函数把整个文件的一个区域或者整个区域映射到内存中,得到指向映射到内存的第一个字节的指针lpMemory;
(4)用该指针来读写文件;
(5)调用UnmapViewOfFile来解除文件映射,传入参数为lpMemory;
(6)调用CloseHandle来关闭内存映射文件,传入参数为hFileMap;
(7)调用CloseHandle来关闭文件,传入函数为hFile。
2.2函数说明
2.2.1CreateFile
功能说明:
创建或打开一个文件内核对象,这个对象标识了磁盘上将要用作内存映射文件的文件。
2.2.2CreateFileMapping
创建一个有名的共享内存。
HANDLECreateFileMapping(
HANDLEhFile,
//映射文件的句柄,设为0xFFFFFFFF以创建一个进程间共享的对象
LPSECURITY_ATTRIBUTESlpFileMappingAttributes,
//安全属性
DWORDflProtect,
//保护方式
DWORDdwMaximumSizeHigh,
//对象的大小
DWORDdwMaximumSizeLow,
LPCTSTRlpName
//必须为映射文件命名
);
2.2.3MappViewOfFile
在创建文件映射对象后使用可以调用MapViewOfFile函数映射到本进程的地址空间内。
LPVOID
MapViewOfFile(HANDLE
hFileMappingObject,
DWORD
dwDesiredAccess,
dwFileOffsetHigh,
dwFileOffsetLow,
dwNumberOfBytesToMap
参数说明:
dwDesiredAccess:
用来指定我们想对文件进行的操作,如:
FILE_MAP_READ等。
dwFileOffsetHigh和dwFileOffsetLow:
用来指定打开文件中欲映射的起始偏移位置(映射整个的文件,可指定它们的值为0)。
dwNumberOfBytesToMap:
用来指定欲映射的字节数(映射整个的文件,设定该值为0)。
MapViewOfFile()函数允许全部或部分映射文件,在映射时,需要指定数据文件的偏移地址以及待映射的长度。
其中,文件的偏移地址由DWORD型的参数dwFileOffsetHigh和dwFileOffsetLow组成的64位值来指定,而且必须是操作系统的分配粒度的整数倍,对于Windows操作系统,分配粒度固定为64KB。
当然,也可以通过如下代码来动态获取当前操作系统的分配粒度:
3测试用例及结果(windows)
说明:
以下测试环境为windowsxp系统,2G内存,个人使用pc机。
3.1用例1
3.1.1测试目的及流程
测试目的:
对于使用fopen,fread,fclose操作文件,与内存映射文件方法,两者效率比较。
流程一:
(1)使用fopen打开文件;
(2)使用fread读取文件;
(3)使用fclose关闭文件。
流程二:
(1)使用CreateFile,CreateFileMapping创建文件对象和文件映射对象;
(2)使用MapViewOfFile将文件映射到进程地址空间(整个文件都映射到内存);
(3)使用MapViewOfFile函数返回的内存指针,访问数据。
(4)释放内存映射对象,关闭以上操作产生的句柄。
3.1.2测试结果
(1)循环打开800个大小为10m的文件
流程
第一次
第二次
第三次
第四次
流程一
5047
313
312
320
流程二
375
359
(2)循环打开40000个大小为1k的文件
69625
32171
22515
21656
24157
22719
26016
27969
(3)按照“流程一”操作文件,第一次运行耗时较长,以后的操作耗时和内存映射文件耗时相差不多。
可能是系统文件缓存原因。
3.2用例2
3.2.1测试目的及流程
比较fopen,fread,fclose操作文件,与内存映射文件方法,在打开文件后,多次读取文件的速度。
(4)使用fopen打开文件;
(5)多次使用fread读取文件;
(6)使用fclose关闭文件。
(5)使用CreateFile,CreateFileMapping创建文件对象和文件映射对象;
(6)使用MapViewOfFile将文件映射到进程地址空间(整个文件都映射到内存);
(7)重复使用MapViewOfFile函数返回的内存指针,访问数据。
(8)释放内存映射对象,关闭以上操作产生的句柄。
3.2.2测试结果
(1)循环打开100个不同文件,每打开一次文件,读取文件内容操作1000次:
流程一时间约为:
650ms
流程二时间约为:
65ms
(2)循环打开100个不同文件,每打开一次文件,读取文件内容操作2000次:
1265ms
60ms
3.3用例3
3.3.1测试目的及流程
测试内存映射文件最大同时打开文件的总大小。
测试流程:
(1)使用CreateFile,CreateFileMapping创建文件对象和文件映射对象;
(2)使用MapViewOfFile将文件映射到进程地址空间(整个文件都映射到内存);
(3)重复以上操作打开不同文件,直到MapViewOfFile返回错误。
(4)关闭以上操作产生的句柄,释放文件映射对象。
3.3.2测试结果
文件夹名称
文件夹中文件个数
内存映射成功文件个数
内存映射成功文件总大小
test-5m
420
405
2025m
test-10m
410
201
2010m
3.4用例4
3.4.1测试目的及流程
(1)测试是否可以创建多个文件映射内核句柄,但不进行文件映射到内存操作;
(2)轮循上一步得到的不同文件映射内核句柄,将对应的文件映射到内存。
(1)使用CreateFile,CreateFileMapping创建多个文件对象和文件映射对象,并将其保存到std:
:
map中;
(2)使用std:
map中保存的句柄,调用MapViewOfFile将文件映射到进程地址空间;
(3)释放步骤
(2)产生的文件映射对象;
(4)重复步骤
(2),步骤(3);
(5)关闭std:
map中保存的句柄。
3.4.2测试结果
产生多个文件映射句柄,并保存。
可依据需要,对某个文件映射内核句柄进行MapViewOfFile文件映射到内存操作。
释放文件映射对象后,重新映射其他文件映射句柄。
避免进程地址空间的大小限制。
3.5用例5
3.5.1测试目的及流程
测试windows(64位)下,内存映射大小。
测试环境:
机器:
192.168.75.238
内存:
64GB
系统:
windowsserver2003(64位)
3.5.2测试结果
测试结果:
测试文件夹文件个数:
共2047个文件(47个1g大小的文件2000个5m大小文件)
全部映射成功,共映射约58128m(约56g)文件,未发生错误。
4其他说明
(1)因进程地址空间大小有限制,所以内存映射文件也同样有限制,可通过调用MapViewOfFile时,指定映射的起始偏移位置,来映射部分文件到内存。
(2)测试未调查linux下文件映射的资料。
(3)因个人水平,可能存在理解偏差或代码编写有误,会不断改进。
5附录(代码)
5.1创建内存映射文件代码
//1.创建一个“文件”内核对象
HANDLEhFile=CreateFileA(file_path,GENERIC_READ|GENERIC_WRITE,
0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(hFile==INVALID_HANDLE_VALUE)
{
printf("
test_file_mapping|CreateFileAfailed(GetLastError=%d)...\n"
GetLastError());
return-1;
}
//2.打开一个“文件映射”内核对象
HANDLEhFileMap=OpenFileMapping(FILE_MAP_READ|FILE_MAP_WRITE,
false,NULL);
if(hFileMap==NULL)
//如果对象不存在,创建一个“文件映射”内核对象
DWORDdwFileSize=0;
hFileMap=CreateFileMapping(hFile,NULL,PAGE_READWRITE,0,
0,NULL);
if(hFileMap==NULL)
{
printf("
test_file_mapping|CreateFileMappingfailed(GetLastError=%d)...\n"
GetLastError());
return-1;
}
//3.将数据映射到进程地址空间
PVOIDpvFileView=MapViewOfFile(hFileMap,FILE_MAP_READ,0,0,0);
if(pvFileView==NULL)
test_file_mapping|MapViewOfFilefailed(GetLastError=%d)...\n"
//4.访问或修改数据
char*pszVal=(char*)pvFileView;
//5.撤销文件内核对象
UnmapViewOfFile(pvFileView);
//6.关闭文件内核对象和文件映射内核对象
CloseHandle(hFileMap);
CloseHandle(hFile);
5.2测试用例3代码
#include<
stdio.h>
#include"
iFly_Assert.h"
utils/sutils.h"
map>
iostream>
sp:
str_arrres_file;
unsignedinttick_count();
constchar*get_res_file_name(intcursor);
unsignedinttick_count()
return:
GetTickCount();
constchar*get_res_file_name(intcursor)
returnres_file[cursor%res_file.size()].c_str();
intmain(intargc,char*argv[])
intret=0;
SP_ASSERT(argc==3);
constchar*uri=argv[1];
intloop_count=atoi(argv[2]);
//getresourcefilelist
sp:
get_file_list(uri,res_file,"
*"
false);
constchar*file_name="
"
;
unsignedinttick_start;
unsignedinttick_end;
inticount=0;
std:
map<
HANDLE,HANDLE>
mapping_map;
unsignedlonglongtotal_size=0;
doublesize_m=0;
unsignedintf_size=0;
HANDLEhMapFile=NULL;
HANDLEhFile=NULL;
for(inti=0;
i<
loop_count;
i++)
icount=i;
file_name=get_res_file_name(i);
hFile=CreateFileA(file_name,GENERIC_READ|GENERIC_WRITE,
0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(hFile==INVALID_HANDLE_VALUE)
{
printf("
CreateFileAfailed(GetLastError=%d)...\n"
GetLastError());
break;
}
hMapFile=CreateFileMapping(hFile,NULL,PAGE_READWRITE,0,
0,NULL);
if(hMapFile==NULL)
CreateFileMappingfailed,filename=%s,GetLastError=%d\n"
file_name,GetLastError());
mapping_map.insert(std:
pair<
HANDLE,HANDLE>
(hFile,hMapFile));
f_size=sp:
get_file_size(file_name);
total_size+=f_size;
filenum=%d,filesize=%d\n"
(icount+1),
f_size);
size_m=(double)total_size/(1024*1024);
totalfilesize=%llu(%f)\n"
total_size,size_m);
iteratoriter1=mapping_map.begin();
for(;
iter1!
=mapping_map.end();
iter1++)
HANDLEhFile_m=iter1->
first;
HANDLEhMappingFile_m=iter1->
second;
PVOIDp_file_view=MapViewOfFile(hMappingFile_m,FILE_MAP_READ,0,0,0);
if(p_file_view==NULL)
MapViewOfFilefailed,filename=%s,GetLastError=%d\n"
return-1;
char*pszVal=(char*)p_file_view;
UnmapViewOfFile(p_file_view);
charc[100];
close...?
\n"
gets(c);
iteratoriter2=mapping_map.begin();
iter2!
iter2++)
HANDLEhFile_m=iter2->
HANDLEhMappingFile_m=iter2->
CloseHandle(hMappingFile_m);
CloseHandle(hFile_m);
mapping_map.clear();
system("
pause"
return0;