Windows编程-- Windows的内存结构
进程的虚拟地址空间
每个进程都被赋予它自己的虚拟地址空间。对于32位进程来说,这个地址空间是4GB,(这是个虚拟地址空间,不是物理地址空间)。
32位指针可以拥有从0x00000000至0xFFFFFFFF之间的任何一个值。这使得一个指针能够拥有4 294 967 296个值中的一个值,它覆盖了一个进程的4GB虚拟空间的范围。对于64位进程来说,这个地址空间是16EB(1018字节),
64位指针可以拥有从0x0000000000000000至0xFFFFFFFFFFFFFFFF之间的任何值。这使得一个指针可以拥有18 446 744 073 709 551 616个值中的一个值,它覆盖了一个进程的16EB虚拟空间的范围。
线程可以访问只属于它的进程的内存。属于所有其他进程的内存则隐藏着,并且不能被正在运行的线程访问
每个进程有它自己的私有地址空间。进程A可能有一个存放在它的地址空间中的数据结构,地址是0x12345678,而进程B则有一个完全不同的数据结构存放在它的地址空间中,地址是0x1234567 8。当进程A中运行的线程访问地址为0x12345678的内存时,这些线程访问的是进程A的数据结构。当进程B中运行的线程访问地址为0x 12345678的内存时,这些线程访问的是进程B的数据结构。进程A中运行的线程不能访问进程B的地址空间中的数据结构。反之亦然。
地址空间中的区域
进程被创建并被赋予它的地址空间时,该可用地址空间的主体是空闲的(即未分配的)。若要使用该地址空间的各个部分,必须通过调用VirtualAlloc函数来分配它里边的各个区域。对一个地址空间的区域进行分配的操作称为保留操作( reserving )。
每当你保留地址空间的一个区域时,系统要确保该区域从一个分配粒度的边界开始。对于不同的CPU 平台来说,分配粒度是各不相同的。目前的CPU 平台(x86 、32 位Alpha 、64 位Alpha 和IA-64 )都使用64KB 这个相同的分配粒度。
当你保留地址空间的一个区域时,系统还要确保该区域的大小是系统的页面大小的倍数。页面是系统在管理内存时使用的一个内存单位。与分配粒度一样,不同的CPU ,其页面大小也是不同的。x86 使用的页面大小是4KB ,而Alpha (当既能运行32 位Windows 2000 也能运行64 位Windows 2000 时)使用的页面大小则是8KB 。如果想保留一个10 KB的地址空间区域,系统将自动对你的请求进行四舍五入,使保留的地址空间区域的大小是页面大小的倍数。
当你的程序算法不再需要访问已经保留的地址空间区域时,该区域应该被释放。这个过程称为释放地址空间的区域,它是通过调用VirtualFree函数来完成的。
在地址空间区域中提交物理存储器
若要使用已保留的地址空间区域,必须分配物理存储器,然后将该物理存储器映射到已保留的地址空间区域。这个过程称为提交物理存储器。物理存储器总是以页面的形式来提交的(因为页面是系统在管理内存时使用的一个内存单位)。若要将物理存储器提交给一个已保留的地址空间区域,也要调用VirtualAlloc函数。
当程序算法不再需要访问保留的地址空间区域中已提交的物理存储器时,该物理存储器应该被释放。这个过程称为回收物理存储器,它是通过VirtualFree函数来完成的。
物理存储器与页文件
磁盘上的文件通常称为页文件,它包含了可供所有进程使用的虚拟内存。
进程中的一个线程试图访问进程的地址空间中的一个数据块时,将会发生两种情况(如图):
在第一种情况中,线程试图访问的数据是在RAM 中。在这种情况下,CPU 将数据的虚拟内存地址映射到内存的物理地址中,然后执行需要的访问。
在第二种情况中,线程试图访问的数据不在RAM 中,而是存放在页文件中的某个地方。
试图访问就称为页面失效, CPU将把试图进行的访问通知操作系统。这时操作系统就寻找RAM中的一个内存空页。
如果找不到空页,系统必须释放一个空页。
如果一个页面尚未被修改,系统就可以释放该页面。
但是,如果系统需要释放一个已经修改的页面,那么它必须首先将该页面从RAM拷贝到页交换文件中,然后系统进入该页文件,找出需要访问的数据块,并将数据加载到空闲的内存页面。然后,操作系统更新它的用于指明数据的虚拟内存地址现在已经映射到R A M中的相应的物理存储器地址中的表。这时CPU重新运行生成初始页面失效的指令,但是这次CPU能够将虚拟内存地址映射到一个物理RAM地址,并访问该数据块。
注意:系统需要将内存页面拷贝到页文件并反过来将页文件拷贝到内存页面的次数越多,你的硬盘倒腾的次数就越多,系统运行得越慢(抖动)。
内存映射文件(不在页文件中维护的物理存储器)
当启动一个应用程序的时候,系统将打开该应用程序的.exe 文件,确定该应用程序的代码和数据的大小。然后系统要保留一个地址空间的区域,并指明与该区域相关联的物理存储器是在.exe 文件本身中。即系统并不是从页文件中分配地址空间,而是将.exe 文件的实际内容即映像用作程序的保留地址空间区域。当然,这使应用程序的加载非常迅速,并使页文件能够保持得非常小。
当硬盘上的一个程序的文件映像(这是个.exe文件或DLL文件)用作地址空间的区域的物理存储器时,它称为内存映射文件。当一个. exe文件或DLL文件被加载时,系统将自动保留一个地址空间的区域,并将该文件映像映射到该区域中。
数据对齐的重要性
数据对齐并不是操作系统的内存结构的一部分,而是CPU结构的一部分。
当C P U访问正确对齐的数据时,它的运行效率最高。当数据大小的数据模数的内存地址是0时,数据是对齐的。例如, W O R D值应该总是从被2除尽的地址开始,而D W O R D值应该总是从被4除尽的地址开始,如此等等。
保护属性
已经分配的物理存储器的各个页面可以被赋予不同的保护属性。
保护属性 |
描述 |
PAGE_NOACCESS |
如果试图在该页面上读取、写入或执行代码,就会引发访问违规 |
PAGE_READONLY |
如果试图在该页面上写入或执行代码,就会引发访问违规 |
PAGE_READWRITE |
如果试图在该页面上执行代码,就会引发访问违规 |
PAGE_EXECUTE |
如果试图在该页面上对内存进行读取或写入操作,就会引发访问违规 |
PAGE_EXECUTE_READ |
如果试图在该页面上对内存进行写入操作,就会引发访问违规 |
PAGE_EXECUTE_READWRITE |
对于该页面不管执行什么操作,都不会引发访问违规 |
PAGE_WRITECOPY |
如果试图在该页面上执行代码,就会引发访问违规。如果试图在该页面上写入内存,就会导致系统将它自己的私有页面(受页文件的支持)拷贝赋予该进程 |
PAGE_EXECUTE_WRITECOPY |
对于该地址空间的区域,不管执行什么操作,都不会引发访问违规。如果试图在该页面上的内存中进行写入操作,就会将它自己的私有页面(受页文件的支持)拷贝赋予该进程 |
当使用 VirtualAlloc 函数来保留地址空间或者提交物理存储器时,不应该传递 PAGE_WRITECOPY
或 PAGE_EXECUTE_READWRITE 。如果传递的话,将会导致 VirtualAlloc 调用的失败。对 GetLastError 的调用将返回 ERROR_INVALID_PARAMETER 。当操作系统映射 .exe 或 DLL 文件映像时,这两个属性将被操作系统使用。
FangSH
2011-01-06