为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

《windows核心编程系列》谈谈内存映射文件(可编辑)

2017-10-17 11页 doc 31KB 13阅读

用户头像

is_003124

暂无简介

举报
《windows核心编程系列》谈谈内存映射文件(可编辑)《windows核心编程系列》谈谈内存映射文件(可编辑) 《windows核心编程系列》谈谈内存映射文件 iitthhzzhhaanngg 收收到到浙浙江江大大华华的的ooffffeerr了了~~~~ 《《wwiinnddoowwss核核心心编编程程系系列列 谈谈谈谈内内存存映映射射文文件件 分类: 谈谈windows 核心编程系列 2011-11-22 21:30 819人阅读 评论 1 收藏 举报 内存映射文件允许开发人员预订一块地址空间并为该区域调拨物理 存储器,与虚拟内存不同的是,内存映射文件的物理存储器...
《windows核心编程系列》谈谈内存映射文件(可编辑)
《windows核心编程系列》谈谈内存映射文件(可编辑) 《windows核心编程系列》谈谈内存映射文件 iitthhzzhhaanngg 收收到到浙浙江江大大华华的的ooffffeerr了了~~~~ 《《wwiinnddoowwss核核心心编编程程系系列列 谈谈谈谈内内存存映映射射文文件件 分类: 谈谈windows 核心编程系列 2011-11-22 21:30 819人阅读 评论 1 收藏 举报 内存映射文件允许开发人员预订一块地址空间并为该区域调拨物理 存储器,与虚拟内存不同的是,内存映射文件的物理存储器来 自磁盘中的文 件,而非系统的页交换文件。将文件映射到内存中后,我们就可以在内存中 操作他们了,就像他们被载入内存中一样。 内存映射文件主要有三方面的用途: 1:系统使用内存映射文件来将exe或是dll文件本身作为后备存储器,而非系 统页交换文件,这大大节省了系统页交换空间,由于不需要将exe或是dll文 件加载到页系统交换文件,也提高了启动速度。 2:使用内存映射文件来将磁盘上的文件映射到进程的空间区域,使得开发人 员操作文件就像操作内存数据一样,将对文件的操作交由操作系统来管理, 简化了开发人员的工作。 3:windows提供了多种进程间通信的方法,但他们都是基于内 存映射文件来 实现的。 这里首先讨论第一种情况: 在一个exe文件运行之前,系统首先为新进程创建一个进程内核对象, 同时预订一块足够大的地址空间来容纳该文件。然后,对该地址空间进行标 注,注明他的后备存储器来 自exe文件,而非系统的页交换文件。此措施对提 高系统性能有重大意义。 一个可执行文件,当他有多个实例同时运行,系统在创建另一个新 的实例时,仅仅是打开了另一个内存映射视图。所有这些视图都来 自于同一 个文件映射对象 (即可执行文件本身)。 当新实例开始运行时,系统只是把包含应用程序代码和数据的虚拟内存页面 映射到了他的地址空间中,当其中的一个实例试图去修改数据段中的数据, 如果不采取有效措施,那么应用程序的所有其他实例的内存都会被修改,这 是不合常理的。因此windows采取了一种叫做写时复制的特性,来防止这种情 况的发生。 系统将可执行文件映射到地址空间中时,会计算有多少页面是可写 的。 (通常包含数据的页面被标记为PAGE_READWRITE属性,它们是可写的) 然后会从系统的页交换文件中调拨物理存储器,来容纳这些可写的页面。但 是系统只是调拨这些页面,并不会实际载入页面的内容,只有当写入可写页 1 是系统只是调拨这些页面,并不会实际载入页面的内容,只有当写入可写页 面的时候才会真正实际载入。 (后面会详细介绍) 任何时候当应用程序试图写入内存映射文件的时候,系统会截获此类 尝试,接着从先前在系统页交换文件中分配的空间中取出一页,复制要写入 页面的内容,让应用程序写入刚刚从系统页交换文件中分配的 页,而不是内 存映射文件中的页。由于写入到的区域仅仅是内存映射文件的副本,不会对 内存映射文件写入,这样就保证了其他实例不会受到任何影响。另外需要注 意的是,内存映射文件的副本 (在系统页交换文件中)被映射到了新实例的 地址空间区域的同一位置。 以上介绍的是在同一个可执行文件的多个实例之间不会共享数据的 情况。有时候在多个实例之间共享数据非常有用,可以大大提高编程效率。 接着我们就讨论如何在一个可执行文件的多个实例中共享数据。 我们知道默认情况下,我们定义的初始化数据被放到了数据段, 未初 始化的数据放到了.bss段。除了使用这些段之外,我们也可以将数据放 在我们 自己的段中。 首先,就要知道如何创建一个段。 #pragm data_seg "sectionname" //创建一个名为sectionname 的 段。 看例子: #pragm data_seg "newsection" //此处创建一个名为newsection的段 int a 23;//向此段中添加变量。 #pragm data_seg //结束添加 此例创建了一个名为newsection的段,并向此段添加int类型变量 a。#pragm data_seg 用于结束向段中添加数据。 要注意一点编译器只会将以初始化的变量放入我们的段中,如上例中的a。 如果这样: #pragm data_seg "newsection" int a 23; int b ; #pragm data_seg b是不会被添加到段newsection中的。而是放到默认的标准段中。 2 虽然编译器只会将初始化的变量放入 自定义段中,但是我们可以强制的 将一个未初始化的数据放我任何我们想放入的段中。 _declspec allocate "newsection" int b ;将b放入newsection中。 仅仅新建一个段,并将要共享的数据放入新建段中是不够的,还需要将 该段声明为共享段。 我们可以使用: 1: #pragm comment linker,"/SECTION :newsection,RWS" 2:链接器开关:/SECTON :newsecton,RWS 其中R表示READ,W表示WRITE,S表示SHARE。他们为newsection指定的属 性。SHARE即为共享的意思,意思是把此段让所有实例共享。 放入共享段的变量在多个实例中只有一份,不会再向数据段中的变量 一样:每个实例都有一个副本。所以任何实例都可以修改它们。非常重要的 一点就是:由于多个实例可以同时修改共享段中的变量,因此要注意同步问 题。可以采取线程同步中所介绍的一些方法。 现在来讨论内存映射文件介绍的第二个用途:内存映射磁盘数据文件。 要使用内存映射磁盘文件需要三个步骤: 1:创建或打开一个文件内核对象。 2:创建一个文件映射内核对象。 3:将文件映射对象映射到进程地址空间。 对于第一点,可以调用CreateFile或是OpenFile,很简单, 此处不作介 绍。 HANDLE WINAPI CreateFile __in LPCTSTR lpFileName, __in DWORD dwDesiredA ess, __in DWORD dwShareMode, __in_opt LPSECURITY_ATTRIBUTES lpSe urityAttributes, __in DWORD dwCreationDisposition, __in DWORD dwFlagsAndAttributes, __in_opt HANDLE hTemplateFile ; 第二点:可以调用CreateFileMapping 3 HANDLE WINAPI CreateFileMapping __in HANDLE hFile, __in_opt LPSECURITY_ATTRIBUTES lpAttributes, __in DWORD flProte t, __in DWORD dwimumSizeHigh, __in DWORD dwimumSizeLow, __in_opt LPCTSTR lpName ; 第一个hFile为要映射到进程地址空间中的文件句柄,CreateFile或是 OpenFile返回。 第二个psa为安全属性,一般都传NULL,表示使用默认安全属性。 第三个为fdwProtect保护属性,指定当将文件映射到进程地址空间的时 候,应该给物理存储器的页面指定何种保护属性。 第四个,第五个参数告诉系统内存映射文件的最大大小。 第四个参数dwimumSizeHigh为表示文件大小的64位整数的高字 节,dwimumSizeLow为低字节。对于小于4G的文件来说,高字节当然为0. 如果要以文件的当前大小创建一个映射对象时,只要将他们设为0就可 以。如果要文件中添加数据,一定要使指定的大小大于文件的真实大小。 ?第六个参数为文件映射内核对象的名称。用于跨进程共享命名内核对 象。 (请参考windows核心编程 第五版 第三章)需要特别强调下,如果为 flProtect指定PAGE_READWRITE属性,当文件的真实大小小于参数中指定的大 小的时候,CreateFileMapping会 自动增大文件大小。为的是在将文件作为内 存映射文件后,物理存储器已经就绪。向其写入数据不会发生错 误。如果指 定PAGE_READONLY或是PAGE_WRITECOPY,那么传入的大小不能 大于文件的真实 大小,因为我们只并不能向文件中增加数据。 第三步:将文件映射到进程地址空间。 MapViewOfFile LPVOID WINAPI MapViewOfFile __in HANDLE hFileMappingObje t, __in DWORD dwDesiredA ess, __in DWORD dwFileOffsetHigh, __in DWORD dwFileOffsetLow, __in SIZE_T dwNumberOfBytesToMap ; 第一个参数hFileMappingObje t即为CreateFileMapping或是OpenFileMap 第二个参数是访问数据的方式。他们依赖于CreateFileMapping 和 CreateFile传递的访问方式。 4 第三个和第四个参数告诉系统把数据文件中的的那些内容映射到进程地 址空间中。他们分别为要映射文件的偏移 量,是64位的,分别表示高32位和 低32位。 第五个参数指明要把磁盘文件的多少数据映射到进程地址空间中。如果 指定为0,系统会把文件中从偏移量开始直到文件末尾的数据全部映射到进程 地址空间中。 当调用MapViewOfFile时指定FILE_MAP_COPY标志,系统会从系统页 交换文件调拨物理存储器,大小有dwNumberOfBytesToMap指定。只要我们不 执行读取数据之外的任何操作,系统就不会使用从页交换文件中调拨页面?。 但是一旦有任何线程写入文件映射视图的任何地址,系统就会从已经调拨的 页交换文件中选择一个页面把原始数据复制到页交换文件中的 页面,然后让 线程进行修改这个副本,再将此页面映射到进程地址空间中。因此任何线程 都只会修改数据的副本而不会修改原始数据。 当不再需要把文件中的数据映射到进程的地址空间的时候,可以调用 UnmapViewOfFile来释放映射的数据。 BOOL WINAPI UnmapViewOfFile __in LPCVOID lpBaseAddress ; lpBaseAddress用于指定区域的基地址,必须和MapViewOfFile相同。 为了提高运行速度,系统会对文件数据的页面进行缓存处理,也 就是说对文 件映射对象映射后的视图进行修改,不会立即反映到数据文件中。如果想要 立即反映到数据文件中,可以调用FlushViewOfFile。来强制系统把修改过的 数据协会磁盘文件。 如果视图最初使用FILE_MAP_COPY标志来映射的,那么对数据文件的 修改实际上对系统页交换文件中的副本进行的修改。请参考红色字段。如果 这种情况下调用FlushViewOfFile,系统不会将做出的修改保存到磁盘文件 中,而会直接释放系统页交换文件中的相关数据,导致数据丢失。这只是 FILE_MAP_COPY的特性,为了防止数据丢失可以用其他标志进行映射。 最后不要忘记调用CloseHandle关闭文件内核对象和文件映射内核对 象。 如果文件非常大,一次无法全部映射到进程的地址空间中,这是该怎 么办呢, 此时可以每次只映射一部分文件到进程空间,使用完毕后,撤销映 射。再映射下一部分,使用完毕后再次撤销映射。如此循环往复。直至将整 个文件映射完毕 。 系统允许我们把一个数据文件映射到多个视图中。如果我们使 用的是 同一个文件映射对象映射到不同视图,一旦有一个视图中的数据被修改,其 他视图中会立刻更新进而显示更新后的视图。也就是说各个视图中的数据是 一致的。为什么各个视图的数据都是一致的呢, 5 因为他们都是从同一个文件映射对象映射的,数据文件在内存中只 有一份,却映射到了不同视图中。但要注意,此处有一前提,就是各个视图 都是有同一文件映射对象映射的,如果是同一数据文件为后备存储器创建不 同文件映射对象,那就不能保证他们的数据是一致的了。为了防止这种情 况,可以在CreateFile时将dwShareMode 设为独占对文件的访问。从而防止 不一致性。 注意:在使用完内存映射文件后一定要先撤销对视图的映射和关闭 文件映射对象句柄,在对执行打开文件等对文件进行的操作。否则将会造成 执行文件操作的失败。另外,执行的操作又失败的可能性,一定要检查。 (2011年11月24 日,在写文件逆置程序时,撤销对视图的映射后,没有关闭 文件映射对象的句柄就执行文件操作,结果这些文件操作都失败了。但是没 有执行判断,花了好久才查到错误。) 下面来讨论第三个问题:内存映射文件实现进程间共享数据。 如果我们在创建文件映射对象时为它命名,那么就可以实现在不同进 程间访问同一文件映射内核对象了。但要注意,要在不同进程分别调用 MapViewOfFile,来将同一命名文件映射内核对象,映射到各 自的进程地址空 间中。 到此,以我们 目前掌握的知识,我们知道要实现在多个进程间共享数 据,要创建一个文件对象和一个命名的文件映射内核对象。然后在另一个进 程内将此命名的内核对象映射到本进程。这一系列的步骤说明:如果我们要 在多个进程间共享数据,我们就必须创建文件,将数据保存在文件中,然后 创建文件对象,文件映射对象。。。。这是很繁琐的。 Microsoft意识到了这一点,为我们提供了支持:让系统创建以页交 换文件为后备存储器的内存映射文件。这就是说当实现进程共享数据时,不 再需要创建以磁盘文件为后备存储器的文件映射对象。此时,文件映射对象 的后备存储器来 自系统页交换文件。这种方法和为磁盘文件创建内存映射文 件几乎完全相同。区别就是:此时无需创建文件对象,在创建文件映射对象 时,只需将INVALID_HANDLE_VALUE传给hFile就可以了。他告诉系统要以系统 页交换文件中调拨物理存储器。以后的步骤跟为磁盘文件创建内存映射文件 相同。 很简单,不是吗,来看个例子: HANDLE hFile CreateFile ... HANDLE hMap CreateFileMapping hFile........ ; if hMap NULL ..................... 6 看出来什么问题吗, 我们知道调用CreateFile失败的时候,返回的是INVALID_HANDLE_VALUE,而 此处没有判断文件对象是否成功,就直接创建文件映射对象,一旦创建文件 对象失败,hFile就是INVALID_HANDLE_VALUE,系统会以为程序员要创建以系 统页交换文件为后备存储器的内存映射文件,而不是为磁盘文件创建内存映 射文件。这就导致了错误。所以在可能导致失败的函数执行之后一定要进程 判断。 参考 自 《windows核心编程―第五版 第三部分,以上仅仅是个人总结,如有 纰漏,请不吝赐教,谢谢。同时,想结交志同道合之士,交流windows核心编 程的学习。 7
/
本文档为【《windows核心编程系列》谈谈内存映射文件(可编辑)】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索