Windows Vista 和 Windows Server 2008 在内存管理方面的功能增强(翻译)

来源:互联网 发布:mac电脑查看运行程序 编辑:程序博客网 时间:2024/05/21 07:52

原文链接

http://www.microsoft.com/whdc/system/cec/MemMgt.mspx

本文没有翻译NUMA 相关内容,需要的话请自行翻译

初来乍到,如果有错误的地方请及时指出来

概要

       此文介绍Windows Vista® 和 Windows Server® 2008 在内存管理方面所做的一些改进信息。文章描述了Microsoft在操作系统内核实现上的改变,并为应用程序开发者,驱动开发者,硬件提供商提供一个指南,以使它们充分利用这些新特性。
       读者应该熟悉Mark Russinovich and David Solomon 编写的Windows Internals 中所介绍的内存管理的基础知识。
本文应用在下面的操作系统上:
       Windows Server 2008
       Windows Vista

介绍

       Microsoft在 Windows Vista® 和 Windows Server® 2008在内存管理的下面这些方面增加了功能或者提升了性能:
       • 虚拟地址空间的更高效率的使用.
       • 更好的安全性.
       • 更好的使用I/O 带宽.
       • 更快的休眠,待机和重启.
       • 支持 Windows Vista 高性能视频模式.
       • 支持NUMA 体系结构.
       • 为服务硬件和应用程序提供更好的可测量性.
       • 更杰出的系统完整性。

       许多改变对于应用程序和驱动来说是透明的。因此已经存在的代码可以无修改的执行。然而,为了更好的从这些改变中受益,开发人员应该按照本文修改或者重新链接它们的程序。

关于内存管理器

       内存管理器为操作系统处理物理内存和虚拟内存的申请和管理操作。下面列出了它所提供的最重要的服务。
       • 管理重要的系统资源,比如分页和未分页的内存池以及系统缓存。
       • 映射虚拟地址到物理内存。.
       • 分页机制.
       • 保护地址空间免受其进程之间以及操作系统本身的影响。

       内存管理器和I/O 管理器和缓存管理器一起,确保进程可以快速的访问其所需要的数据。如下图1所示:
这里写图片描述

       如上图所示,文件系统从应用程序接收到I/O 请求并调用I/O 管理器或者缓存管理器来处理该请求。I/O 管理器处理设备和应用程序之间的交互。I/O 管理器和应用程序都调用内存管理器来代表驱动映射文件并申请内存以供内部使用。随后如果有对于被映射文件的访问,内存管理器按需处理发生的页面错误。缓存管理器在文件系统和内存管理之间提供了接口以支持fast I/O 和 缓存I/O 。缓存管理器在内核虚拟地址空间中申请内存以映射基于缓存的I/O 访问模式的文件的视图。

虚拟地址空间

       Window Vista 在使用虚拟地址空间方面实现重大的改变使其更加有效,并简化管理, 增加对于更多的处理器和更大的内存配置的扩展性。这些修改很大程度上消除了基于注册表大小,配置,和SKU 之间的区别。虚拟地址空间的改变包括下面几个方面 :
       • 动态申请内核虚拟地址空间。
       • X86 体系结构下的内核模式栈跳转(Kernel-mode stack jumping in x86 architectures)
       • 额外的池内存的使用

内核虚拟地址空间的动态申请

       Windows Vista +,内核虚拟地址空间是动态申请的。根据要求,包括分页和未分页内存池在内的重要的系统资源的大小和位置不再是确定的,而是动态调整的。其结果就是,系统调节是自动的,系统管理员不再像往常一样需要手动重新设置系统以避免资源不平衡。
这里写图片描述
       如上图所示, 在早期windows 的发行版本中,内核虚拟地址空间是静态申请的。资源是在固定的位置以固定的大小申请的。 静态分配对内存池、系统页表和其他系统资源的大小施加人为限制。一些资源(如内存池)的大小是通过注册表或者SKU 设置的。
       在 Windows Vista +。内核虚拟地址空间类似于用户地址空间,是动态申请的。内核虚拟地址空间仅仅受当前体系结构下可以获得的虚拟内存的数量限制。单独的资源的大小和位置可以根据当前系统的要求而动态改变。因此,系统资源不再驻留在虚拟地址空间的固定的位置。        Windows Vista 忽略早先在系统引导时使用的用于判断资源大小的注册表的值。 动态申请的结果是,所有的资源可以被所有的请求者申请。
       比如特定池(分页,未分页等等)和驱动认证这样的功能可以直接开启而不用重新引导系统,因为系统可以动态申请其所需要的空间,而不是在系统引导的时候预申请。 Windows Vista +, 这样的功能在关闭的时候是不需要任何内存的. 因此如果功能不使用的话,就释放了,这在之前的操作系统版本中是做不到的,功能对应的内存都是在系统引导的时候申请的。

x86 体系结构的一些细节

       在32位windows 中,所有的内核虚拟内存地址空间被所有的系统使用的资源共享。 单独的资源没有一个预置大小的限制,除了非分页内存池,被限制为75% 物理内存大小。 一个标准配置引导的 32-bit 系统有整整2GB 的内核虚拟地址空间分享给资源使用。 管理员可以使用bcdedit 命令来增加用户虚拟地址空间的大小,相应的减小内核虚拟地址空间的大小。
       由于当前系统资源的申请是动态的,而不是引导时确定,以3GB 用户地址空间和PAE引导的32-bit 系统现在可以使用全部64GB 内存。之前的32-bit windows 版本相同条件下限制了只能使用16GB内存。
       通过使用内核模式调试器,开发者和管理员可以获得X86体系结构下的内核地址空间的使用信息。!vm 调试命令设置扩展标志值为 0x21 显示内核虚拟地址空间的使用情况,没有进程特定的信息。这个命令可以用来检查地址空间的分裂, 这在32-bit 系统上有更大的影响,因为32-bit 系统很明显有更少的内核虚拟地址空间。
       32-bit 系统中,在极少的情况下,几个分裂可能导致内核虚拟地址空间的枯竭。 32-bit 版本的 Windows Vista SP1 和 Windows Server 2008 支持一系列的注册表键,标志哪个管理员可以限制当前系统的资源的大小。这些注册表键在64-bit 的系统上被忽略。更多信息请参见“Memory Management Registry Keys” on MSDN®.

