论文部分内容阅读
摘要:分析了Win32 系统下的内存结构以及内存管理方式,重点介绍了内存映射文件的工作机理以及相应的程序实现 ,并与堆方式内存分配作比较指出其优越性。
关键词:内存管理方式;内存结构;内存映射文件;堆分配
中图分类号:TP311文献标识码:A 文章编号:1009-3044(2007)17-31352-02
The Application andAdvantage of Memory-Mapped File on the Operation of Mass Data Files
JIA Qin-yong,GUO Qing-ping
(Computer Institute,Wuhan university of Technology,Wuhan 430063,China)
Abstract:AnalyzesMemory mode in WIN32 and introduces the operation on Memory-Mapped Fileandpoints out how to realize it in programming, we can find the efficiency about mapping file into memory compare to Heap Allocation.
Key words:Memory Storage; Memory Structure; Memory-Mapped File ; Heap Allocation
1 Win32内存结构及管理方式
1.1 Win32内存的结构形式
每个进程的虚拟地址空间都要划分成各个分区,地址空间的分区是根据操作系统的基本实现方法来进行的。不同的Windows内核,其分区也略有不同。
如表1,在Windows 系统中, 任何一个进程都被赋予其独立的虚拟地址空间, 对于一个32 位进程, 地址空间覆盖的大小为4GB, 一个指针可以使用这个4GB 空间范围内的任何值, 从图中可以看到,用户进程实际只有小于2G的空间(从0x00010000-0x7FFEFFFF),其余的2G(0x80000000-0xFFFFFFFF)则为内核所拥有,用户没有权限访问。
表1 32位进程的地址空间分区
1.2Windows 操作系统对内存的管理方式
Windows 操作系统对内存的管理方式有3 种: 虚拟内存方式、内存映像文件方式、堆方式。其中虚拟内存的管理方式常用来管理大型对象和结构数组;内存映射文件适合用于管理来自文件的大型数据流,以及在单机上的进程间共享数据;堆方式用于管理大量的小型对象。
2 内存映射文件的原理及其实现
在文件操作中,一般的方法不外乎是使用fopen,fseek,fread,fclose等等一些底层通用的文件操作函数来进行文件的I/O处理,或者是使用针对某种文件格式的高层文件操作函数,然后在应用程序中对这些数据进行处理,由于从硬盘读、存取数据的时间比较长,应用程序为了能正常运行要不断的与硬盘交换数据,这会浪费很长的时间来等待。如果要处理的文件是一个几十M甚至几个G的大型数据文件时,此时仍用此类方法对任何人来说都是无法忍受的。
本文我们通过对字符或字符串的查找来实现对超大文件的操作,并通过计算查找时间来证明内存映射文件相对传统在对文件的操作中的优势,并进一步通过实验论证内存映射文件相对于堆分配在内存的分配中的快速性。
2.1 内存映射文件的实现步骤
下面我们介绍如何使用内存映射文件来实现对文件的处理,程序在VC++6.0中实现。
(1) 调用CreateFile()函数,将文件映像的物理存储器的位置告诉操作系统。传递的路径名用于指明支持文件映像的物理存储器在磁盘(或网络或光盘)上的确切位置。
HANDLE hFile = CreateFile(strPath,GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
strPath为查找的文件路径,这通过文件打开的方式找到;
(2) 用CreateFileMapping() 函数打开一个文件内核对象, 并告诉系统,文件映射对象需要多少物理存储器,该文件内核对象用于标志用作内存映射文件的磁盘文件,该函数返回一个文件映射句柄供后面的操作用;代码如下:
HANDLE hFileMapping = CreateFileMapping(hFile, NULL,
PAGE_READONLY, 0, 0, NULL);
以上代码为创建文件映射内核对象,文件的大小为磁盘中文件的,以只读方式打开文件;
(3) 用MapViewOfFile()将文件映射对象的全部或一部分映射到进程地址空间中。当把文件映射到进程地址空间后,就可以通过指针来访问内存映射文件中的数据。
LPCTSTR pbFile = (LPCTSTR) MapViewOfFile(hFileMapping, FILE_MAP_READ,
(DWORD)(qwFileOffset >> 32),
(DWORD)(qwFileOffset & 0xFFFFFFFF),
dwBytesInBlock);
其中qwFileOffset为指向的视图文件起始偏移处,文件偏移值设置为64位,方便超大文件的读取,并且这个偏移值必须是系统的分配粒度的倍数。dwBytesInBlock为每次视图文件映射到进程地址空间的实际大小。在对超大文件进行的操作中,我们既是通过修改此值来保证可以每次都读取到一部分的数据到进程地址空间中,而不会受到文件大小或是系统地址空间大小的限制。
当完成对内存映射文件的使用后,必须执行下面步骤将它清除:
(4) 用UnmapViewOf File() 在进程的地址空间中撤消文件
映射内核对象的映射;
(5) CloseHandle() ,关闭文件内核对象。
2.2 对超大文件进行查找字符串的原理及实现
运用上述原理,我们就可以不受限制的访问超大文件了。下面是我们实行的对超大文件查询的查询工作,其中关于内存映射文件中所用到的API函数及其具体使用方法已在上述代码中给出说明。对此不再论述。字符串的查询我们用到的是CString类。通过将文件映射对象每次只映射dwBytesInBlock的大小到进程地址空间中,并记录此块的数目,然后在每块中来查找字符串出现的次数,以及出现的位置,我们可以很直观的看到查找结果。在程序代码开始和结尾处我们分别给出函数GetTickCount(),我们可以得到程序每次总共计时数,由于采用了内存映射文件技术,我们会发现,程序的运行过程只是一瞬间的事情。哪怕是超大文件的读取,此程序仍然不会花太多时间。在如下图1中我们给出程序查找字符串的结果,本文所查询文档大小为109KB,为方便实验显示,没有采用超大文件,但程序中以实现对于超大文件的读取,而采用了以系统分配粒度为单位映射文件映射对象到地址空间中的方法来实现程序,其方法还是有理论依据的。
实验论证结果如下:
图1查询结果显示
2.3 内存映射文件相对堆内存分配的优势
为更形象的显示内存映射文件方法对内存分配的快捷性,我们实现了其对堆内存的对比,前者中主要实现代码如下:
HANDLE hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0, 2000000,NULL);
LPVOID lpMapAddress = MapViewOfFile(hFileMap,FILE_MAP_WRITE,0,0,0);
程序创建400个字节的文件映射对象,并将其全部映射到进程地址空间中去。
对堆内存分配,主要代码为:
int *lpint = new int[500000];
delete[] lpint;
从上可以看到,我们对两种方法分配内存大小一样,都是2000000字节。然后我们将两个代码分别放到一个3000次的循环中去,这样更容易得到明显的差别。对应每种方法我们也分别放置两个GetTickCount(),得出分别的用时数。可看到如下结果:
the time of filemapping is : 63milliseconds
the time of newing the heap is :29812milliseconds
该程序完整的给出了内存映射文件在对内存的分配上的优势,更有利于说明内存映射文件的快捷性。
以上程序实现均在同一台机器上实现,机器配置为:P4 2.4GHZ,1.00GB内存。
3 结论
本文分析了WINDOWS32内存结构及其在对超大文件的读取工作中所一般采用的内存映射文件的方法,对其原理进行了分析并通过实验论证其可行性,对通过与堆内存分配方式作比较,得出其在内存分配方式中的快捷性,实验均在VC++6.0编译器上通过。
参考文献:
[1]Jeffery Richer .windows 核心编程(中文版)[M] .北京:机械工业出版社, 1999,27.
[2]David J Kruglinski. Visual C ++ 6. 0 技术内幕(第四版)[M] .北京:清华大学出版社,1999.
[3]http://www.microsoft.com/msdn
注:本文中所涉及到的图表、注解、公式等内容请以PDF格式阅读原文。
关键词:内存管理方式;内存结构;内存映射文件;堆分配
中图分类号:TP311文献标识码:A 文章编号:1009-3044(2007)17-31352-02
The Application andAdvantage of Memory-Mapped File on the Operation of Mass Data Files
JIA Qin-yong,GUO Qing-ping
(Computer Institute,Wuhan university of Technology,Wuhan 430063,China)
Abstract:AnalyzesMemory mode in WIN32 and introduces the operation on Memory-Mapped Fileandpoints out how to realize it in programming, we can find the efficiency about mapping file into memory compare to Heap Allocation.
Key words:Memory Storage; Memory Structure; Memory-Mapped File ; Heap Allocation
1 Win32内存结构及管理方式
1.1 Win32内存的结构形式
每个进程的虚拟地址空间都要划分成各个分区,地址空间的分区是根据操作系统的基本实现方法来进行的。不同的Windows内核,其分区也略有不同。
如表1,在Windows 系统中, 任何一个进程都被赋予其独立的虚拟地址空间, 对于一个32 位进程, 地址空间覆盖的大小为4GB, 一个指针可以使用这个4GB 空间范围内的任何值, 从图中可以看到,用户进程实际只有小于2G的空间(从0x00010000-0x7FFEFFFF),其余的2G(0x80000000-0xFFFFFFFF)则为内核所拥有,用户没有权限访问。
表1 32位进程的地址空间分区
1.2Windows 操作系统对内存的管理方式
Windows 操作系统对内存的管理方式有3 种: 虚拟内存方式、内存映像文件方式、堆方式。其中虚拟内存的管理方式常用来管理大型对象和结构数组;内存映射文件适合用于管理来自文件的大型数据流,以及在单机上的进程间共享数据;堆方式用于管理大量的小型对象。
2 内存映射文件的原理及其实现
在文件操作中,一般的方法不外乎是使用fopen,fseek,fread,fclose等等一些底层通用的文件操作函数来进行文件的I/O处理,或者是使用针对某种文件格式的高层文件操作函数,然后在应用程序中对这些数据进行处理,由于从硬盘读、存取数据的时间比较长,应用程序为了能正常运行要不断的与硬盘交换数据,这会浪费很长的时间来等待。如果要处理的文件是一个几十M甚至几个G的大型数据文件时,此时仍用此类方法对任何人来说都是无法忍受的。
本文我们通过对字符或字符串的查找来实现对超大文件的操作,并通过计算查找时间来证明内存映射文件相对传统在对文件的操作中的优势,并进一步通过实验论证内存映射文件相对于堆分配在内存的分配中的快速性。
2.1 内存映射文件的实现步骤
下面我们介绍如何使用内存映射文件来实现对文件的处理,程序在VC++6.0中实现。
(1) 调用CreateFile()函数,将文件映像的物理存储器的位置告诉操作系统。传递的路径名用于指明支持文件映像的物理存储器在磁盘(或网络或光盘)上的确切位置。
HANDLE hFile = CreateFile(strPath,GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
strPath为查找的文件路径,这通过文件打开的方式找到;
(2) 用CreateFileMapping() 函数打开一个文件内核对象, 并告诉系统,文件映射对象需要多少物理存储器,该文件内核对象用于标志用作内存映射文件的磁盘文件,该函数返回一个文件映射句柄供后面的操作用;代码如下:
HANDLE hFileMapping = CreateFileMapping(hFile, NULL,
PAGE_READONLY, 0, 0, NULL);
以上代码为创建文件映射内核对象,文件的大小为磁盘中文件的,以只读方式打开文件;
(3) 用MapViewOfFile()将文件映射对象的全部或一部分映射到进程地址空间中。当把文件映射到进程地址空间后,就可以通过指针来访问内存映射文件中的数据。
LPCTSTR pbFile = (LPCTSTR) MapViewOfFile(hFileMapping, FILE_MAP_READ,
(DWORD)(qwFileOffset >> 32),
(DWORD)(qwFileOffset & 0xFFFFFFFF),
dwBytesInBlock);
其中qwFileOffset为指向的视图文件起始偏移处,文件偏移值设置为64位,方便超大文件的读取,并且这个偏移值必须是系统的分配粒度的倍数。dwBytesInBlock为每次视图文件映射到进程地址空间的实际大小。在对超大文件进行的操作中,我们既是通过修改此值来保证可以每次都读取到一部分的数据到进程地址空间中,而不会受到文件大小或是系统地址空间大小的限制。
当完成对内存映射文件的使用后,必须执行下面步骤将它清除:
(4) 用UnmapViewOf File() 在进程的地址空间中撤消文件
映射内核对象的映射;
(5) CloseHandle() ,关闭文件内核对象。
2.2 对超大文件进行查找字符串的原理及实现
运用上述原理,我们就可以不受限制的访问超大文件了。下面是我们实行的对超大文件查询的查询工作,其中关于内存映射文件中所用到的API函数及其具体使用方法已在上述代码中给出说明。对此不再论述。字符串的查询我们用到的是CString类。通过将文件映射对象每次只映射dwBytesInBlock的大小到进程地址空间中,并记录此块的数目,然后在每块中来查找字符串出现的次数,以及出现的位置,我们可以很直观的看到查找结果。在程序代码开始和结尾处我们分别给出函数GetTickCount(),我们可以得到程序每次总共计时数,由于采用了内存映射文件技术,我们会发现,程序的运行过程只是一瞬间的事情。哪怕是超大文件的读取,此程序仍然不会花太多时间。在如下图1中我们给出程序查找字符串的结果,本文所查询文档大小为109KB,为方便实验显示,没有采用超大文件,但程序中以实现对于超大文件的读取,而采用了以系统分配粒度为单位映射文件映射对象到地址空间中的方法来实现程序,其方法还是有理论依据的。
实验论证结果如下:
图1查询结果显示
2.3 内存映射文件相对堆内存分配的优势
为更形象的显示内存映射文件方法对内存分配的快捷性,我们实现了其对堆内存的对比,前者中主要实现代码如下:
HANDLE hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0, 2000000,NULL);
LPVOID lpMapAddress = MapViewOfFile(hFileMap,FILE_MAP_WRITE,0,0,0);
程序创建400个字节的文件映射对象,并将其全部映射到进程地址空间中去。
对堆内存分配,主要代码为:
int *lpint = new int[500000];
delete[] lpint;
从上可以看到,我们对两种方法分配内存大小一样,都是2000000字节。然后我们将两个代码分别放到一个3000次的循环中去,这样更容易得到明显的差别。对应每种方法我们也分别放置两个GetTickCount(),得出分别的用时数。可看到如下结果:
the time of filemapping is : 63milliseconds
the time of newing the heap is :29812milliseconds
该程序完整的给出了内存映射文件在对内存的分配上的优势,更有利于说明内存映射文件的快捷性。
以上程序实现均在同一台机器上实现,机器配置为:P4 2.4GHZ,1.00GB内存。
3 结论
本文分析了WINDOWS32内存结构及其在对超大文件的读取工作中所一般采用的内存映射文件的方法,对其原理进行了分析并通过实验论证其可行性,对通过与堆内存分配方式作比较,得出其在内存分配方式中的快捷性,实验均在VC++6.0编译器上通过。
参考文献:
[1]Jeffery Richer .windows 核心编程(中文版)[M] .北京:机械工业出版社, 1999,27.
[2]David J Kruglinski. Visual C ++ 6. 0 技术内幕(第四版)[M] .北京:清华大学出版社,1999.
[3]http://www.microsoft.com/msdn
注:本文中所涉及到的图表、注解、公式等内容请以PDF格式阅读原文。