关于内存映射文件的理解

everbeing 2010-05-24 09:11:01
内存映射文件 是不是 和虚拟内存机制 差不多

不同之处 系统程序 缺页时 虚拟内存机制 是磁盘虚拟内存区(如果虚拟内存中没有,就从物理磁盘调入虚拟内存)中调入新的页面 以继续程序的正常执行

而 内存映射 是发现缺页时候,是直接从磁盘调入内存

我理解有没有问题?
...全文
642 35 打赏 收藏 转发到动态 举报
写回复
用AI写文章
35 条回复
切换为时间正序
请发表友善的回复…
发表回复
JohnHealy 2012-05-15
  • 打赏
  • 举报
回复
说到,内存文件映射,首先要明白系统对硬盘文件的资源访问是什么方式的.
这里,只举个例子,在fat32磁盘格式里的文件.
"FAT32具有一个最大的优点:在一个不超过8GB 的分区中,FAT32分区格式的每个簇容量都固定为4KB,与FAT16相比,可以大大地减少磁盘的浪费,提高磁盘利用率。"

再看"启动扇区
  格式如下   偏移(字节) 长度(字节) 说明   0x00 3 跳转指令(跳过开头一段区域)   0x03 8 OEM名称(空格补齐)。 MS-DOS检查这个区域以确定使用启动记录中的哪一部分数据。常见值是IBM 3.3(在“IBM”和“3.3”之间有两个空格)和MSDOS5.0.   0x0b 2 每个扇区的字节数。基本输入输出系统参数块从这里开始。   0x0d 1 每簇扇区数   0x0e 2 保留扇区数(包括启动扇区)   0x10 1 文件分配表数目   0x11 2 最大根目录条目个数   0x13 2 总扇区数(如果是0,就使用偏移0x20处的4字节值)   0x15 1 介质描述 0xF8 单面、每面80磁道、每磁道9扇区   0xF9 双面、每面80磁道、每磁道9扇区 ........"

这里假设,你要映射的文件"c:\temp\xxx.txt",因为使用系统提供的函数,会自动标明该文件里的某一段内存实际指向的硬盘信息(文件的柱,扇,磁 的具体信息)区(每4k为一 个块 FAT32分区格式的每个簇容量),

再看CreateFile---The CreateFile function creates or opens a file, file stream, directory, physical disk, volume, console buffer, tape drive, communications resource, mailslot, or named pipe. The function returns a handle that can be used to access an object. --->等于获取指定的计算机某资源,而文件映射获取正是指定文件在硬盘具体信息.

CreateFileMappin -->将该文件资源按指定SizeHight,SizeLow的大小进行映射,假设文件头数据pData的磁盘资源为1,30,32 通过映射,pData+4k 可能为(1,30,32+一个文件块大小),pData+8k可能为(1,30,32+一个文件块大小).

MapViewOfFile -->获取该文件映射后的内存指针,该(BYTE*)指针+4K的大小,等于直接访问文件的下一个块.

因此,可以看到,经过第一次I/O (CeateFile)操作后,读写文件信息都不需要再进行 I/O操作了(或可能是非查询式的I/O操作).
seacat_hello 2011-05-20
  • 打赏
  • 举报
回复
哦 顺序不对- -重新写一遍
善古 2011-01-03
  • 打赏
  • 举报
回复
我觉得 yxwsbobo 说得很好..
内核模式和用户模式是不一样的,内存映射是内核模式下做的.
zkj66278006 2010-06-28
  • 打赏
  • 举报
回复
学习了
yxwsbobo 2010-06-02
  • 打赏
  • 举报
回复
内存映射文件和 虚拟内存机制不是一个东西


程序是无法直接和真正内存打交道的,所以每个程序都有4G可以用的内存(32位 2^32),我们知道实际上并没有这么多,所以程序直接接触到的内存必然是虚拟内存


内存映射文件,本质上就和我们打开文件一样,也是将文件从磁盘复制到内存中,但内存映射文件可以选择将哪部分,以及多大映射到内存中,而且这个操作是系统内部完成的,显然要比用程序快.

为什么可以映射大文件,因为它可以一部分一部分映射到内存中,而且是系统内部操作的,所以你感觉和打开了一个大文件一样,但要注意,由于是系统操作,所以如果处理大量小数据反而会造成内存碎片
everbeing 2010-06-02
  • 打赏
  • 举报
回复
这几天没时间,太忙- -,先结了吧 ,以后再来讨论这个问题,谢谢各位
副组长 2010-05-29
  • 打赏
  • 举报
