windows核心编程--内存结构
每个进程都被赋予它自己的虚拟地址空间。对于3 2位进程来说,这个地址空间是4 G B,因为3 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虚拟空间的范围。
由于每个进程可以接收它自己的私有的地址空间,因此当进程中的一个线程正在运行时,该线程可以访问只属于它的进程的内存。属于所有其他进程的内存则隐藏着,并且不能被正在运行的线程访问。注意在Windows 2000中,属于操作系统本身的内存也是隐藏的,正在运行的线程无法访问。这意味着线程常常不能访问操作系统的数据。
每个进程的虚拟地址空间都要划分成各个分区。地址空间的分区是根据操作系统的基本实现方法来进行的。不同的Wi n d o w s内核,其分区也略有不同。表1 3 - 1显示了每种平台是如何对进程的地址空间进行分区的。
表13-1 进程的地址空间如何分区
分区 | 32位Windows 2000(x86和Alpha处理器) | 32位Windows 2000(x86w/3GB用户方式) | 64位Windows 2000(Alpha和IA-64处理器) | Windows 98 |
N U L L指针分配的分区 | 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 F F F F | 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 F F F F | 0x00000000 00000000 0x00000000 0000FFFF | 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 F F F |
DOS/16位Windows应用程序兼容分区 | 无 | 无 | 无 | 0 x 0 0 0 0 0 1 0 0 0 0 x 0 0 3 F F F F F |
用户方式 | 0 x 0 0 0 1 0 0 0 0 0 x 7 F F E F F F F | 0 x 0 0 0 1 0 0 0 0 0 x B F F E F F F F F | 0x00000000 00010000 0x000003FF FFFEFFFF | 0 x 0 0 4 0 0 0 0 0 0 x 7 F F F F F F F |
64-KB | 0 x 7 F F F 0 0 0 0 | 0 x B F F F 0 0 0 0 | 0 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 F | 0 x B F F F F F F F | 0 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 8 0 0 0 0 0 0 0 0 0 x F F F F F F F F | 0 x C 0 0 0 0 0 0 0 0 x F F F F F F F F | 0x00000400 00000000 0xFFFFFFFFF FFFFFFF | 0 x B F F F F F F F 0 x C 0 0 0 0 0 0 0 0 x F F F F F F F F |
NULL指针:就是程序中的NULL哦, 进程地址空间的这个分区的设置是为了帮助程序员掌握N U L L指针的分配情况。
MS-DOS/16位Windows应用程序兼容分区—仅适用于Windows 98:进程地址空间的这个4 M B分区是Windows 98需要的,目的是维护M S - D O S应用程序与1 6位应用程序之间的兼容性。
用户方式: 这个分区是进程的私有(非共享)地址空间所在的地方。一个进程不能读取、写入、或者以任何方式访问驻留在该分区中的另一个进程的数据。对于所有应用程序来说,该分区是维护进程的大部分数据的地方。由于每个进程可以得到它自己的私有的、非共享分区,以便存放它的数据,因此,应用程序不太可能被其他应用程序所破坏,这使得整个系统更加健壮。系统还可以在这个分区中映射该进程可以访问的所有内存映射文件。当我最初观察3 2位进程的地址空间的时候,我惊奇地发现可以使用的地址空间还不到我的进程的全部地址空间的一半。难道内核方式分区真的需要上面的一半地址空间吗?实际上回答是肯定的。系统需要这个地址空间,供内核代码、设备驱动程序代码、设备I / O高速缓存、非页面内存池的分配和进程页面表等使用。实际上M i c r o s o f t将内核压缩到这个2 G B空间之中。
64KB禁止进入的分区—仅适用于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函数时,该函数将在执行它的操作之前使内存块生效。
共享的MMF分区—仅适用于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位进程都能很容易同时访问它们。
内核方式分区—适用于Windows 2000和Windows 98 : 这个分区是存放操作系统代码的地方。用于线程调度、内存管理、文件系统支持、网络支持和所有设备驱动程序的代码全部在这个分区加载。驻留在这个分区中的一切均可被所有进程共享。在Windows 2000中,这些组件是完全受到保护的。
地址空间中的区域
当进程被创建并被赋予它的地址空间时,该可用地址空间的主体是空闲的,即未分配的。若要使用该地址空间的各个部分,必须通过调用Vi r t u a l A l l o c函数(第1 5章介绍)来分配它里边的各个区域。对一个地址空间的区域进行分配的操作称为保留( r e s e r v i n g )。
每当你保留地址空间的一个区域时,系统要确保该区域从一个分配粒度的边界开始。对于不同的C P U平台来说,分配粒度是各不相同的。
提交地址空间区域中的物理存储器
若要使用已保留的地址空间区域,必须分配物理存储器,然后将该物理存储器映射到已保留的地址空间区域。这个过程称为提交物理存储器。物理存储器总是以页面的形式来提交的。若要将物理存储器提交给一个已保留的地址空间区域,也要调用Vi r t u a l A l l o c函数。
物理存储器与页文件
在较老的操作系统中,物理存储器被视为计算机拥有的R A M的容量。换句话说,如果计算机拥有1 6 M B的R A M,那么加载和运行的应用程序最多可以使用1 6 M B的R A M。今天的操作系统能够使得磁盘空间看上去就像内存一样。磁盘上的文件通常称为页文件,它包含了可供所有进程使用的虚拟内存。
当然,若要使虚拟内存能够运行,需要得到C P U本身的大量帮助。当一个线程试图访问一个字节的内存时, C P U必须知道这个字节是在R A M中还是在磁盘上。
从应用程序的角度来看,页文件透明地增加了应用程序能够使用的R A M(即内存)的数量。如果计算机拥有6 4 M B的R A M,同时在硬盘上有一个100 MB的页文件,那么运行的应用程序就认为计算机总共拥有1 6 4 M B的R A M。
当然,实际上并不拥有1 6 4 M B的R A M。相反,操作系统与C P U相协调,共同将R A M的各个部分保存到页文件中,当运行的应用程序需要时,再将页文件的各个部分重新加载到R A M。由于页文件增加了应用程序可以使用的R A M的容量,因此页文件的使用是视情况而定的。如果没有页文件,那么系统就认为只有较少的R A M可供应用程序使用。但是,我们鼓励用户使用页文件,这样他们就能够运行更多的应用程序,并且这些应用程序能够对更大的数据集进行操作。最好将物理存储器视为存储在磁盘驱动器(通常是硬盘驱动器)上的页文件中的数据。这样,当一个应用程序通过调用Vi r t u a l A l l o c函数,将物理存储器提交给地址空间的一个区域时,地址空间实际上是从硬盘上的一个文件中进行分配的。系统的页文件的大小是确定有多少物理存储器可供应用程序使用时应该考虑的最重要的因素, R A M的容量则影响非常小。
数据对齐的重要性
当C P U访问正确对齐的数据时,它的运行效率最高。当数据大小的数据模数的内存地址是0时,数据是对齐的。例如, W O R D值应该总是从被2除尽的地址开始,而D W O R D值应该总是从被4除尽的地址开始,如此等等。当C P U试图读取的数据值没有正确对齐时, C P U可以执行两种操作之一。即它可以产生一个异常条件,也可以执行多次对齐的内存访问,以便读取完整的未对齐数据值。