64-bit 体系结构的细节

       64-bit Windows中, 每个资源可以获得128 GB (除了非分页内存) ,系统拥有1TB 系统缓存,这些不受所在的机器所拥有的物理内存数量的限制。在 Windows Server 2008 非分页池大小限制到75%的物理内存, Windows Vista 和之前的系统,这一数值为40%。
       举例来说,一个拥有512MB 内存的 64-bit 系统。在Windows XP, 分页池大小相应的减小,大概1GB。在拥有1TB 内存的系统上,分页池的大小将特别大,最大为128 GB。在 Windows Vista, 分页池大小一直是128 GB ,无论是 512 MB 或者 1 TB系统,系统忽略物理内存的大小. 管理员不再需要为了一个需要大量分页内存的应用程序而重新配置系统或者改变注册表设置了。

x86 体系结构的内核模式栈跳转

       X86 体系结构下,Windows Vista 通过在递归调用到内核模式的时候更有效的使用内核模式栈空间来更进一步增加内核虚拟地址空间的有效性。当一个线程执行一个Windows API 系统调用并因此转换到32-bit 内核时,Windows Vista 内存管理器通过生成一个16KB 栈空间并将其串联在第一个栈来提供内核模式栈空间。当线程从系统调用展开,内存管理器断开链表并删除栈空间。这个功能称为 “kernel-mode stack jumping”,它减少了线程需要的虚拟地址空间的数量并因此减少线程内存足迹的大小。内核模式栈跳转在单个终端服务客户端中使得内存使用更加有效,因此,它通常使得在每台客户机上可以运行2~4倍的客户端。.
       Figure 3 展示x86体系结构下,Windows Vista和之前的Windows版本在内核模式栈空间使用上的不同。
这里写图片描述
       之前版本的windows, 一个执行系统调用的线程得到一个扩大的,64-KB 虚拟栈 (尽管每一次系统调用只有12 KB可用, 另外有 4 KB 的守护页面)。线程持续持有这个栈空间,直到线程结束,无论它是否继续使用这个栈空间。对于这样的线程来说,如果不执行内核模式代码的话,将不会使用这部分额外栈内存。
       在32-bit Windows Vista 中,每次Win32k.sys 生成一个回调并因此转移到内核时,一个线程每次得到一个附加的 16 KB 栈。当每个相互嵌套的系统调用完成的时候,内存管理器删除绑定的栈。
       如果驱动需要额外的内核模式栈空间,可以调用 KeExpandKernelStackAndCallout 函数。这个函数使一个驱动可以获取自己需要的大小的栈空间,最大到 Ntddk.h 中的宏 MAXIMUM_EXPANSION_SIZE—当调用这个函数的时候,系统将调用一个特定的回调函数。如果当前栈没有足够的空间,系统按需要申请一个或者更多的栈,并在特定的驱动回调函数返回的时候删除它们。. KeExpandKernelStackAndCallout 在64-bit 版本的Windows Server 2003 和 Windows Vista 全体系结构上受支持。

额外的池内存的使用

       当一个驱动模式组件申请超过一个页面的内存的时候,内存管理器当前使用在那个申请的结尾和下一个页面边界之间的内存以满足其它的内存请求。 如图4所示。
       驱动不可以越界访问。驱动检验器已经检查这个错误很多年,因此已经存在的被正确测试的驱动应该不会有问题。驱动开发者应该知道这一变化. 使用超出它所申请的内存的范围的驱动可能会破坏其它驱动申请使用的内存的内容,甚至修改驱动本身的内容。
       Figure 4 展示了 Windows Vista 在申请池内存方面和之前的发行版本的windows 的不同
这里写图片描述
Figure 4. Excess Pool Allocation

安全:地址空间布局随机化 Address Space Layout Randomization

       为了增加系统的安全性,限制缓冲区溢出攻击可能导致的伤害, 在每次DLL 和 EXE 被加载的时候,Windows Vista 都将其加载到不同的地址。这个技术称之为,地址空间布局随机化, 此技术使得流氓程序更难预测特定的API 函数的地址。
       开发则必须选择使用/DYNAMICBASE链接选项来使DLL 和 EXE 使用ASLR。这个选择在镜像头中设置一个标志,暗示这个镜像可以被加载到一个随机的地址。 特别鼓励独立软件提供商 (ISVs) 在他们的产品中来使用这个标志。

ASLR 对于镜像加载地址的影响。

       在Windows Vista, 镜像加载的时候内存管理器从用户高地址空间中的256 个 64-KB 对齐的地址随机选择一个镜像加载偏差值。镜像加载偏差是内存管理器加载支持ASLR 的DLL (标志为支持动态基地址的DLL)的时候为其指定的一个起始地址。同样的镜像加载偏差应用在系统所有的用户模式进程中,因此进程可以分享动态加载的DLL。
       在之前的windows 版本中,内存管理器尝试通过使用DLL 头中的预定义加载地址在所有进程中将同样的DLL 加载在同样的地址 (假设在进程中没有地址空间冲突出现)。因此,当hacker 在Microsoft 或者第三方的产品中发现了栈溢出漏洞,他们可以使用在其中一个PE模块中已经知道的一个特定函数的被链接地址来启动一个攻击行为。
       Figure 5 展示了在一个假想的32-bit Windows Vista 系统下,ASLR 对于镜像加载地址的影响。
