C17、内存映射文件

来源:互联网 发布:真无线耳机 推荐 知乎 编辑:程序博客网 时间:2024/06/01 10:18

一、使用目的:

n         系统加载执行.exe DLL文件(节省页文件空间,快速启动);

n         访问磁盘文件而不必进行I/O操作和缓存;

n         进程间共享数据(进程间通讯最有效的方法)。

 

二、默认情况下,全局数据和静态数据不能被同一个.exe DLL文件的多个映像共享。但可以改变(但不鼓励使用)。改变的方法:

n         每个.exe DLL文件的映像都由许多节组成,如:.text, .bss, .data等。 使用DumpBin /Headers myPrg.exe可以查看.exeDLL映射文件中各个节的列表。

n         可以创建自己的节,eg// 创建名为mynode的节,包含单个LONG值:

#pragma data_seg(“mynode”)

LONG g_lMyData = 0;

#pragma data_seg()

n         如果想共享这个节,可以在链接程序使用开关:/SECTIONeg

#pragma comment(linker, “/SECTION:mynode,RWS”)

       其中:RWS表示Read-Write-Share属性。

 

三、使用内存映射文件:

1.         创建/打开一个文件内核对象(用于标识做映射的文件);

HANDLE CreateFile(

PCTSTR pszFileName,      // 文件名

DWORD dwDesiredAccess,   //0-用于获取文件属性;GENERIC_READ,WRITE

DWORD dwShareMode, // FILE_SHARE_READ, WRITE

PSECURITY_ATTRIBUTES psa, // NULL 默认安全属性

DWORD dwCreationDisposition,

DWORD dwFlagsAndAttributes,

HANDLE hTemplateFile);

u       成功返回文件内核对象句柄;失败返回INVALID_HANDLE_VALUE

2.         创建一个文件映射内核对象(可以设定文件大小、访问权限);

HANDLE CreateFileMapping(

HANDLE hFile, // CreateFile返回的文件句柄

PSECURITY_ATTRIBUTES psa, // NULL 默认安全属性

DWORD fdwProtect, // 一般保护属性:PAGE_READONLY, READWRITE,WRITECOPY以及节保护属性:SEC_NOCACHE(无高速缓存,设备驱动程序使用),_IMAGE(可移植的可执行文件),_RESERVE,_COMMIT(二者互斥,不用于内存映射数据文件)

DWORD dwMaximumSizeHigh, // 文件最大字节数(64位)的 32

DWORD dwMaximumSizeLow, //  32位。如果等于文件大小,=0

PCTSTR pszName);// 进程间共享名,可以=NULL

u       成功:返回对象句柄;失败:NULL

3.         映射全部/部分到进程地址空间。

PVOID MapViewOfFile(

HANDLE hFileMappingObject, // CreateFileMapping返回的句柄

DWORD dwDesiredAccess, // FILE_MAP_WRITE, _READ, _ALL_ACCESS, _COPY (Win98必须)

DWORD dwFileOffsetHigh, // 64位文件偏移位置的 32

DWORD dwFileOffsetLow, // 32位;必须为分配粒度的倍数(64K

SIZE_T dwNumberOfBytesToMap); // 映射的字节数,=0将映射的文件尾为止

u       成功:返回地址空间的首地址,失败:NULL

u       如果用FILE_MAP_COPY,系统马上提交页文件(但不一定使用);一旦有写入,系统马上将这个有操作的页面的原始数据拷贝到页文件的相应页面,你只写入到该拷贝中(原始文件无变化)

4.         撤销映像;

BOOL UnmapViewOfFile(PVOID pvBaseAddress); // MapViewOfFile返回的首地址。

 

因为系统的高速缓存,需强制系统将修改过的数据的一部分或全部重新写入磁盘

BOOL FlushViewOfFile(

PVOID pvAddress,        // 映射的首地址,函数自动将其圆整为页面边界值

SIZE_T dwNumberofBytesToFlush)// 刷新的字节数

u       但,网络服务器因为高速缓存,并不保证写入远程磁盘。若要保证服务器写入,在CreateFile中传递FILE_FLAG_WRITE_THROUGH,并使用FlushViewOfFile

