《Windows核心编程》读书心得——内存(内存体系结构)(5)

 

虚拟内存:

每个进程有自己的虚拟内存,进程内的线程只能访问该进程的虚拟内存,而无法访问其他进程的虚拟内存,也无法访问系统的内存。进程地址空间的划分如下:

 

 

                                                                     (图5 - 1)

空指针:

0x00000000到0x0000FFFF的区间,线程访问该区间的地址,将会引发访问违规。注意,一个没有赋初值的指针不是空指针(在Debug下,默认值为0xCCCCCCCC,是属于内核区间的地址,也会引发访问违规;在Release下,则是一个随机的地址)。

 

用户模式区间:

在X86(32位)下,用户进程可用的地址空间大小为2G,系统可用的地址空间也为2G。在x64(64位)下,用户进程可用的地址空间大小为8T,剩余的地址空间为系统可用的地址空间。

X86下的3G用户内存模式:X86下,可设置用户内存空间为3G,系统内存空间为1G(即大内存模式,与此相关的设置参见BCD 和 /LARGEADDRESSAWARE开关的资料)。但使用大内存模式,限制了内核可用的地址空间,从而限制系统所能创建的线程、栈以及其它资源的数量,因此一般情况下不提倡使用。

X64用户内存模式:用户进程可用的地址空间大小为8T,但需要打开/LARGEADDRESSAWARE链接器开关,否则,程序仍然默认使用2G的用户内存空间。

 

内存的分配原则:

系统分配内存时,首地址必须是分配粒度(64K)的整数倍;同时内存大小必须是页面大小(X86和X64为4K)的整数倍,因为页面时内存分配时的最小单位。

l  内存块:块是一些连续的页面,这些页面具有相同的保护属性,并以相同类型的物理存储器作为后备存储器。一个内存区域一般包括多个内存块。

 

l  页面的保护属性:每个内存页面可以指定不同的保护属性。保护属性大致分为两大类,即以“PAGE_*”命名的属性和以“PAGE_EXECUTE_*”命名的保护属性。

定义如下:

PAGE_NOACCESS:不能访问页面,不能执行代码;

PAGE_READONLY:只能读取页面数据,不能写入数据;不能执行代码;

PAGE_READWRITE:可读写数据,不能执行代码;

PAGE_WRITECOPY:写入数据时创建一份该页面的私有备份;不能执行代码;

 

PAGE_EXECUTE:不能访问页面;

PAGE_EXECUTE_READ:只能读取页面数据,不能写入数据;

PAGE_EXECUTE_READWRITE:可读写数据;

PAGE_EXECUTE_WRITECOPY:写入数据时创建一份该页面的私有备份;

只有使用以“PAGE_EXECUTE_*”命名的保护属性时,才能执行代码。否则,最多只能操作数据,这样能够有效的防止恶意软件执行恶意代码。)

 

物理存储器的构成:

虚拟内存实际上是由内存和硬盘共同构成的。磁盘上的文件称为页交换文件(磁盘上的虚拟内存),页交换文件可增大应用程序可用的内存总量。系统将虚拟地址转换为物理存储器地址的流程如下:

可以看得出来,假如数据在页交换页面上,系统需要在内存和硬盘之间进行数据搬移,而数据搬移的频率越高,硬盘颠簸得越厉害,系统运行越慢。(有时候加大内存比提升CPU速度,更能有效的提升系统运行速度,就是这个道理,因为可以减少内存和硬盘之间的数据来回搬移

 《Windows核心编程》读书心得——内存(内存体系结构)(5)_第1张图片

                                                           (图5 - 2)

内存对齐:

只有对齐了的内存,其访问效率才是最高的,因为如果内存没有对齐,CPU访问它的时候需要修复错位,这个操作将增加时间。

 

获取内存信息的函数:

GetSystemInfo():获得与主机CPU相关的值(分配粒度、页面大小等);

GlobaMemoryStatus():获得当前内存状态的动态信息;

VirtualQuery():获取虚拟内存的详细信息(基地址、保护属性等)。

 

分配和释放内存的函数:

l  分配内存:

VirtualAlloc

PVOID pvAddress,

SIZE_T dwSize,

DWORD fdwAllocationType,

DWORD fdwProtect);

pvAddress:内存地址(若传NULL,则系统自己选定一个地址);

dwSize:需要分配的大小;

fdwAllocationType:分配标志(MEM_RESERVE:预订;  MEM_COMMIT:调拨; MEM_RESET:重置。

fdwProtect:内存的保护属性。(同一物理存储页面,只能有一个保护属性)

 

说明:在分配虚拟内存时,都需要先预订,再调拨。

(1)预订:告知系统,这块内存已经被占用,不能再分配它。只是给内存打上“已占用”的标记。

(2)调拨:真正的给内存分配物理存储器。如果只是预订,是不会分配物理存储器的。只有调拨了的内存,才能用来访问数据。

(3)重置:告知系统,这块内存是闲置的,系统可以重新对该内存进行分配。

        在下次调用VirtualAlloc()时,如果引用到的页面在页交换文件中,系统会直接抛弃这些页面,分配新的页面;如果页面在内存中,那么系统会将其标记为没有修改过,而这块内存可以直接拿来用,但是该内存是没有清零的(这样做的好处是:由于虚拟内存已经关联了物理存储器,就不需要再重新分配,大大提升了效率)。

        对于用户来说,一旦重置了一块内存,在对其重新分配之前,都应将其看做“已释放”的内存,不应当再使用它。

        内存重置的应用很广,我们在程序中释放一块内存的时候,系统通常都是使用“重置”操作,而不是实质性的去释放物理存储器。这就是为什么我们有时候会发现,一块释放了的内存,其中的数据并没有被清零

 

l  释放内存:

VirtualFree

LPVOID pvAddress,

SIZE_T dwSize,

DWORD fdwFreeType);

pvAddress:内存地址(若地址在一个页面的中间,将释放整个页面);

dwSize:释放的内存大小(传0,系统自动释放应释放的内存);

fdwFreeType:释放标志(MEM_DECOMMIT:对该地址指向的第一个页面的内存撤销调拨;MEM_RELEASE:释放该区域所有的内存)。

 

l  更改内存的保护属性:

VirtualProtect()。

你可能感兴趣的:(《Windows核心编程》读书心得——内存(内存体系结构)(5))