这里写图片描述
Figure 5. Effect of ASLR on Load Addresses
       Figure 5中,内存管理器以一个随机选择的镜像加载偏移来加载第一个动态基地址的DLL,之后按照地址空间的工作方式来为剩下的动态基地址的DLL 来绑定地址。假定有足够的虚拟地址空间可以使用,且在进程中没有虚拟地址空间冲突的问题产生,使用/DYNAMICBASE 链接的DLL 被加载到使用它的所有进程的同样的基地址,由此使得代码可以在这些进程中共用。对于可执行镜像来说,内存管理遵循一个简单的模式,选择一个与镜像头中指定的加载基地址相近的,随机选择的64-KB 对齐的地址。进程可以按照它们共享DLL 的方式来共享可执行程序。
       如果一个DLL 或者 可执行镜像被所有使用它的进程卸载,内存管理器不需要在下次加载它的时候仍然加载到相同的地址。相反,内存管理器再次随机选择一个加载地址。
       Windows Vista 加载不支持ASLR 的DLL 的操作跟之前的Windows 版本一样。系统尽量按照镜像头指定的加载地址加载DLL,如果这个地址已经被使用而导致了一个冲突,系统在进程地址空间中由高到低找到一个合适的地址。 如果其它的进程加载同样的DLL,系统加载镜像到进程的相同的地址。如果在之后的进程中会产生冲突,系统迁移之前加载的DLL 到其它的地址,并修正引用此DLL 的其它的模块的导入表。因此潜在的导致一个与DLL 加载类似的“多米诺骨牌效应”。

ASLR带来的好处

       ASLR 通过消除加载地址的可预测性来增加安全性。另外,相比于之前的Windows版本,它将DLL 包装在一个更小的虚拟地址范围中,因此避免了冲突,保存页表空间,给应用程序留下了更多的可用的连续虚拟地址空间。动态基地址的DLL 仍然在使用它们的进程中共享使用。
       在 Windows Vista 和Windows Server 2008 中,ASLR 被应用到用户模式可执行文件和Microsoft提供的DLL 中,并被扩展到内核,硬件抽象层,和Windows Server 2008 的驱动组件中。 驱动是自动动态重定位的,但是用户可执行文件和DLL 必须在链接的时候显式指定链接选项/DYNAMICBASE 才可以。
       当不可执行保护和ASLR一起使用的时,将提供很好的安全性以对抗那些利用应用程序漏洞的恶意程序,比如缓冲区溢出漏洞。单独的ASLR 来对抗栈溢出攻击的话是相对无效的安全方法。 不能判断出系统函数地址的hacker 可以向栈中注入并执行代码。单独的不可执行保护是类似的: 它禁止代码的执行,但是hacker 可以使用已知的系统函数的地址。一起使用的时候,hacker 既不能在栈中执行流氓代码,也不能从一个DLL 中的已知地址来执行代码。
       一般情况下, ASLR 对于性能并没有特别大的影响,因为它是在内核内存管理层实现的。在 32-bit 系统中,应用程序可能因为ASLR 带来的代码共享和地址空间更有效的利用而有轻微的系统性能提升。 尽管如此,在高度拥挤的32-bit 系统中可能会产生性能下降,当我们加载很多的随机镜像文件,这种可能性以及性能影响取决于镜像的大小和质量。

如何创建动态基地址的镜像文件

       默认情况下,ASLR应用在所有的系统DLL 和EXE 中,它们链接的时候都添加了/DYNAMICBASE 标志. ISVs 被推荐使用ASLR。为了创建这样的应用,开发者设置/DYNAMICBASE 链接标志, Microsoft Linker (version 8.00.50727.161 or later) 和Microsoft® Visual Studio® 2005 SP1支持该链接选项. 之前的windows 版本忽略此开关,因此没有向后兼容的问题。
为了同时使用 ASLR 和no-execute,同时指定 /NXCOMPAT 和/DYNAMICBASE.
       更多ASLR 和no-execute protection 信息在MSDN 上查找”Windows Vista ISV Security”

I/O 带宽

       处理器速度和内存大小在过去的短短几年间增加了数量级,但是磁盘的I/O 速度仅仅是加倍了 (尽管当前的一些提升,比如固态硬盘的使用开始改变这一情况)。由于磁盘子系统的速度没有跟上,处理器和内存的提升并没有没完全利用起来 。提升I/O 速度对于Windows Vista 来说是一个重要的挑战,涉及下面列出的一些变化:
       • 微软超级预取Microsoft SuperFetch™
       • 写页面文件
       • 内存管理和缓存管理的协作
       • Prefetch-style clustering
       • 大文件管理Large file management
       • 睡眠和待机Hibernate and standby

Microsoft SuperFetch

       超级预取是在Windows Vista 上有适应能力的页面预取技术,该技术通过分析数据使用模式并根据这些模式预加载数据到内存中。
        当最终用户从事他们的工作,内存管理器记录关于它们的活动的信息。 一个用户模式SuperFetch 服务分析日志,找到使用的模式,然后在系统范围内优先加载常用界面。该服务之后调用内存管理器来按照它们通常的使用情况来在恰当的时机预加载这些页面。当处理器没有因为其它的工作繁忙或者磁盘不是繁忙的情况下,内存管理器工作在一个比较低的优先级上。
       例如,考虑一个经常在周五中午运行计算报表的系统。内存管理器记录该报表程序通常所需要的页面,SuperFetch 服务认为这是经常使用的页面,因此在接近中午的时优先加载它们。结果就是更有效的使用虚拟内存,并给用户更好的体验。
