第十三章:windows内存体系结构

来源:互联网 发布:时光机器软件 编辑:程序博客网 时间:2024/05/22 10:50
 

1. 虚拟地址空间的分区

◆ 空指针赋值分区(从0x00000000~0x0000FFFF的闭区间).保留该分区的目的是为了帮助程序员捕获对空指针的赋值.如果进程中的线程试图读取获取写入位于这个分区内的内存地址,就会引发访问违规.(注明:我们在运行代码的时候常常会碰到一种情况就是,我们指针还没有分配指向的空间就开始进行指向操作.这样我们的程序如果是在Debug模式下,程序会down在一个地方,我们可以查看它的内存,发现其为:0xcccccccc.获取有时为0x00000000).

◆ 用户模式分区(根据系统的位数不同而有差异).这块区域进程无法通过指针来读取,写入或者以任何方式,访问驻留在这一分区中其他进程的数据.进程的大部分数据都保存在这一分区(像.exe和DLL,另外系统还会把该进程可以访问的所有内存映射文件映射到这一分区).

默认情况下当运行一个64位应用程序时,系统就会保留用户模式地址空间中位于地址0x000000008000000,这也就保证了所有内存都分配自64位地址空间中最底部的2GB.(这就是空间沙箱).

我们可以利用两个函数来分配和释放区域:VirtualAlloc和VirtualFree.

2. 当用户执行一个应用程序时,系统会打开该应用程序对应的exe文件并计算出应用程序的代码和数据的大小.然后系统会预定一块地址空间,并注明与该区域先关联的物理存储器就是.exe文件本身.这样就大大提高了性能.

内存映射文件:当把一个程序位于硬盘上的文件映像(即exe文件)用作地址空间区域对应的物理存储器时,我们称这个文件映像为内存映射文件.

3. 页面保护属性表

保护属性

描述

PAGE_NOACCESS

试图读取页面、写入页面或执行页面中的代码将引发访问违规

PAGE_READONLY

试图写入页面或者执行页面中的代码将引发访问违规

PAGE_READWRITE

试图执行页面中的代码将引发访问违规

PAGE_EXECUTE

试图读取页面或者写入页面将引发访问违规

PAGE_EXECUTE_READ

试图写入页面将引发访问违规

PAGE_EXECUTE_READWRITE

对页面执行任何操作都不会引发访问违规

PATE_WRITECOPY

试图执行页面中的代码将引发访问违规,试图写入页面将使系统为进程单独创建一份该页面的私有副本(以页交换文件为后备存储器)

PAGE_EXECUTE_WRITECOPY

对页面执行任何操作都不会引发访问违规.试图写入页面将使系统为单独单独创建一份改页面的私有副本(以页交换文件为后备存储器)

如果启用了数据执行保护(DEP),那么只有对那些真正需要执行代码的内存区域,操作系统才会使用PAGE_EXECUTE_*保护属性.其他保护属性用于只应该存放数据的内存区域.(比如线程栈和应用程序堆).如果CPU要执行没有PAGE_EXECUTE_*保护属的代码,将会引发异常.

有时系统为了提高效率,经常需要将两个或者两个以上的进程共享同一存储器.为了避免不必要的的错误,系统会给共享的存储页指定写时复制属性.当系统把一个exe或dll映射到一个地址空间的时候会计算有多少可写页,然后系统从页交换文件分配存储空间来容纳这些可写页面.如果兄台那个试图写入一个共享页面时将执行以下步骤:

1). 系统在内存中找到一个闲置页面.

2). 系统把线程想要修改的页面内容复制到在第一步找到的闲置页面.注意此时系统不会对原来的页面进行任何修改.

3). 更新进程页面表.

另外在预定地址或者调拨物理存储器时,不能使用PAGE_WRITECOPY或者PAGE_EXECUTE_WRITECOPY保护属性.这样做会导致VirtualAlloc失败.

其他的访问属性标志:

PAGE_NOCACHE:用来禁止对已调拨的页面进行缓存.该标志存在的主要目的是为了让需要操控内存缓冲区的驱动程序开发人员使用,而不建议其他人使用.

PAGE_WRITECOMBINE:也是供驱动程序开发人员使用.它允许把对单个设备的多次写操作组合在一起,以提高效率.

PAGE_GUARD:使应用程序能够在页面中的任何一个字节被写入时得到通知.

4. 数据的对齐:只有当访问已对齐的数据时,CPU的执行效率才是最高的.把数据的地址模除数据的大小,如果结果为0,表示数据时对齐的.

X86和AMD CPU中,是靠CPU来自动执行必要的操作来访问错位数据.而IA-64 CPU不能自动处理,它是通过通知操作系统,让其来进行处理.正常情况下,windows会自动将数据错位的错误转换成一个EXCEPTION_DATATYPE_MISALIGNMENT异常.我们可利用SetErrorMode,让操作系统为进程中的所有线程自动修正数据错位的错误.

UINT SetErrorMode(UINT fuErrorMode);

这里我们应该设置SEM_NOALIGNMENTFAULTEXCEPT标志.只要设置了该标志系统会自动修正对错位数据的访问.一旦改变这个标志,那么就无法在在进程的生命周期内再次改变它.

在IA-64 CPU版本的C/C++编译器中,当通过一个带__unaligned修饰符的指针访问数据时编译器会认为数据未经过对齐,并生成额外的CPU指令以访问数据.(需要注意的是x86并不支持这个关键字)