作者:shenzi
链接:http://blog.csdn.net/shenzi
1.进程的虚拟地址空间
每 个进程都有自己的虚拟地址空间。对32位进程来说,这个地址空间的大小为4GB,这是因为32位指针可以表示从0x00000000到 0xFFFFFFFF之间的任一值,指针在这个范围内可以有4 294 967 296个值,它们覆盖了进城的4GB地址空间。对62位进程来说,它们可以覆盖16EB地址空间,这个地址空间实在太大了!!!
每个进程有自己的私有地址空间,但是要记住这只是虚拟地址空间,不是物理存储器。为了能过正常读/写数据,我们需要把物理存储器分配或映射到相应的虚拟地址空间,否则将导致访问违规。
2.虚拟地址空间的分区
每个进程的虚拟地址空间被划分成许多分区(partition)。由于地址空间的分区依赖于操作系统的底层实现,因此会随着Windows内核的不同而略有变化。表1列出了各平台上对进程地址空间的分区。
分区 |
x 86 32位Windows |
3 GB用户模式下的 x 86 32位Windows |
x 64 64位Windows |
IA-64 64位 Windows |
---|---|---|---|---|
空指针赋值分区 |
0x00000000 |
0x00000000 |
0x00000000'00000000 |
0x00000000'00000000 |
0x0000FFFF |
0x0000FFFF |
0x00000000'0000FFFF |
0x00000000'0000FFFF |
|
用户模式分区 |
0x00010000 |
0x00010000 |
0x00000000'00010000 |
0x00000000'00010000 |
0x7FFEFFFF |
0xBFFEFFFF |
0x000007FF'FFFEFFFF |
0x000006FB'FFFEFFFF |
|
64-KB 禁入分区 |
0x7FFF0000 |
0xBFFF0000 |
0x000007FF'FFFF0000 |
0x000006FB'FFFF0000 |
0x7FFFFFFF |
0xBFFFFFFF |
0x000007FF'FFFFFFFF |
0x000006FB'FFFFFFFF |
|
内核模式分区 |
0x80000000 |
0xC0000000 |
0x00000800'00000000 |
0x000006FC'00000000 |
0xFFFFFFFF |
0xFFFFFFFF |
0xFFFFFFFF'FFFFFFFF |
0xFFFFFFFF'FFFFFFFF |
空指针赋值分区:
保留该区域的目的是为了帮助程序员捕获对空指针的赋值。如果进程中的线程试图读取或写入位于这一分区内的内存地址,就会引发访问违规。没有任何办法可以让我们分配到位于这一地址区间内的虚拟内存。
用户模式分区:
这一分区是进程地址空间的驻地。对所有应用程序来说,进程的大部分数据都保存在这一分区。
内核模式分区:
系 统需要用这一空间来存放内核代码。设备驱动程序代码、设备输入/输出高速缓存、非分页缓冲池分配表、进程页面表,等等。驻留在这一分区内的任何东西为所有 进程共有。该分区中的所有代码和数据都被完全的保护起来。如果一个应用程序试图读取或写入位于这一分区中的内存地址,会引发访问违规。在默认情况下,访问 违规会导致系统先向用户显示一个消息框,然后结束该应用程序。
3.地址空间中的区域
当系统创建一个进程并赋予它地址空间时,可用地址空间中的大部分都是闲置的(free)或尚未分配的(unallocated)
。为了使用这部分地址空间,我们必须调用VirtualAlloc来分配其中的区域(region) 。分配区域的操作被称为预定(reserving) 。
当应用程序预定地址空间区域时,系统会确保区域的起始地址正好是分配粒度 (allocation granularity)的整数倍。分配粒度会根据不同的CPU平台而有所不同。目前,所有的CPU平台都使用相同的分配粒度,大小为64KB—也就是系统会把分配请求取整到64KB的整数倍。
当应用程序预定地址空间的一块区域时,系统会确保区域的起始地址正好是系统页面 大小的整数倍。页面是一个内存单元,系统通过它来管理内存。与分配粒度相似,页面大小会根据不同的CPU而有所不同。x86和x64系统使用的页面大小为4KB,IA-64系统使用的页面大小为8KB。
(即当我们预定地址空间时,分配粒度影响我们的起始地址,因为起始地址必须整除分配粒度。页面大小影响我们预定的大小,大于等于我们预定的大小,因为要是页面大小的整数倍。)
虽然系统规定应用程序在预定地址空间区域时起始地址必须是分配粒度的整数倍,但系统自己却不存在这样的限制。非常有可能出现的情况是,系统预定的区域的起始地址并非64KB的整数倍,但是预定的区域仍然必须是CPU页面大小的整数倍。
当程序不再需要访问所预定的地址空间区域时,应该释放该区域。通过调用VirtualFree函数来完成。
4.给区域调拨物理存储器
为了使用所预定的地址空间区域,我们还必须分配物理存储器,并将存储器映射到所预定的区域。这个过程称为调拨(committing)物理存储器。物理存储器始终都以页面为单位来调拨。我们通过调用VirtualAlloc函数来讲物理存储器调拨给所预定的区域。
当程序不再需要访问所预定区域中已调拨的物理存储器时,应该释放物理存储器。这个过程称为撤销调拨(decommitting)物理存储器,通过调用VirtualFree函数来完成。
( VirtualAlloc, VirtualFree具体使用在以后章节具体讨论 )
说明:预定只是在虚拟地址空间中预定区域,这时并未为该区域指定物理地址空间。通过调拨操作,我们将之前预定的虚拟地址空间绑定到特定的物理地址空间。
5、物理存储器和页交换文件
在老式的操作系统中,物理存储器被认为是极其中的内存的总量。当今的操作系统能让磁盘空间看起来像内存一样。磁盘上的文件一般被称为页交换文件 (paging file),其中包含虚拟内存,可供任何进程使用。
为了能够使用虚拟内存,操作系统需要CPU的大力协助。当线程试图访问存储器中的一个字节时,CPU必须知道该字节是在内存中还是在磁盘上。如果一台机器 准备了1GB的内存,硬盘上还有1GB的也交换文件,那么有用程序会认为可用内存的总量为2GB。当然,这台机器实际上并没有准备2GB的内存。实际上, 是操作系统与CPU分工协作,把内存中的一部分保存到也交换文件中,并在应用程序需要的时候再将页交换文件中的对应部分载入内存。因此,使用也交换文件可 以增大应用程序可用内存的总量。最好把物理存储器看成是保存在磁盘上的也交换文件中的数据。
当一个线程试图访问所属进程的地址空间中的一块数据时,有可能会出现两种情况。图1显示了经简化后的流程:
系统需要在内存和也交换文件之间复制页面的频率越高,硬盘颠簸(thrash)得越厉害,系统运行得也越慢。
要让计算机跑的快,最好是增加内存。
不在页交换文件 中维护的物理存储器:
如 果每次运行一个程序时,系统都必须为该进程的代码和数据预定地址空间区域,为这些区域调拨物理存储器,然后把硬盘上的程序文件中的代码和数据复制到页交换 文件中已调拨的物理存储器中去。那么载入一个程序并让它运行起来会花费很长的时间。事实上,系统并不会执行刚才所说的这些操作。当用户要求执行一个应用程序时,系统会打开该应用程序对应的.exe文件并计算出应用程序的代码和数据的大小。然后系统会预定一块地址空间,并注明与该区域相关联的物理存储器就是.exe文件本身。 是的,系统并没有从页交换文件中分配空间,而是将.exe文件的实际内容用作程序预定的地址空间区域。这样一来,不但载入程序非常快,而且页交换文件也可以保持一个合理的大小。
当把一个程序位于磁盘上的文件映像(即一个.exe或DLL文件)用作地址空间区域对应的物理存储器时,我们称这个文件映像为内存映像文件 (memory mapped file)。当载入一个.exe或DLL时,系统会自动预定地址空间区域并把文件映像映射到该区域。
6.页面保护属性
我们可以给每个已分配的物理存储页指定不同的页面保护属性。表2列出了所有页面保护属性:
保护属性
|
描述
|
---|---|
PAGE_NOACCESS |
试图读取页面、写入页面或执行页面中的代码将引发访问违规。 |
PAGE_READONLY |
试图写入页面或执行页面中的代码将引发访问违规。 |
PAGE_READWRITE |
试图执行页面中的代码将引发访问违规。 |
PAGE_EXECUTE |
试图读取页面或写入页面将引发访问违规。 |
PAGE_EXECUTE_READ |
试图写入页面将引发访问违规。 |
PAGE_EXECUTE_READWRITE < |