关于 SuperFetch 的更多信息请参见 “Windows PC Accelerators.” SuperFetch is not supported in Windows Server 2008.

Page-File Writes

       Windows Vista 包含许多增强以使得写页面文件更加快速和有效。在 Windows Vista中,内存管理器在每次操作中写更多的数据,包括对齐的数据以及它们的邻居,而且不写纯0 的页面。
       之前的Windows 版本,单个页面文件写操作最大为64 KB。 Windows Vista 取消了这个限制,因此可以一次写更大的簇。写操作现在通常是1MB。
       之前的算法被设计为:将数据尽快的写到磁盘上。在Windows Vista中,目标是即聪明又快速。移动磁盘头之后再进行读写操作是相对比较消耗时间的操作,因此减少这样的操作可以帮助提高I/O 带宽。当内存管理器准备将一个被修改的页面写入到它的后备文件中的时候,它额外检查其附近的页面以判断它们中间有没有同样需要被写入文件的, 而忽略它们是否被取消映射或者从工作集中被修剪。因此一个写操作包含一簇邻近页面,一些页面可能依然在工作集中,一些可能没有。通过这种聚集写的方式,内存管理器帮助磁盘增加数据的邻近性(连续性),另一方面,数据的连续性也使得接下来对于同样的页面的读入操作更加快速。
       另一方面,内存管理器在读写之前会检查零页面。通过内核追踪发现在某些情况下7%~8%的写操作操作的都是零页面。内存管理器并不会占用I/O 带宽来写这些页面(随后可能又需要读这些页面)。内存管理器只是简单的将这些放到“已经置零的页面链表”中,并在页表中标记页面属性为“需要零页面” ,如果对应的虚拟地址随后被访问了,内存管理器从内存中找到一个零化的页面而不是从磁盘中读取一个零页面。
       Windows Server 2008 和Windows Vista SP1 通过在有足够的物理内存的系统上实行延迟“修改页面写磁盘”来节省一部分的I/O 带宽。这些程序不会使用预先确定的修改的页面的数量(脏页面)为界限,而是同时考虑物理内存的数量和磁盘的电源状态来将被修改的页面写到磁盘。这种方法需要更少的磁盘I/O 操作,而且可以通过给笔记本上的磁盘更少的供电来延长笔记本的电池寿命。

内存管理器和缓存管理器的协同工作

       Windows Vista使得内存管理器和缓存管理器之间进行更好的协作以进行写操作。内存管理器和缓存管理器通过它们的工作线程来将被修改页面写入到它们的后备存储中。内存管理器使用两个对应的线程,被修改页面写出线程以及映射页面写出线程。缓存管理器使用几个懒惰写线程,这些线程周期性的将系统缓存中的脏页面排队,以写入到其后备存储中。Windows Vista 协调这些线程的行为,以优化查找操作,减少延迟,最小化重叠的I/O 操作。额外的功能增强提供并行工作,并尽量的避免锁操作
       当被修改的页面的数量,可以获得的页面的数量和已经存在的被缓存的页面的重用都满足要求时,被修改页面写出线程将会把脏页面写出到页面文件中。
       被映射页面写出线程定时将脏页面写出到它们的后备文件中。在Windows Vista 和之后的Windows releases 版本中, 被映射页面写出线程定时清理脏页面列表,并刷新所有的数据到它们的后备存储上。如果系统当前的可用内存比较少,将以更短的时间间隔进行清理工作。在之前的 Windows releases版本,映射页面写出线程每5分钟执行一次。Windows Vista 写脏页面的频率相比之前的Windows 发行版本更加快,写操作往往包含更少的数据。
       缓存管理器的懒惰写线程周期性的从系统缓存中将脏页面排队以写出到它们的后备存储中。懒惰写线程与映射页面写出线程一起协作以一种有序的方式来向文件系统产生写请求。特别的,系统写页面时尽量按照页面的线性顺序来将其写入到后备存储中,而不是基于页面在被修改页面链表中的顺序来写脏页面。这个功能对于比较大的稀疏文件来说是特别有效的。
       考虑一个从1MB 被扩展到200MB 的磁盘文件。一些新页面包含数据但是大部分没有。在之前的Windows 版本中,假设接近文件末尾的一个页面,比如说180MB,可能被第一次修改。 其它的被修改页面的数据被写入到磁盘上中间的块之前,如果系统崩溃可能会导致文件混乱为了避免这种情况,文件系统将把磁盘块的1MB 到 180MB 填充为0。文件系统将独立的写其它的数据块到后备存储,而不考虑单独工作的映射页面写出线程和懒惰写线程发送的请求的顺序。在Windows Vista 中,映射页面写出线程和懒惰写线程协作从而能够按顺序将1MB 到 180 MB 被修改的页面依次进行写操作。所有的这些页面最终都会进行写操作。以线性的顺序来进行写操作就避免了磁盘头的移动,这样操作,使得整体的操作更加有效,另外,这样的操作避免了将介于中间的页面清零这样的相对耗时的操作,因为此时已经可以得到被修改的页面。
       内存管理器现在也支持多个异步刷数据的操作来增加性能。写线程为所有的数据产生多个重叠写请求,之后等待所有这些行为执行完毕。在之前的Windows 版本中,线程顺序产生这些请求,一次只产生一个。在可能的情况下, 异步请求的使用使得写操作可能同时发生。同时,线程在以处理器和内存的速度在执行,而不是因为一个单独的写操作而限制在I/O 速度上。

