Berry's blog with C++ & .Net

Windows & VxWorks development and learning notes.

导航

Windows内存结构

Posted on 2009-10-09 17:14  Berry029  阅读(2219)  评论(0编辑  收藏  举报

1  进程的虚拟地址空间布局

1.1 进程虚拟地址空间布局:

每个进程都被赋予它自己的虚拟地址空间。对于3 2位进程来说,这个地址空间是4GBWin2KIA-32架构的CPU下面的进程地址空间分布如下表所示:

地址空间说明

地址空间

64K NULL指针分配区

0x00000000~0x0000FFFF  (64K)

用户空间

0x00010000~0x7FFEFFFF  (2G–128K)

64K禁入区

0x7FFF0000~0x7FFFFFFF  (64K)

内核空间

0x80000000~0xFFFFFFFF   (2G)


可见Win2K的内存布局非常简单,主要有4个部分,两个不能被存取的64K;然后剩下的部分就是分别是用户空间(2G–128K)和系统空间(2G)了。

1.2 进程虚拟地址空间布局说明:

1.  64K NULL指针分配区:

这个分区的设置是为了帮助程序员掌握NULL指针的分配情况。如果你的进程中的线程试图读取该分区的地址空间的数据,或者将数据写入该分区的地址空间,那么C P U就会引发一个访问违规。也就是说在Win2K中,NULL的宏定义不必一定是0;可以是64K之内的任何地址;比如在win2K下面定义一个指针变量pVar;令pVar取值在06553564K)之间的时候都会发生存取异常;当令pVar = 65536就没有问题了。

2.  用户空间:

Windows 2000中,所有的. exeDLL模块均加载这个分区。每个进程可以将这些D L L加载到该分区的不同地址中(不过这种可能性很小)。系统还可以在这个分区中映射该进程可以访问的所有内存映射文件。

3.  64K禁入区:

64K禁入区的作用很明显是隔离了用户和内核空间;防止用户程序跨越到内核空间中。

4.  内核空间:

这个分区是存放操作系统代码的地方。用于线程调度、内存管理、文件系统支持、网络支持和所有设备驱动程序的代码全部在这个分区加载。驻留在这个分区中的一切均可被所有进程共享。在Windows 2000中,这些组件是完全受到保护的。

2  进程的虚拟地址空间分配

2.1 如何分配地址空间?

当进程被创建并被赋予它的地址空间时,该地址空间的主体是未分配的。若要使用该地址空间的各个部分,必须通过调用VirtualAlloc函数来分配它里边的各个区域。对一个地址空间的区域进行分配的操作称为保留( reserving)

分配地址空间的两个规则:要确保保留区域从一个分配粒度的边界开始(系统自身保留地址空间时未必遵守这个约定)。x86使用64KB这个分配粒度;还要确保保留区域的大小是系统页面大小的倍数。x86使用的页面大小是4KB

2.2 如何回收地址空间?

当你的程序不再需要访问已经保留的地址空间区域时,保留区域应该被释放。这个过程称为释放地址空间的区域,它是通过调用VirtualFree函数来完成的。

注意这节说的只是对地址空间的分配,而不是对内存的分配。已经被分配的地址空间没有和内存相对应;这时候对这块空间的读写会发生访问违规。

2.3 地址空间可以和什么相对应呢?

和地址空间相对应的只有两样东西,一个是页面文件,也就是通常所说的虚拟内存,一个是内存映射文件,内存映射文件也是磁盘上的文件,这包括exedll等,还包括用户自己创建的内存映射文件。或许你认为和地址空间相对应的应该是ram,不错,当我们真正对一个地址进行读写的时候,确实是对ram进行读写;但是ram只是相当于一个缓冲区;负责对内存映射文件和虚拟内存读写进行缓冲。

3  进程的虚拟地址空间的保护属性

3.1 什么是Copy-On-Write保护属性?

在保留地址空间的同时可以指定其保护属性,这些属性无法就是读,写,执行,及其组合,这里重点说一下两个特殊的保护属性。一个是PAGE_WRITECOPY,另一个是PAGE_EXECUTE_WRITECOPY。这两个属性的作用是为了节省RAM的使用量和页文件的空间。Windows支持一种机制,使得两个或多个进程能够共享单个内存块。但是这要求所有实例都将该内存视为只读或只执行的内存。如果一个实例中的线程将数据写入内存修改它,那么其他实例看到的这个内存也将被修改,从而造成一片混乱。为了防止出现这种混乱,操作系统给共享内存块赋予了Copy-On-Write保护属性。当一个.exeDLL模块被映射到一个内存地址时,系统将计算有多少页面是可以写的(通常包含代码的页面标为PAGE_EXECUTE_READ,而包含数据的页面则标为PAGE_READWRITE)。然后,系统依照计算结果从页文件中分配内存,以防备对这些可写页面写入时的需要。当然如果不对该模块的可写页面进行实际的写入操作,那么这些页文件内存就不会被使用。

3.2 Copy-On-Write保护是如何实现的?

当一个进程中的线程试图将数据写入一个共享内存块时,系统就会进行干预,并执行下列操作步骤:

1)系统查找RAM中的一个空闲内存页面。

2)系统将试图被修改的页面内容拷贝到第一步中找到的页面。并将其赋予PAGE_READWRITEPAGE_EXECUTE_READWRITE保护属性。原始页面的保护属性和数据不发生任何变化。

3)然后系统更新进程的页面表,使得被访问的虚拟地址被转换成新的RAM页面。

当系统执行了这3个操作步骤之后,该进程就可以访问它自己的内存页面的私有实例。