13.1 进程虚拟地址空间
每个进程都有自己的专有地址空间,对32位进程来说,这个地址空间的大小为4GB,这是因为32位指针可以表示从0x00000000-0xffffffff的任意值。对64位进程来说则可以表示0x0000000000000000 到0xffffffffffffffff之间任一值。
因为每一个进程都有自己的专有地址空间,当进程中的各线程运行时,它们只访问属于该进程的内存。线程即看不到其他进程内容空间,也无法访问它们。
进程A可以在位于它地址空间内的0x12345678地址处存储一个数据结构,而进程B也可以 “自己的”地址空间存储一个0x12345678处的内存。当进程A访问0x12345678时他访问的是A的数据结构,进程 B访问0x12345678他访问的则是B的这块数据。进程A中的线程无法访问位于进程 B的地址空间内的数据结构,反之亦然 。
虽然应用程序有这么大的地址空间可用,但是要记住这只是虚拟地址空间----不是物理存储空间。这个地址空间只不过是一个内存地址区间。为了能够正常读写我们还需要把物理存储器分配或映射到相应地址空间,否则将导致访问违规。
13.2 虚拟地址空间的分区
进程的地址空间是如何划分的
分区 x86 32位 windows 3GB用户模式下的x86 32位windows x64 windows ia-64 64位windows 空指针赋值分区 0x00000000
0x0000FFFF0x00000000
0x0000FFFF0x00000000 00000000
0x00000000 0000FFFF0x00000000 00000000
0x00000000 0000FFFF用户模式分区 0x00010000
0x7FFFFFFFF0x00010000
0xBFFFFFFFF0x00000000 00010000
0x000007FF FFFFFFFF0x00000000 00000000
0x000006FB FFFFFFFF64 KB 禁入分区 0x7FFF0000
0x7FFFFFFF0xBFFF0000
0xBFFFFFFF0x000007FF FFFF0000
0x000007FF FFFFFFFF0x000006FB FFFF0000
0x000000FB FFFFFFFF内核模式分区 0x80000000
0xFFFFFFFF0xc0000000
0xFFFFFFFF0x00000800 00000000
0xFFFFFFFF FFFFFFFF0x000006FC 00000000
0xFFFFFFFF FFFFFFFF
13.2 .1 空指针赋值分区
目的:引发违规。
在c++中,错误检查经常不够彻底,如下代码
int * pnSomeInteger = (int * ) malloc(sizeof(int))
*pnSomeInteger = 5;上面这段代码malloc如果没有足够内存,那么它会返回NULL,但是前面的代码汉有检查这一错误。注意:没有任何办法可以让我们分配到位于这一地址区间的虚拟地址空间。
13.2 .1 用户模式分区
这一分区是进程地址空间的驻地。可用的地址敬意和用户模式分区的大小取决于cpu体系结构
进程无法通过指针来读取,写入或以任何方式,访问驻留在这一分区中其它进程的数据。、
CPU体系结构,对应的用户模式可用地址区间以及大小
cpu体系结构 用户模式分区的可用地址区间 用户模式分区的大小 x86(普通) 0x00010000->0x7fffffff ~2GB x86w/3GB 0x00010000->0xBFFFFFFF ~3GB x64 0x000000000001000->0x000007FFFFFFFFFF ~8192GB IA-64 0x00000000 00010000->0x000006FB FFFFFFFF ~7152GB 1. x86 Windows下得到更大的用户模式分区
2.在64位windows下得到2GB用户模式分区
3.内核模式分区
13.3 虚拟地址空间的分区
VirtualAlloc分配其中的区域(region)。分配区域的操作被称为预订(reserving)。
当预定时系统会确保区域的起始地址正好是分配粒度的整数倍。分配粒度会根据不同的CPU平台而有所不同。但是,在写作本书时所有CPu平台都使用相同的分配粒度,大小为64KB,也就是说,系统会把分配请求取到64KB的整数倍。
当应用程序预订地址空间中的一块区域时,系统会确保区域的大小为正好是系统页面大小的整数倍。页面是一个内存管理单元,系统通过页面来管理内存。与分配粒度相似页面大小与CPu不同而不同。x86和x64的页面大小为64KB,而IA-64系统使用的页面大小为8KB。
说明:
有时候系统会以应用程序的名义来预订地址空间区域。例如,系统会分配一块地址区间来存放进程环境块(PEB)。PEB是一个完全由系统创建,操控并销毁的小型数据结构。
系统同时还需要创建线程环境块(TEB),系统会在线程创建时分配TEB区域,并在销毁线程时释放相应区域。
13.4 给区域调拨物理存储器
为了使用所预订的地址空间区域,我们还必须分配物理存储器,并将存储器映射到所预订的区域。这个过程被称为调拨(committing)物理存储器。物理存储器始终都以页面为单位来调拨。我们通过调用VirtualAlloc来将物理存储器给所预订的区域。
当我们调拨物理存储器给区域时,并不需要给整个区域都调拨物理存储器。例如,我们可以预订一块大小为64KB的区域,然后把物理存储器拨给该区域中的第2个页面和第4个页面。
当程序不再需要访问所预订区域中已调拨的物理存储器时,应该释放物理存储器。这个过程被称为撤销调拨(decommitting)物理存储器,通过调用VirtualFree来完成。
13.5 物理存储器和页交换文件
页交换文件:硬盘上的文件一般被称为页交换文件,其中包含虚拟内存,可供任何程序使用。
不在页交换文件中维护物理存储器
在读过上一节之后,读者可能会想到,如果许多程序同时运行,页交换文件可能会变的相当大----尤其是每次运行一个程序时,系统都必须为该进程的代码和数据预订地址空间区域,为这些区域调拨物理存储器,然后把硬盘上程序文件中的代码和数据复制到页交换文件中已调拨的物理存储器中去。事实上,系统并不会执行刚才所说的这些操作。
如用户要求执行一个应用程序时,系统会打开该应用程序对应.exe文件并计算出应用程序的代码和数据的大小,。然后系统会预订一块地址空间,并注明与该区域相关靠人为生者物理存储器就是exe文件本身。是的,系统燕没有从页交换文件中分配空间,而是将exe文件的实际内容(或文件映象即file image)用作程序预订的地址空间区域。这样一来,不但载入程序非常快,而且页交换文件也可以保持一个合理的大小。
当把一个程序位于硬盘上的文件映象(即一个exe或 dll文件)用作地址空间区域对应的物理存储器时,我们称这个文件映象为内存映射文件(memory mapped file)。当载入一个exe或dll时,系统会自动预订空间区域并把文件映象映射到该区域。但是,系统也提供了一组函数,可以上开发人员把数据文件映射到地址空间。
说明:
当windows从软盘载入.exe或.dll文件时,系统会把整个文件从软盘复制到内存中,此外,系统还会从页交换文件中分配足够的存储空间来存放文件映象。只有当系统需要把一个页面换出内存,而页面又包含文件映象的一部份时,系统才会写入页交换文件。如果系统的内存负载很轻,那么文件总是在内存中运行。
Microsoft必须让软盘上执行的文件以这种方式 运行,因为只有这样安装程序才能正常运行。通常安装程序从第一张软盘开始,在安装过程中用户会取出该软盘并插入其它软盘。如果系统需要再从第一张软盘去加载exe或dll的一部分代码,那么很显然,第一张软盘可能已经不在软驱中了。但是由于系统已经把文件复制到了内存中(并以页交换文件为后备存储器),因此系统可以随时访问安装程序而且不会遇到任何问题。
除非映象文件是用/SWAPRUN:CD或/SWAPRUN:NET开关链接的,否则系统不会把位于其它可移动媒介(比如 光盘、或网络驱动器)上的映象文件复制到内存中的。