Prefetch-Style Clustering

       内存管理器预取比较多的页面来满足页面错误和填充系统缓存。预取操作直接将数据读取到系统页面缓存中而不是虚拟内存的工作集中,因此预取操作并不消耗虚拟地址空间,预取操作的大小并不受可以得到的虚拟地址空间的大小的限制,被预取的页面被放到备用列表中,在PTE 中被标志为在转移。如果预取的页面随后被引用了,内存管理器将其增加到工作集中, 然而,如果它从来没有被引用过,释放它就好,并不需要任何的系统资源。
       如果一个预取的页面已经在内存中了,内存管理器并不会再次读取它们。内存管理器将使用一个【傀儡】的页面来代表这个页面,如图6 所示。
这里写图片描述
       Figure 6. Prefetch-style clustering
       在Figure 6中, 文件偏移和虚拟地址与页面A,Y,Z,B绑定,虽然虚拟地址逻辑上是邻居,但是物理页面并不一定是邻居。页面A 和 B 当前没有在内存中,因此内存管理器必须读取他们。页面Y 和 Z 已经在内存中了,没有必要读取它们。实际上,自从它们最后从它们的后备存储中被读取进来到内存中,他们可能已经被修改了,这种情况下,重写它们的内容可能导致一系列的问题。尽管如此,在一个单独的操作中读取A 和 B 页面相比 读取A 然后 读取B 是更有效率的。因此,内存管理器从后备存储中产生一个单一的读取请求包含了所有的页面(AYZB)。基于可获得的内存的数量,当前系统使用的内存的状况等等,这样的读取请求包含尽量多的对于读请求有意义的页面。       当内存管理器为了一个请求建立MDL 的时候,它提供了A 和 B 的合法指针。尽管如此,页面Y 和 Z 的项指向一个单一的system-wide 傀儡页面X。内存管理器可以使用后备存储的可能相对陈旧数据来填充虚拟页面X ,因为它并不使X 可视。然而,如果一个组件访问MDL 中的Y 和 Z 的偏移,它看见假的页面X 而不是 Y 和 Z。.
       内存管理器可以将任意数量的被释放的页面表示为一个单一的傀儡页面,这个页面可以被多次植入到同一个MDL 甚至多个同时存在的不同驱动使用的MDL。然而,被抛弃的页面的位置的内容可以随时改变。
       预取风格的聚集可能影响那些直接引用MDL 中的指针进行读操作的驱动的操作。 驱动开发者不应该假设MDL 所描述的页面的顺序或者内容,且不能依赖于MDL 所引用的任何的任何位置对应的数据的值。通常来说,驱动开发者并不直接引用MDL 中的内存位置来获得数据,因此这个限制只影响少数的驱动。
       执行解码或者计算验证和的驱动,它们的操作是基于MDL 映射的页面的数据的值的,它们不可以从系统提供的MDL 解引用指针以访问数据(可能被释放掉之类的操作)。取而代之,为了确保正确的操作,这样的驱动应该创建一个临时的MDL,这个MDL 基于驱动从I/O 管理器接收到的系统提供的MDL(锁定它的内容,确保其内容的同时确保其不会被置换到后备文件中). 驱动条目 “What Is Really in That MDL?” 概述了这样的驱动应该遵守的步骤。
       如果MDL 描述了一个直接I/O 写操作对应的缓冲区,产生这个I/O 请求的应用程序可能映射了同样的物理页面到它的地址空间中。如果这样的话, 在驱动检查内存时候应用程序可能同时修改数据。驱动必须通过创建一个临时的MDL 来双缓冲它的内容以查看它的快照正确的处理这种情况。
       在典型的I/O 操作中使用MDL 但是没有底层页面的数据的驱动没有必要创建临时MDL。在内部,内存管理器一直跟踪被保留的所有的页面,并监控它们如何被映射。当一个驱动将一个MDL 传给系统函数以执行I/O 操作,内存管理器其使用的是正确的数据。
       预取风格的聚集之前在Windows XP 上使用的时候只在有限的地方使用, Windows Vista 在整个操作系统范围更普遍的使用这个技术,以更好的提升系统性能,提供更高的性能优势。

大文件管理

       Windows Vista 为大的稀疏文件提供了更好的性能。Windows Vista 内存管理器通过AVL 树来描述大型的稀疏文件所跨越的磁盘块的范围。AVL 树是一个自平衡的二叉树,对其进行操作比早期的Windows 使用的链表更有效。连接的链表需要一个线性的遍历来遍历文件的所有节。
一个AVL 树极大的增加了那些使用文件偏移进行映射,刷新,和清除大文件的系统函数的速度。因此,现在的备份速度常常是Windows XP 时代的两倍。

休眠和待机

       Windows Vista 和之后的windows 发行版本,休眠和待机相对于之前的windows版本来说是更加快速有效的。当前系统通过下面了两步来执行休眠和待机:
1. 拷贝物理内存的内容到磁盘上的休眠文件中。所有的设备栈都在活动。
2. 关闭除了在休眠路径上的所有的设备栈。仅仅拷贝被修改(相对步骤1时的内存状态)的数据到休眠文件上。

       休眠和待机现在使用与容错系统相同的内存管理监控技术。内存管理器在所有的设备栈都活动的时候拷贝物理内存的内容到磁盘上。因此,拷贝操作将受益于更大的I/O 大小,分散/聚集 的直接内存访问 (DMA),和其它先进有效的I/O 技术来为休眠存储数据。 这样的技术包括预取支持,有了它就可以在需要的时候将快速恢复所需要的页面读取到内容并包含在休眠文件中。
       初始的拷贝操作完成后,系统关闭除了休眠路径所在的设备栈的所有设备栈。之后仅仅拷贝自从第一次拷贝以来产生改变的页面到休眠文件中。休眠不再像之前的操作系统一样单纯的写页面缓存,系统明智的根据缓存的用途来写被缓存的数据到休眠文件中。休眠操作将聚集数据然后再进行写操作,一次写操作的大小是之前大小的两倍。因此,休眠和待机更加的快速,而用户不需要知道它们之间的不同。对于相同的系统快照来说,总体休眠时间大于是之前的Windows 版本的两倍,而休眠文件的大小只有一半。

