Windows核心编程013.pdf
《Windows核心编程013.pdf》由会员分享,可在线阅读,更多相关《Windows核心编程013.pdf(24页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、下载第1 3章Wi n d o w s的内存结构操作系统使用的内存结构是理解操作系统如何运行的最重要的关键。当开始对一个新的操作系统进行操作时,你会想到一系列的问题。比如,“如何在两个应用程序之间共享数据呢?”“系统将要查找的信息存放在什么地方呢?”“如何使我的程序能够更加有效地运行呢?”等等。很好地理解系统如何管理内存,可以帮助你更快和更准确地地回答这些问题。本章将要介绍M i c r o s o f t公司的Wi n d o w s操作系统使用的内存结构。13.1 进程的虚拟地址空间每个进程都被赋予它自己的虚拟地址空间。对于 3 2位进程来说,这个地址空间是4 G B,因为3 2位指针可以
2、拥有从0 x 0 0 0 0 0 0 0 0至0 x F F F F F F F F之间的任何一个值。这使得一个指针能够拥有4 294 967 296个值中的一个值,它覆盖了一个进程的4 G B虚拟空间的范围。对于6 4位进程来说,这个地址空间是 1 6 E B(1 01 8字节),因为6 4位指针可以拥有从 0 x 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0至0 x F F F F F F F F F F F F F F F F之间的任何值。这使得一个指针可以拥有18 446 744 073 709 551 616个值中的一个值,它覆盖了一个进程的1 6 E B虚拟空间的
3、范围。这是相当大的一个范围。由于每个进程可以接收它自己的私有的地址空间,因此当进程中的一个线程正在运行时,该线程可以访问只属于它的进程的内存。属于所有其他进程的内存则隐藏着,并且不能被正在运行的线程访问。注意在Windows 2000中,属于操作系统本身的内存也是隐藏的,正在运行的线程无法访问。这意味着线程常常不能访问操作系统的数据。Windows 98中,属于操作系统的内存是不隐藏的,正在运行的线程可以访问。因此,正在运行的线程常常可以访问操作系统的数据,也可以破坏操作系统(从而有可能导致操作系统崩溃)。在Windows 98中,一个进程的线程不可能访问属于另一个进程的内存。前面说过,每个进
4、程有它自己的私有地址空间。进程 A可能有一个存放在它的地址空间中的数据结构,地址是0 x 1 2 3 4 5 6 7 8,而进程B则有一个完全不同的数据结构存放在它的地址空间中,地址是0 x 1 2 3 4 5 6 7 8。当进程A中运行的线程访问地址为0 x 1 2 3 4 5 6 7 8的内存时,这些线程访问的是进程A的数据结构。当进程B中运行的线程访问地址为 0 x 1 2 3 4 5 6 7 8的内存时,这些线程访问的是进程B的数据结构。进程A中运行的线程不能访问进程B的地址空间中的数据结构。反之亦然。当你因为拥有如此大的地址空间可以用于应用程序而兴高采烈之前,记住,这是个虚拟地址空间
5、,不是物理地址空间。该地址空间只是内存地址的一个范围。在你能够成功地访问数据而不会出现违规访问之前,必须赋予物理存储器,或者将物理存储器映射到各个部分的地址空间。本章后面将要具体介绍这是如何操作的。第三部分内 存 管 理13.2 虚拟地址空间如何分区每个进程的虚拟地址空间都要划分成各个分区。地址空间的分区是根据操作系统的基本实现方法来进行的。不同的Wi n d o w s内核,其分区也略有不同。表1 3-1显示了每种平台是如何对进程的地址空间进行分区的。表13-1 进程的地址空间如何分区分区3 2位Wi n d o w s3 2位Wi n d o w s6 4位Wi n d o w sWind
6、ows 982 0 0 0(x 8 6和2000(x86 w/32 0 0 0(A l p h a和A l p h a处理器)G B用户方式)I A-6 4处理器)N U L L指针分0 x 0 0 0 0 0 0 0 00 x 0 0 0 0 0 0 0 00 x00000000 000000000 X 0 0 0 0 0 0 0 0配的分区0 x 0 0 0 0 F F F F0 x 0 0 0 0 F F F F0 x00000000 0000FFFF0 x 0 0 0 0 0 F F FD O S/1 6位无无无0 x 0 0 0 0 0 1 0 0 0Wi n d o w s应用程0
7、 x 0 0 3 F F F F F序兼容分区用户方式0 x 0 0 0 1 0 0 0 00 x 0 0 0 1 0 0 0 00 x00000000 000100000 x 0 0 4 0 0 0 0 00 x 7 F F E F F F F0 x B F F E F F F F F0 x000003FF FFFEFFFF0 x 7 F F F F F F F6 4-K B0 x 7 F F F 0 0 0 00 x B F F F 0 0 0 00 x 0 0 0 0 0 3 F F F F F F 0 0 0 0无禁止进入0 x 7 F F F F F F F0 x B F F F F
8、 F F F0 x 0 0 0 0 0 3 F F F F F F F F F F共享内存映射无无无0 x 8 0 0 0 0 0 0 0文件(MMF)内0 x B F F F F F F F核方式0 x 8 0 0 0 0 0 0 0 00 x C 0 0 0 0 0 0 00 x00000400 000000000 x C 0 0 0 0 0 0 00 x F F F F F F F F0 x F F F F F F F F0 xFFFFFFFFF FFFFFFF0 x F F F F F F F F如你所见,3 2位Windows 2000的内核与6 4位Windows 2000的内核拥
9、有大体相同的分区,差别在于分区的大小和位置有所不同。另一方面,可以看到 Windows 98下的分区有着很大的不同。下面让我们看一下系统是如何使用每一个分区的。注意M i c r o s o f t公司正在积极开发6 4位Windows 2000。但是当我撰写本书时,该系统仍在开发之中。应该使用本书中关于6 4位Windows 2000的信息,将它们用于你的当前项目的设计和实现中。不过应该知道,等到6 4位Windows 2000上市时,本章中介绍的一些详细信息很可能已经发生了变化。至于 I A-6 4(6 4位I n t e l结构)的内存管理,分区和系统页面大小的特定虚拟地址范围也有可能变
10、更。13.2.1 NULL指针分配的分区适用于Windows 2000和Windows 98进程地址空间的这个分区的设置是为了帮助程序员掌握 N U L L指针的分配情况。如果你的进程中的线程试图读取该分区的地址空间的数据,或者将数据写入该分区的地址空间,那么C P U就会引发一个访问违规。保护这个分区是极其有用的,它可以帮助你发现 N U L L指针的分配情况。C/C+程序中常常不进行严格的错误检查。例如,下面这个代码就没有进行任何错误检查:300计计第三部分 内 存 管 理下载如果m a l l o c不能找到足够的内存来满足需要,它就返回 N U L L。但是,该代码并不检查这种可能性,
11、它认为地址的分配已经取得成功,并且开始访问 0 x 0 0 0 0 0 0 0 0地址的内存。由于这个分区的地址空间是禁止进入的,因此就会发生内存访问违规现象,同时该进程将终止运行。这个特性有助于编程员发现应用程序中的错误。13.2.2 MS-DOS/16位Wi n d o w s应用程序兼容分区仅适用于Windows 98进程地址空间的这个4 M B分区是Windows 98需要的,目的是维护M S-D O S应用程序与1 6位应用程序之间的兼容性。不应该试图从3 2位应用程序来读取该分区的数据,或者将数据写入该分区。在理想的情况下,如果进程中的线程访问该内存,C P U应该产生一个访问违规
12、,但是由于技术上的原因,M i c r o s o f t无法保护这个4 M B的地址空间。在Windows 2000中,1 6位M S-D O S与1 6位Wi n d o w s应用程序是在它们自己的地址空间中运行的,3 2位应用程序不会对它们产生任何影响。13.2.3 用户方式分区适用于Windows 2000和Windows 98这个分区是进程的私有(非共享)地址空间所在的地方。一个进程不能读取、写入、或者以任何方式访问驻留在该分区中的另一个进程的数据。对于所有应用程序来说,该分区是维护进程的大部分数据的地方。由于每个进程可以得到它自己的私有的、非共享分区,以便存放它的数据,因此,应用
13、程序不太可能被其他应用程序所破坏,这使得整个系统更加健壮。Windows 2000在Windows 2000中,所有的.e x e和D L L模块均加载这个分区。每个进程可以将这些D L L加载到该分区的不同地址中(不过这种可能性很小)。系统还可以在这个分区中映射该进程可以访问的所有内存映射文件。Windows 98在Windows 98中,主要的Wi n 3 2系统D L L(K e r n e l 3 2.d l l,A d v A P I 3 2.d l l,U s e r 3 2.d l l和G D I 3 2.d l l)均加载共享内存映射文件分区中。.e x e和所有其他D L L
14、模块则加载到这个用户方式分区中。所有进程的共享 D L L均位于相同的虚拟地址中,但是其他D L L可以将这些D L L加载到用户方式分区的不同地址中(不过这种可能性不大)。另外,在Windows 98中,用户方式分区中决不会出现内存映射文件。当我最初观察3 2位进程的地址空间的时候,我惊奇地发现可以使用的地址空间还不到我的进程的全部地址空间的一半。难道内核方式分区真的需要上面的一半地址空间吗?实际上回答是肯定的。系统需要这个地址空间,供内核代码、设备驱动程序代码、设备 I/O高速缓存、非页面内存池的分配和进程页面表等使用。实际上 M i c r o s o f t将内核压缩到这个2 G B空
15、间之中。在6 4位Windows 2000中,内核终于得到了它真正需要的空间。1.在x86 的Wi n d o w s 2 0 0 0中获得3 G B用户方式分区多年来,编程人员一直强烈要求扩大用户方式的地址空间。为了满足这个需要,M i c r o s o f t允许x 8 6的Windows 2000 Advanced Server版本和Windows 2000 Data Center版本将用户方式分区扩大为3 G B。若要使所有进程都能够使用 3 G B用户方式分区和 1 G B内核方式分区,必须将/3 G B开关附加到系统的B O O T.I N I文件的有关项目中。表1 3-1中的“
16、3 2位Windows 2000(x 8 6w/3 G B用户方式)”这一列显示了使用3 G B开关时它的地址空间是个什么样子。在M i c r o s o f t添加/3 G B开关之前,应用程序无法看到设置了高位的内存指针。一些有创意的编程员自己将这个高位用作一个标志,这个标志只对他们的应用程序具有意义。这时,当应用程序访问内存地址时,运行的代码将在内存地址被使用之前清除该指针的高位。可以想象,第 1 3章Wi n d o w s的内存结构计计301下载当应用程序在3 G B的用户方式环境中运行时,该应用程序转眼之间就会运行失败。M i c r o s o f t不得不提出一个解决方案,以
17、便使该应用程序能够在3 G B环境中运行。当系统准备运行一个应用程序时,它要查看该应用程序是否与/L A R G E A D D R E S S AWA R E链接程序开关相链接。如果是链接的,那么应用程序就声称它并没有对内存地址执行什么特殊的操作,并且完全准备充分利用3GB 用户方式地址空间。另一方面,如果该应用程序没有与/L A R G E A D D R E S S AWA R E开关相链接,那么操作系统将保留0 x 8 0 0 0 0 0 0 0至0 x B F F F F F F F之间的1 G B区域。这可以防止在已经设置了高位的内存地址上进行内存分配。注意,内核已经被紧紧地压缩到
18、了一个 2 G B的分区中。当使用3 G B的开关时,内核勉强地被放入一个1 GB的分区中。使用/3 G B的开关,可以减少系统能够创建的线程、堆栈和其他资源的数量。此外,系统最多只能使用1 6 G B的R A M,而通常情况下最多可以使用6 4 G B的R A M,因为内核方式中没有足够的虚拟地址空间可以用来管理更多的 R A M。注意当操作系统创建进程的地址空间时,需要检查一个可执行的 L A R G E A D D R ES S AWA R E标志。对于D L L,系统则忽略该标志。在编写 D L L时,必须使之能够在 3G B用户方式分区中正确地运行,否则它们的行为特性是无法确定的。2
19、.在6 4位Windows 2000中获得2 GB用户方式分区M i c r o s o f t发现许多编程人员需要尽可能迅速而方便地将现有的 3 2位应用程序移植到6 4位环境中去。但是,在许多源代码中,指针被视为 3 2位值。如果简单地重新编写应用程序,就会造成指针被截断的错误和不正确的内存访问。然而,如果系统能够确保不对 0 x 0 0 0 0 0 0 0 0 7 F F F F F F F以上的内存地址进行分配,那么应用程序就能很好地运行。当较高的3 3位是0时,将6 4位地址截断为3 2位地址,不会产生任何问题。通过在地址空间范围内运行应用程序,而这个地址空间范围将进程的可用地址空间
20、限制为最低的G B,那么系统就能够确保这一点。默认情况下,当启动一个6 4位应用程序时,系统将保留从 0 x 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0开始的所有用户地址空间。这可以确保在底部的 2GB 64位地址空间中进行所有的内存分配。这就是地址空间的范围。对于大多数应用程序来说,这个地址空间足够了。若要使 6 4位应用程序能够访问它的全部4 TB(terabyte)用户方式分区,该应用程序必须使用/L A R G E A D D R E S S AWARE 链接开关来创建。注 意当操作系统创建进程的6 4位地址空间时,要检查一个可执行文件的L A R G E A D D
21、R E S S AWA R E标志。如果是D L L,那么系统将忽略该标志。编写D L L时,必须使之能够在整个4 TB用户方式分区中正确地运行,否则它们的行为特性将无法确定。13.2.4 64 KB禁止进入的分区仅适用于Windows 2000这个位于用户方式分区上面的64 KB分区是禁止进入的,访问该分区中的内存的任何企图均将导致访问违规。M i c r o s o f t之所以保留该分区,是因为这样做将使得M i c r o s o f t能够更加容易地实现操作系统。当将内存块的地址和它的长度传递给Wi n d o w s函数时,该函数将在执行它的操作之前使内存块生效。可以很容易创建类似
22、下面这个代码(在3 2位Windows 2000系统上运行):302计计第三部分 内 存 管 理下载对于Wr i t e P r o c e s s M e m o r y这样的函数来说,写入的内存区是由内核方式代码来使之生效的,该代码能够成功地访问内核方式分区中的内存(3 2位系统上0 x 8 0 0 0 0 0 0 0以上的地址)。如果在0 x 8 0 0 0 0 0 0 0地址上存在内存,上面的函数调用就能成功地将数据写入只应该由内核方式代码访问的内存。为了防止出现这种情况,并使这个内存区迅速生效,M i c r o s o f t选择的办法是使该分区始终保持禁止进入的状态。只要试图读取
23、或写入该分区中的内存,就一定会导致访问违规。13.2.5 共享的M M F分区仅适用于Windows 98这个1 G B分区是系统用来存放所有 3 2位进程共享数据的地方。例如,系统的动态链接库K e r n e l 3 2.d l l、A d v A P I 3 2.d l l、U s e r 3 2.d l l和G D I 3 2.d l l等,全部存放在这个地址空间分区中,因此,所有3 2位进程都能很容易同时访问它们。系统还为每个进程将 D L L加载相同的内存地址。此外,系统将所有内存映射文件映射到这个分区中。内存映射文件将在第 1 7章中详细介绍。13.2.6 内核方式分区适用于Wi
24、ndows 2000和Windows 98这个分区是存放操作系统代码的地方。用于线程调度、内存管理、文件系统支持、网络支持和所有设备驱动程序的代码全部在这个分区加载。驻留在这个分区中的一切均可被所有进程共享。在Windows 2000中,这些组件是完全受到保护的。如果你试图访问该分区中的内存地址,你的线程将会产生访问违规,导致系统向用户显示一个消息框,并关闭你的应用程序。关于访问违规和如何处理这些违规的详细说明,请参见第2 3、2 4和2 5章的内容。Windows 2000在6 4位Windows 2000中,4 TB用户方式分区看上去与16,777,212TB 的内核方式分区非常不成比例。
25、并不是内核方式分区需要使用该虚拟地址空间的全部空间,它只是说明 6 4位地址空间是非常大的,而该地址空间的大部分是不用的。系统允许应用程序使用4 TB分区,并且允许内核使用它需要的东西,而内核方式分区的大部分是不用的。幸好系统并不需要任何内部数据结构来维护内核方式分区的不用部分。Windows 98不幸的是,在Windows 98中该分区中的数据是不受保护的。任何应用程序都可以从该分区读取数据,也可以写入数据,因此有可能破坏操作系统。13.3 地址空间中的区域当进程被创建并被赋予它的地址空间时,该可用地址空间的主体是空闲的,即未分配的。若要使用该地址空间的各个部分,必须通过调用Vi r t u
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Windows 核心 编程 013
限制150内