回复
[Quote=引用 27 楼 everbeing 的回复:]
ok1234567 的说法是:

一假设个大文件如果假设是1G,那么只要分配到1G的内存,它就全将1G加载进去(虽然那个时候又这么的内存可利用,但这和一次性加载似乎就没有区别了)?????,当如果太大内存分配不够的话的话,久采用磁盘虚存page?


哎,这个问题我知道也不好讨论,但讨论来讨论去,最终也还是依旧比较模糊,好像说来说去都只是站在外沿说,都不够”核心“,都没有讲出“具……
[/Quote]
根据需要来具体设计你的系统吧,其实已经非常清楚了。
副组长 2010-05-29
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 ok1234567 的回复:]
如果不flush或者关闭文件,不会有任何更改的数据写入盘文件
Mapping不时缓存机制,而是一个映像机制
[/Quote]
如果有几百G的文件,映像遍历一次,并且有数据的变更,不写回磁盘的话数据会存在哪儿呢?
everbeing 2010-05-28
  • 打赏
  • 举报
回复
ok1234567 的说法是:

一假设个大文件如果假设是1G,那么只要分配到1G的内存,它就全将1G加载进去(虽然那个时候又这么的内存可利用,但这和一次性加载似乎就没有区别了)?????,当如果太大内存分配不够的话的话,久采用磁盘虚存page?


哎,这个问题我知道也不好讨论,但讨论来讨论去,最终也还是依旧比较模糊,好像说来说去都只是站在外沿说,都不够”核心“,都没有讲出“具体做法“!!!!!!!!!!!!!!!!!!!!!!!!

ok1234567 2010-05-27
  • 打赏
  • 举报
回复
32位系统下,最大只能map不到2G的文件,未测试过awe

我觉得,mapfile 与所谓虚拟内存没有什么关系,外存中的数据,总是要加载到内存才能处理,如果物理内存足够,就很少使用分页文件交换数据
mapfile与硬盘上的文件数据进行交换的机制应该是透明的,如果你不显式地回写数据,数据就不会自动写到盘文件中(如果物理内存不够,会交换到系统分页文件中)
ok1234567 2010-05-27
  • 打赏
  • 举报
回复
使用FileMapping进行结构化数据的处理是非常理想的,2G的数据对于小型应用已经很壮观了:)
在64位系统下,应该可以借此方式开发特制的大型数据库应用
ok1234567 2010-05-27
  • 打赏
  • 举报
回复
当你CreateFileMapping的时候,你就裁决了文件的大小!没有只map文件一部分的做法
ok1234567 2010-05-27
  • 打赏
  • 举报
回复
如果不flush或者关闭文件,不会有任何更改的数据写入盘文件
Mapping不时缓存机制,而是一个映像机制
vincent_1011 2010-05-27
  • 打赏
  • 举报
回复
[Quote=引用 22 楼 ok1234567 的回复:]

FileMapping并非是为了处理大文件(在32位系统下),mapping到进程的地址空间,通常不超过2G,这是不可逾越的壁垒
FileMapping提供了高效、灵活、安全、方便的文件数据处理机制(使用数据指针),这可能是其优势
[/Quote]
呵,我并不是说一次全map进去
不过你说的那个好处也确实很好。把磁盘数据map在内存中线性操作啥的
ok1234567 2010-05-27
  • 打赏
  • 举报
回复
FileMapping并非是为了处理大文件(在32位系统下),mapping到进程的地址空间,通常不超过2G,这是不可逾越的壁垒
FileMapping提供了高效、灵活、安全、方便的文件数据处理机制(使用数据指针),这可能是其优势
vincent_1011 2010-05-27
  • 打赏
  • 举报
回复
[Quote=引用 20 楼 everbeing 的回复:]

引用18楼说的:
“不断更改MapView,缓冲区满了以后内容交换的磁盘文件上,或者关闭的时候也将其全部写到磁盘文件上。按照我这种推断,即使不关闭映射,仅更改MapView也能导致磁盘文件的更新”

我也有这样的想法,但这点很重要,基本上我看的资料“都没有提到这点:当修改量达到一定时写回磁盘”,而都只是说只有在关闭映射文件对象或者显示flush的时候才写会磁盘,但这想这显然是不合理的,数据……
[/Quote]

我也觉得不可能只MAP 2G,这个机制我觉得最大好处就是用来操作大文件。

另外,19楼提到其中一点我同意,就是你说的关键点。当你修改大量数据,而内存不足的话,应该是用pagefile来支撑的。不可能自动写回磁盘文件本身,不信可以测试下,把一个4G文件全部访问赋值一次。然后不关闭映射文件,也不调用flush。看磁盘文件是不是被修改了。