Advanced Video Model

       Windows Vista and 和之后的Windows release 版本的新的视频体系结构更好更多的使用现代的GPU 和虚拟内存为游戏和模拟提供更逼真的着色,纹理,褪色, 和其它的视频效果。
       为了支持视频体系结构,内存管理器提供 一个新的映射类型称为 旋转虚拟地址描述符。rotate virtual address descriptors (VADs). Rotate VADs 使得视频驱动可以快速转换用户视图,从常规的应用内存到缓存的,非缓存的,或者组合写的图形加速接口或者以每页为基础的视频内存, 同时完整支持所有的缓存属性。以这种方式,视频体系结构可以直接使用GPU 来传输数据以达到更好的性能,按需将不需要的页面转入或者转出。Figure 7 展示如何将一个虚拟地址映射到一个常规的物理内存或者图形内存内存。
这里写图片描述
Figure 7. Rotate Virtual Address Descriptors
       在图 7 中, 用户虚拟地址空间中的一个地址对应页表中的一个页表项,该页表项可以引用或者视频RAM 或则 AGP 或者文件中的一个页面。为了转换视图,视频驱动简单的提供一个新的地址。这个技术使得视频驱动可以使用GPU 来执行直接的传输,因此可以比之前的视频模式性能提升100 倍。

可扩展性Scalability

        随着Windows 运行在更大更能力的机器上,微软持续提高系统的可扩展性以使用更多更快的处理器和内存。

Efficiency and Parallelism

       由于内存管理进行了大量的内部改进,内存分配现在需要更少的I/O 操作和更少的锁以实现最佳吞吐量。
       在内部,内存管理器使用位图而不是链表来追踪非分页内存池的空闲页面。不像链表,在搜索位图的时候不需要锁,由此减少了对应的锁50%的竞争率。此外,位图提供了对于连续的空闲页面的自动收集。 Windows Server 2008 也使用位图来描述系统PTE。
       大的共享的集合现在直接被映射而不是通过hash。当一个hash 表中的项特别多的时候,将经常出现冲突的现象,除非hash 可以动态的改变其大小。然而,在大集合上,修改大小将非常耗费时间。在这种情况下,直接的映射是更加有效的。
       在Windows Server 2008,对于连续的物理内存的申请速度有了很大的提高。 对于申请连续内存的请求常常是成功的,因为内存管理器现在动态替换页面,常常不需要修剪工作集或者执行I/O 操作。另外,更多类型的页面,比如内核栈和文件系统元数据页面还有其它页面—是被替换的候选页面。结果就是,在任何时间,可以得到更多连续的内存,另外,用来进行这样的申请所消耗的资源被大大降低了。

Page-Frame Number and PFN Database

       PFN 数据库包含机器上所有物理内存的信息。 64-bit 版本的Windows Vista SP1 和Windows Server 2008,PFN 是 64 bits 长以支持大量的内存和 NUMA 体系结构,在NUMA 上,物理地址空间有时被内存稀疏的填充。
       在之前的Windows 版本中, 无论何时我们需要一个页面的时候,内存管理器获得PFN 锁并根据PFN 数据库来从恰当的链表移除一个新的页面。于此不同, Windows Vista为每一个NUMA 节点和页面颜色维护了一个小的链表包含立即可以得到的零化并空闲的页面 (页面颜色是一种技术,内存管理器用来使用以减少页面之间的高速缓存线路碰撞的可能性) 在很多情况下,特别是要求0的页面错误和拷贝写错误,系统现在可以在不获得锁的前提下得到一个空闲页面。降低自旋锁的获得次数降低了潜在的在其它处理器上死锁的可能性,因此提高了并发性。

Large Pages

       Windows Server 2003 向用户应用程序引入了大页面。Windows Vista 和Windows Server 2008 内部更广泛的使用大页面并加强了对于它们的支持。Windows Vista 和Windows Server 2008 在下面列举的方面支持大页面:
       • Initial nonpaged pool。
       • PFN database
       • User application and driver images
       • Page file-backed shared memory
       • User-mode VirtualAlloc allocations
       • Driver I/O space mappings

       X86 系统下,用户模式应用程序可以通过在调用VirtualAlloc 函数并传入MEM_LARGE_PAGES 标志申请4MB 大小的页面。表1 列举出了不同Windows 硬件平台支持的大页面大小。

Architecture Large page size x86 4 MB x86 with PAE enabled 2 MB x64 2 MB Itanium 16MB

       应用程序可以调用 GetLargePageMinimum 来判断当前大页面的大小。
       Windows Vista 内存管理器 比之前的Windows 版本更快地分配大页面的范围。而且整个范围不再需要是连续的,因此申请大页面更有可能成功而不太可能导致页面抖动。例如,如果一个应用程序申请了10MB 的大页面,Windows Vista 和之后的Windows 发行版本可能申请5个大页面,每个2MB (如果大页面在当前的硬件平台下是2MB) 而不是寻找10MB 物理连续内存。

