《Windows核心编程第五版》笔记
第13章 Windows内存体系结构
【NOTE】进程的虚拟地址空间
32位进程的地址空间的大小是4GB
【NOTE】虚拟空间地址的分区 (x86 32位)
1.空指针赋值分区 0x00000000~0x0000FFFF (0~65,535 65536大小)
*我们没有办法分配到这分区地址来使用
原文地址:http://www.cnblogs.com/anzhihun/archive/2009/08/08/1349032.html 1.为什么通过空指针读写的时候就会出现异常? 2.除了NULL表示空指针,是否还有其他的值也是空指针? 3.如果还有其他的值,你们这些表示空指针的值都是什么?为什么? 首先解答第一个问题,在windows核心编程第四版的windows的内存结构一章中,表13-1有提到NULL指针分配的分区。其范围是从0x00000000到0x0000FFFF。这段空间是空闲的,对于空闲的空间而言,没有相应的物理存储器与之相对应,所以对这段空间来说,任何读写操作都是会引起异常的。 有了上面的解答后,第二个问题就很容易解答了。NULL的定义出现以下几个地方: stdio.h文件中 #ifndef NULL #ifdef __cplusplus #define NULL 0 #else #define NULL ((void *)0) #endif #endif ios.h文件中 #ifndef NULL #define NULL 0 #endif windef.h文件中 #ifndef NULL #ifdef __cplusplus #define NULL 0 #else #define NULL ((void *)0) #endif #endif 可见,NULL的值,基本上是用0来表示的,是不是只能用0呢?在windows xp sp2的系统平台下,如果执行下面代码也是会发生异常的: int * pAddr = (int *)0x0000ffff; *pAddr = 1; 而下面的代码是不会出问题的: int * pAddr = (int *)0x00010000; *pAddr = 1; 为什么呢?在windows xp sp2下发现0x00000000到0x0000FFFF是空闲区间,而0x00010000所处的是进程的私有区间。我想第二个问题应该已经解决了,我想,空指针是程序无论在何时都没有物理存储器与之对应的地址。为了保障“无论何时”这个条件,需要人为划分一个空指针的区域,固有上面NULL指针分区。 在第二个问题的基础上,要解答NULL指针的范围,那就相对来说容易了,对于在32位x86计算机上运行的windows xp sp2来说,就是从0x00000000到0x0000ffff。为什么分配如此大的空间?而在定义NULL的时候,只使用了 0x00000000这么一个值,这不是浪费吗?我想,这是操作系统地址空间的分配粒度相关的,windows xp sp2的分配粒度是64KB,为了达到对齐,空间地址需要从0x00010000开始分配,故空指针的区间范围有那么大。 上面的阐述如有问题,希望各位更正,谢谢。
2.用户模式分区
x86 0x0010000~0x7FFEFFFF 65,535~2,147,418,111
* 进程不能通过指针访问驻留在这分区的其他进程的数据
3.64KB禁入分区
4.内核模式分区
x86 0x80000000~0xFFFFFFFF 2,147,483,648~4,294967,295
【NOTE】地址空间中的区域
* 预定(reserving)与调拨(committing)
* 预定地址空间区域时,区域的起始地址的分配粒度是64KB,大小的分配粒度正好是页面大小4KB
【NOTE】物理存储器和页交换文件
* VirtualAlloc函数调拨的一般是页交换文件,不是内存的物理空间
* 当一个线程试图访问所属进程的地址空间的一块数据的时候:
情况1:要访问的数据就在内存中,此时CPU先把虚拟内存地址映射到内存的物理地址上,接下来就可以访问了
情况2:要访问的数据在页交换文件中,此时CPU将页面错误通知操作系统,操作系统随即在内存中寻找一个闲置的页面。如果找不到,就必须释放一个已分配页面,如果待释放的页面没有被使用过,就可以直接被释放。如果被使用过,操作系统就必须先把页面复制到交换文件中,再释放。
【NOTE】内存映射文件---不在页交换文件中维护的物理存储器(物理存储器可以在内存条或者页交换文件中)
* 当载入一个dll/.exe时,系统会计算应用程序的代码和数据大小,然后预定一块地址空间,并且注明关联的物理存储器就是dll/.exe本身,从而不用另外关联页交换文件,从而避免了在硬盘中的复制
【页面保护属性】
* 页面保护属性一览:
PAGE_NOACCESS
PAGE_READONLY
PAGE_READWRITE
PAGE_EXECUTE
PAGE_EXECUTE_READ
PAGE_EXECUTE_READWRITE
PAGE_WRITECOPY
PAGE_EXECUTE_WRITECOPY
* 对内存的操作有:读取,写入,执行代码, 页面属性保护就是针对上述操作进行保护
* 进程虚拟地址空间中每个区域可能是以下四种类型之一:
1.闲置,表示还没有被预订。
2.私有,表示虚拟地址以页交换文件作为后备存储器。
3.映像,表示虚拟地址以映像文件(比如.EXE或者.DLL)为后备存储器。
4.已映射,以内存映射文件为后备存储器。
【NOTE】写时复制
* PAGE_WRITECOPY 、PAGE_EXECUTE_WRITECOPY
* Windows的一个机制,允许多个进程共享一个存储器,如果可以同时有10个记事本程序同时运行,这就要求所有的实例只能读取和执行里面的代码
* 预定地址空间和调拨物理存储器的时候不能使用PAGE_WRITECOPY 、PAGE_EXECUTE_WRITECOPY两个属性,这两个属性是系统映射dll/.exe映像文件的时候用的
【NOTE】数据对齐--数据的地址模除数据自的大小等于0
* 对齐步骤:
1.结构体的对齐规则是先按数据类型自身进行对齐
2.然后再按整个结构体进行对齐
例子:
struct Test1 { int i ; double d ; char c ; };
4(i) + 4(补齐)+ 8(d) + 1(c) = 17字节 ,然后整体以最大的类型double对齐,所以整体以8对齐,17 + 7(补齐) = 24字节。