希望你放上结果
everbeing 2010-05-27
  • 打赏
  • 举报
回复
引用18楼说的:
“不断更改MapView,缓冲区满了以后内容交换的磁盘文件上,或者关闭的时候也将其全部写到磁盘文件上。按照我这种推断,即使不关闭映射,仅更改MapView也能导致磁盘文件的更新”

我也有这样的想法,但这点很重要,基本上我看的资料“都没有提到这点:当修改量达到一定时写回磁盘”,而都只是说只有在关闭映射文件对象或者显示flush的时候才写会磁盘,但这想这显然是不合理的,数据量修改的太多的话显然是不可能继续吧修改的数据留在内存的,具体的做法我也只能猜了- -

关于上面的测试 :我主要是测一下它们访问的速度,当时也并没有想到用随机访问方式来访问,而是用最简单的 顺序访问来测试 ,不过有点奇怪

两个测试我都只读数据, 如果先看第二个测试,在看第一个测试,时间突然反差我久有点迷糊....

19楼说只能最多映射<2G文件,我觉得这个不是吧,映射进去的只是 几个页面而已,当然文件很小的话,可能都直接被加载进去了 ,如果"最大只能map不到2G的文件",那么着和传统的 直接全部加载进内存久没有什么区别了。
everbeing 2010-05-26
  • 打赏
  • 举报
回复
哈 出现了一个有趣的问题 速度好快 如果不打印到屏幕的话


int main()
{


ShareMemory sm;//一个自定义的封装内存映射类
DWORD start,ReflectTime,IOTime;
if(sm.Reflect("ShareMemory.txt",GENERIC_READ))
{
char *p=(char*)sm.GetStartAddress();//获取映射内存首地址
start=GetTickCount();//记录用内存映射读取数据开始时间
//cout<<p<<endl;//输出
int i=0;
while(*p++);
}
ReflectTime=GetTickCount()-start;//记录内存映射输出花费时间
sm.Release();


ifstream infile("ShareMemory.txt");
if(!infile.is_open())
{
cerr<<"open error!"<<endl;
}

char ch;
start=GetTickCount();//记录用IO读取内存起始时间
while(infile>>ch);
// cout<<ch;
IOTime=GetTickCount()-start;//记录IO读花费时间
infile.close();

cout<<endl<<"ReflectTime= "<<ReflectTime<<"ms"<<endl;
cout<<endl<<"IOTime= "<<IOTime<<"ms"<<endl;
return 0;



测了三次:(还有测试的时候甚至两次测出 ReflectTime= 0ms IOTime=5644/5702ms情况)

ReflectTime= 16ms

IOTime= 5163ms


ReflectTime= 16ms

IOTime= 5164ms


ReflectTime= 15ms

IOTime= 5133ms
everbeing 2010-05-26
  • 打赏
  • 举报
回复
我做了一个测试:读取一个文本内容,并打印在屏幕上(刷屏)来统计其各自所耗费时间

开始用个14M的读,发现时间太长了,懒得等了,久改了个2.73M的文本

用IO读取一个2.73M文本 和用内存映射 读取一个文本

int main()
{

ShareMemory sm;//一个自己封装的内存映射类
DWORD start,ReflectTime,IOTime;
if(sm.Reflect("ShareMemory.txt",GENERIC_READ))//建立映射,包括提交物理存储器(映射整个文件)
{
char *p=(char*)sm.GetStartAddress();//获取映射内存首地址
start=GetTickCount();//记录用内存映射读取数据开始时间
cout<<p<<endl;//输出
}
ReflectTime=GetTickCount()-start;//记录内存映射输出花费时间
sm.Release();//释放操作


ifstream infile("ShareMemory.txt");
if(!infile.is_open())
{
cerr<<"open error!"<<endl;
}

char ch;
start=GetTickCount();//记录用IO读取内存起始时间
while(infile>>ch)
cout<<ch;
IOTime=GetTickCount()-start;//记录IO读花费时间
infile.close();

cout<<endl<<"ReflectTime= "<<ReflectTime<<"ms"<<endl;
cout<<endl<<"IOTime= "<<IOTime<<"ms"<<endl;
return 0;


结果:
ReflectTime= 181164ms

IOTime= 139138ms

程序运行时系统不是很忙,可能系统忙的时候,可能不同,这个直接IO读和用内存映射来读 时间还短一点!!
iqyely 2010-05-26
  • 打赏
  • 举报
回复
来学习下。
加载更多回复(15)

15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