Cache-Aligned Pool Allocation

       Windows Vista实现了对于高速缓存对齐的池申请的支持。驱动可以在调用ExAllocatePoolXxx 函数时指定下面的标志以请求高速缓存对齐的内存:
• NonPagedPoolCacheAligned
• PagedPoolCacheAligned

       这些标志是在早期的Windows 发行版本中已经被定义的,但是它们被忽略了。

Virtual Machines

       效率和扩展性不仅仅对于好的服务器性能来说比较重要,它们对于Windows 在虚拟化系统中作为客户系统运行也是至关重要的。Windows Vista 包含几个改动以增加虚拟机方案的性能。
       TLB缓存从VA 到 PA 的转换,因此处理器可以快速访问该信息。如果一个地址不能从TLB 中得到,处理器经常必须经过几次内存引用,这几个操作是非常消耗时间的。因此,整个系统的性能随着TLB 命中率的降低而降低。
       一种增加地址在TLB 中的概率的方法是更少的刷新TLB。每次一个页面转换为非法的时候,它的项被从TLB 中刷新。一个页面会由于“取消映射,释放,从工作集剪枝,或者被拷贝写操作修改,或者其它操作”变为非法。当页面的保护属性或者缓存属性被改变的时候,该项也必须被刷新。
       刷新所有处理器的整个TLB是相对费时的操作,它需要巨大的操作系统开销。而且,在TLB被刷之后,它们必须被重新构成。 Windows Vista 极少刷新整个TLB。其结果就是虚拟机可以更有效的执行。
       如果一个虚拟机宿主里面运行了好几个客户机操作系统,客户机的大小会限制客户机管理程序的性能并限制其可扩展性。为了使用更好的内存足迹并因此成为一个更好的客户机操作系统, Windows Server 2008 释放具有投机性分配的不需要的内存。具体来说,系统从初始化的非分页内存池中回收没有被使用的内存。

Load Balancing

       Windows Vista 导出下面的新事件来帮助负载平衡:
       • LowCommitConditionNotification
       • HighCommitConditionNotification
       • MaximumCommitConditionNotification

       LowCommitConditionNotification内存使用率比较低,有大量的内存可以使用。
       HighCommitConditionNotification 内存使用率比较高,可用内存比较少。如果适当的内存空间可以获得,系统自动增加页面文件的大小以获得跟多的内存,直到达到管理员的限制。短期的解决方法就是降低当前系统的负载。长期的解决方法就是增加最低限度的页面文件大小或者增加内存。
       MaximumCommitConditionNotification内存使用特别特别高,只剩下及其少数的内存可以使用。系统由于当前管理员的设置限制而不能再增加页面文件的大小了。系统管理员是可以在不重启计算机的情况下增加页面文件的数量或者大小的,只要可以获得的磁盘空间是足够的。其它的可行行为有,增加最小或者最大的页面文件的大小,增加内存,或者减少负载。
        这些事件支持Windows Server 2003 添加的池通知事件。驱动和其它的内核模式组件可以注册这些事件。关于内存相关的通知事件请参阅”Standard Event Objects”。

Additional Optimizations

        额外的内存管理优化包含下面的几个方面:
        • VirtualAlloc 和address windowing extensions (AWE) 申请。
        • VirtualProtect 函数。
        • WOW64。

        Windows 获得一个进程相关的地址空间锁以同步用户地址空间的修改。 在 Windows Vista, 这个锁同时支持共享和独占访问,之前的Windows 版本只支持独占访问。一因此,如 VirtualAlloc 和 VirtualQuery 等许多操作可以并行执行。总体来看, 在一些场景下,VirtualAlloc 的修改使得AWE 申请所需要的时间降低了将近2500%。
        VirtualProtect 函数修改虚拟内存的一部分页区域的访问保护属性。当一个页面的访问保护属性被修改,进程必须刷新对应的TLB 项。Windows Server 2008 产生一个单独的刷新请求到所有 TLB 可能包含该项的处理器,而不是给所有的处理器都发送刷新请求 。通过这样的操作, VirtualProtect 可以以比之前的Windows 版本快60 倍的速度来修改页面的访问保护属性。
        在 64-bit 体系结构下, Windows Vista 使用需要零内存代替池内存来为32-bit 二进制仿真程序申请页表位图。这个改变使得32-bit 仿真程序运行的更加快速,因为它们需要更小的系统内存足迹并执行更少的I/O 操作。

System Integrity

        通过online crash analysis (OCA),用户可以上传系统崩溃的相关数据到 Microsoft。这些信息足以检测通常的导致系统崩溃的原因,并使得系统检测和处理潜在的系统破坏的能力得以增强。 Windows Vista 和Windows Server 2008 在以下领域提升性能以提高系统的完整性:
        • 硬件错误的诊断Diagnosis of hardware errors
        • 代码完整性和驱动签名Code integrity and driver signing
        • Bug check 期间的数据保存Data preservation during bug checks

Diagnosis of Hardware Errors

       之前”Page-File Writes” 提到过,Windows 维护了一个零化页面的链表。硬件错误,比如DMA 传输错误和单个bit 错误,在页面被初始化为0 之后可能损坏内存,因此Windows Vista 检查这个链表以确保这些页面的内容确实为0。如果系统找到一个错误,它记录发生错误的物理内存的地址并记录错误的属性到事件日志中。这些信息帮助我们确定由硬件错误导致的single-bit 错误。
       经常出现这样的错误的机器通常比较容易产生应用挂起或者崩溃的现象,而这样的错误是非常难追踪的。OCA 数据显示,这样的错误比之前的预测的更为普遍。Independent hardware vendors (IHVs) 可以通过使用错误诊断代码内存(error-correcting code (ECC) memory)帮助诊断并避免这样的错误。

