关于内存映射文件的理解

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

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

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

我理解有没有问题?
...全文
694 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)
本PDF电子书包含上下两册,共1576页,带目录,高清非扫描版本。 作者: 毛德操 胡希明 丛书名: Linux内核源代码情景分析 出版社:浙江大学出版社 目录 第1章 预备知识 1.1 Linux内核简介. 1.2 Intel X86 CPU系列的寻址方式 1.3 i386的页式内存管理机制 1.4 Linux内核源代码中的C语言代码 1.5 Linux内核源代码中的汇编语言代码 第2章 存储管理 2.1 Linux内存管理的基本框架 2.2 地址映射的全过程 2.3 几个重要的数据结构和函数 2.4 越界访问 2.5 用户堆栈的扩展 2.6 物理页面的使用和周转 2.7 物理页面的分配 2.8 页面的定期换出 2.9 页面的换入 2.10 内核缓冲区的管理 2.11 外部设备存储空间的地址映射 2.12 系统调用brk() 2.13 系统调用mmap() 第3章 中断、异常和系统调用 3.1 X86 CPU对中断的硬件支持 3.2 中断向量表IDT的初始化 3.3 中断请求队列的初始化 3.4 中断的响应和服务 3.5 软中断与Bottom Half 3.6 页面异常的进入和返回 3.7 时钟中断 3.8 系统调用 3.9 系统调用号与跳转表 第4章 进程与进程调度 4.1 进程四要素 4.2 进程三部曲:创建、执行与消亡 4.3 系统调用fork()、vfork()与clone() 4.4 系统调用execve() 4.5 系统调用exit()与wait4() 4.6 进程的调度与切换 4.7 强制性调度 4.8 系统调用nanosleep()和pause() 4.9 内核中的互斥操作 第5章 文件系统 5.1 概述 5.2 从路径名到目标节点 5.3 访问权限与文件安全性 5.4 文件系统的安装和拆卸 5.5 文件的打开与关闭 5.6 文件的写与读 5.7 其他文件操作 5.8 特殊文件系统/proc 第6章 传统的Unix进程间通信 6.1 概述 6.2 管道和系统调用pipe() 6.3 命名管道 6.4 信号 6.5 系统调用ptrace()和进程跟踪 6.6 报文传递 6.7 共享内存 6.8 信号量 第7章基于socket的进程间通信 7.1系统调用socket() 7.2函数sys—socket()——创建插口 7.3函数sys—bind()——指定插口地址 7.4函数sys—listen()——设定server插口 7.5函数sys—accept()——接受连接请求 7.6函数sys—connect()——请求连接 7.7报文的接收与发送 7.8插口的关闭 7.9其他 第8章设备驱动 8.1概述 8.2系统调用mknod() 8.3可安装模块 8.4PCI总线 8.5块设备的驱动 8.6字符设备驱动概述 8.7终端设备与汉字信息处理 8.8控制台的驱动 8.9通用串行外部总线USB 8.10系统调用select()以及异步输入/输出 8.11设备文件系统devfs 第9章多处理器SMP系统结构 9.1概述 9.2SMP结构中的互斥问题 9.3高速缓存与内存的一致性 9.4SMP结构中的中断机制 9.5SMP结构中的进程调度 9.6SMP系统的引导 第10章系统引导和初始化 10.1系统引导过程概述 10.2系统初始化(第一阶段) 10.3系统初始化(第二阶段) 10.4系统初始化(第三阶段) 10.5系统的关闭和重引导

15,466

社区成员

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

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