u       如果使用FILE_MAP_COPY映射视图,你对文件数据的任何修改实际上是对页文件中的拷贝所作的修改,FlushViewOfFile不会刷新磁盘文件。若要保留修改,可以用同一个文件创建另一个映射(使用PAGE_READWRITE),然后用FILE_MAP_WRITE映射到地址空间。扫描第一个视图寻找带有PAGE_READWRITE保护属性的页面(在写入操作时,系统将页面的保护属性从PAGE_WRITECOPY改为PAGE_READWRITE),调用MoveMemory将数据页面从第一个视图拷贝到第二个视图,FlushViewOfFile保存第二个视图。

5.         关闭文件映射内核对象:CloseHandle(hFileMapping);

早期关闭内核对象:如果不用创建多个文件映射对象或映射多个视图,可以在调用MapViewOfFile后立即关闭文件对象和文件映射内核对象。

6.         关闭文件(内核)对象:CloeseHandle(hFile)

 

四、文件倒序实例程序(FileRev):

u       文本文件的结尾无0结束符,而_strrev_wcsrev)函数要求以0结尾的字符串。在文件的结尾添加0

a)         CreateFileMapping(…… dwFileSize+ sizeof(WCHAR), NULL; // 映射的字节数是文件长+一个宽字符的大小

b)        输入文本结尾字符=0

c)        文件转换完成后,使用SetFilePointerSetEndOfFile重新设置文件结尾位置=原文件大小)。

d)        因为使用了PAGE_READWRITE属性,所有不用FlushViewOfFile

u       文本的换行符"/r/n",使用_strrev后转换成了"/n/r",所有要转回来。遍历所有字符,查找’/n’将连续的两个字符换成"/r/n"。(UNICODE L’/n’)。

 

五、超大文件的操作注意事项:

u       __int64 qwFileSize =  GetFileSize分别返回高32位和低32位值移位加;

u       按粒度大小进行映射视图时的地址偏移;

u       测试内存分配粒度,按粒度大小分别映射视图;文件尾部少于粒度大小,则按实际大小映射视图

 

一、内存映射文件与数据视图的相关性:只要是映射相同的文件映射对象,系统就会确保映射视图数据的相关性(在多线程中也一样)。

 

 

二、可以设定内存映射文件的基地址,使用MapViewOfFileEx

u       一般用于与其他进程共享数据时。如果内存映射文件使用了链表结构,不使用相同的基地址,遍历时会出错!不过使用Mircrosoft的编译器__based关键字也能解决,但不建议使用。

u       他比MapViewOfFile多一个pvBaseAddress参数,是你的目标地址(在WIN2000下应该是分配粒度边界64KB的倍数,否则出错,GetLastError返回1132ERROR_MAPPED_ALIGNMENTWIN98系统自动圆整)。如果系统无法映射到该位置(文件太大,且与另一个保留的地址重叠),也出错(返回NULL)。如果使用pvBaseAddress = NULL,则与MapViewOfFile运行结果完全相同。

 

三、页文件支持的内存映射文件(非磁盘文件支持):

u       不需要CreateFile返回文件句柄;

u       调用CreateFileMapping,用INVALID_HANDLE_VALUE作为hFile的参数;

u       如果进程间共享,调用CreateFileMappingOpenFileMapping,用相同的字符串作为pszName参数;

u       使用结束后,CloseHandle

u       所以,如果在打开文件失败时未检查返回值,系统将创建的是“页文件支持的内存映射文件”。

 

 

四、稀疏提交的内存映射文件(要求NT文件系统):

u       调用CreateFileMappingfdwProtect使用SEC_RESERVESEC_COMMIT标志创建页文件支持的内存映射文件(hFile = INVALID_HANDLE_VALUE

u       CreateFileMappingSEC_RESERVE时,系统并不从页文件中提交物理存储器,只返回文件映射对象的句柄,MapViewOfFileEx)只保留地址空间区域,也不提交物理存储器,(这时,任何访问都会违例!);

u       调用VirtualAlloc提交物理存储器(pvAddress = MapViewOfFile返回地址)。(虽然,WIN98下,VirtualAlloc要求内存地址位于0x004000000x7FFFFFFF,内存映射文件的地址位于0x800000000xBFFFFFFF,但不会产生错误);

u       WIN2000下,不能用VirtualFree释放使用SEC_RESERVE保留的存储器;但是,WIN98允许;

u       NT文件系统(NTFS 5)提供了对稀疏文件的支持。参见示例:MMFSparse