代码完整性和驱动签名(Code Integrity and Driver Signing)

       内存管理器实现简单,快速的技术来验证镜像的代码完整性。在X64 系统上这个功能强制执行内核模式驱动的强制代码签名。
       更多关于代码签名的内容请参见: “Digital Signatures for Kernel Modules on x64-based Systems Running Windows Vista,” “Kernel-Mode Code Signing Walkthrough,” and “Summary of Windows Driver Signing Requirements.”
       Windows Vista在system-wide 和session drivers 上支持热更新, 可以在不重新引导系统的前提下进行补丁安装。因此,只要安全补丁是可获得的,就可以安装该补丁并从中受益,而不必等到系统重新引导。

Data Preservation during Bug Checks

       在发生某些无损bug checks的时候,Windows Vista 比之前的Windows 版本保留更多的数据。例如,如果bug check 产生在系统对某些内核模式组件上进行页面读入的时候,因为组件缺失某些信息,系统无法继续进行,但是已经存在于系统缓存的数据是不受影响的。为了避免数据丢失,内存管理器将系统缓冲中所有的被修改页面写出到它们的后备存储中 (经常是一个磁盘文件) 之后产生一个bug check。只有内核模式的代码或者数据错误是比较严重的错误。用户进程的代码或者数据的页面错误 很少导致应用程序的异常。
       为了更好的保护系统数据, Windows Vista 支持将系统缓存视图标记为只读的能力。注册表使用这个功能来避免意外的驱动崩溃导致的破坏。因此,注册表是只读的,除非当它主动进行修改except when it is actively being modified?
       驱动开发者可以使用新的 .pagein 调试命令来查看已经被置换到页面文件的内核模式内存地址的内容。关于这个调试命令的更多信息请参见“see Debugging Tools for Windows”

What You Should Do

       这篇文章中所介绍的大部分的内存管理器的功能加强是内部的,对于管理员,软件开发者和硬件厂商了来说是透明的。尽管如此,我们需要清除系统做出的一些改变,另外我们需要一些对应的操作以获得更大的好处并最终提高用户体验。

对于硬件厂商来说

       • Use ECC.

对于驱动开发人员来说

       • 不要访问自己申请的内存之外的数据,这个错误可以通过驱动程序检验器来检查。
       • 在直接使用MDL 来访问内存的驱动中,正确处理假页面。
       • 在需要的时候使用KeExpandKernelStackAndCallout 来获得额外的栈空间。
       • 在驱动中使用新的 NUMA-感知函数,这些函数能够感知NUMA 体系结构,如果中断关联对于你的设备来说是比较重要的话,指定它。
       • 如果你的驱动申请“可以在操作过程中释放”的内存的话,使用事件通知系统负载。
       • 注意Windows Vista +,尤其是X64 结构下的驱动签名需要。
       • 使用.pagein 调试命令来检查被置换处内存的内核模式数据。

对于应用程序开发人员来说

• 对于Windows Vista + 的操作系统中运行的程序,通过设置/DYNAMICBASE and /NXCOMPAT链接标志来启动ASLR 和no-execute protection
• 要意识到,内存申请的默认NUMA 节点不再是当前节点,而是空闲节点。
• 使用新的NUMA 感知的系统函数来控制内存申请操作并获取分析NUMA 体系结构中的页面位置。

对于系统管理员来说

• 理解内核虚拟地址的动态分配,之后就可以修改系统的调整行为,或者避免完全的调整行为。
• 使用!vm 21 调试命令来研究32-bit 系统上的内核虚拟地址空间的细节。
• 检查系统事件日志中是否有零化页面损坏错误,如果可能的话,随时上传崩溃数据到OCA。

相关话题的介绍

MSDN:
Windows Vista ISV Security
http://msdn2.microsoft.com/en-us/library/bb430720.aspx
Memory Management Registry Keys
http://msdn2.microsoft.com/en-us/library/bb870880.aspx
Windows Driver Kit:
http://msdn2.microsoft.com/en-us/library/aa972908.aspx
Kernel-Mode Driver Architecture Design Guide
Memory Management
Interrupt Affinity and Priority
Standard Event Objects
Kernel-Mode Driver Architecture Reference
Standard Driver Routines
Driver Support Routines
Driver Development Tools
Boot Options for Driver Testing and Debugging
Network Design Guide
NDIS MSI-X
Windows Hardware and Driver Central:
Driver Signing Requirements for Windows [home page]
http://www.microsoft.com/whdc/winlogo/drvsign/drvsign.mspx
Digital Signatures for Kernel Modules on Systems Running
Windows Vista
Summary of Windows Kernel-Mode Driver Signing Requirements
Windows PC Accelerators: Performance Technology for Windows Vista
http://www.microsoft.com/whdc/system/sysperf/accelerator.mspx
What Is Really in That MDL?
http://www.microsoft.com/whdc/driver/tips/mdl.mspx
Interrupt Architecture Enhancements in Windows
http://www.microsoft.com/whdc/system/bus/PCI/MSI.mspx
NUMA I/O Optimizations
http://download.microsoft.com/download/a/f/d/afdfd50d-6eb9-425e-84e1-b4085a80e34e/SVR-T332_WH07.pptx
Microsoft TechNet:
Inside the Windows Vista Kernel: Part 3 (April 2007)
http://www.microsoft.com/technet/technetmag/issues/2007/04/VistaKernel/
Book:
Windows Internals, Fourth Edition,
Russinovich, Mark, and David A. Solomon. Redmond, WA: Microsoft Press, 2005
http://www.microsoft.com/MSPress/books/6710.aspx

阅读全文
0 0
原创粉丝点击