part 10.1 - 内存管理
标签(空格分隔): win32汇编
内存管理
1)内存管理基础
- windows内存管理的层次:
- 标准内存管理函数:在默认堆中分配和释放内存,常规意义上的内存管理函数。
- 堆管理函数:有效地故案例内存和进程的地址空间。(堆:程序初始化时向操作系统申请并预留的大内存快)
- 虚拟内存管理函数:保留/提交/释放虚拟内存,在虚拟内存页上改变保护位,锁定虚拟内存页
- 内存映射文件函数:将文件直接映射到进程的地址空间中。
堆:
- win32中分两种堆:默认堆、私有堆
- 默认堆:一个进程只有一个,线程共享。
- 私有堆:一个进程可有多个,线程独占。
win16中内存有“全局”和“本地”之分;
全局堆(Gloabl Heap)是系统中所有进程所共有的堆,每个进程有一个私有的本地堆(Local Heap)
Win32下,考虑到安全,所有进程使用的全局堆废除,本地堆改名为进程堆(Process Heap)即默认堆。内存的状态
- 一个进程地寻址空间为4GB
- 高端2GB共操作系统内核使用。
低端2GB应用程序使用。
- 该2GB包括应用程序、用户DLL代码、静态数据段。
- 剩余的空间才是内存函数可使用的地址空间。
PS:可分配内存的大小还受制于物理内存和物理交换文件的大小。
GlobalMemeoryStatus 可以获取内存信息。
windows可以根据内存使用的需求自动调整交换文件的大小。
2)标准内存管理函数
- 标准内存管理函数的功能是在进程的默认堆中申请和释放内存块。
函数组成
函数名 作用 参数 返回值 GlobalAlloc* 申请固定的内存块 Flag : 标志
dwBytes:内存大小成功: 地址 or 句柄
失败: NULLGlobalReAlloc* 修改内存块大小 lpMemory:内存指针
dwBytes:新的大小
uFlags:是否允许移动成功:新的指针
失败:NULLGlobalFree 释放固定内存块 lpMemory or hMemory 成功 :NULL /失败:lpMemory GlobalLock 内存上锁 hMemory 返回举兵指针 GlobalUnlock 内存解锁 hMemory 成功:非0值 GlobalDiscard 当可丢弃内存块的锁定计数为0时,
可使用该函数丢弃hMemory GlobalFlags 3 3 3 GlobalHandle 4 4 4 GlobalSize 5 5 5
- GlobalAlloc Flag参数
- GMEM_FIXED:申请固定内存块
- GMEM_ZERO:初始化为0
- GMEM_MOVEABLE:可移动内存块 - 操作系统根据内存情况移动内存块,以防止碎片,
此时成功时返回的是内存句柄而不是指针- GMEM_DISCARDBAABLE:windows急需内存时可以将他从物理内存中丢弃。
当对他使用GlobalLock返回NULL时,说明已经被Windows丢弃。- GlbalReAlloc-uFlag
- GMEM_MOVEABLE:必要的时候可移动
- GlobalLock(UnLock):维护一计数器,lock+1,Unlock-1,计数器为0时才真正解锁。
3)堆管理函数
- 堆管理函数:私有堆相当于在默认堆中保留了一大块内存,使用堆管理函数可以在这个保留的内存块中分配内存。
- 同步问题:
- 默认堆的访问时顺序进行的。(线程间同步)
- 私有堆空间线程独占
- 申请私有堆的过程是独占的,使用默认参数申请时,系统会进行独占检测。
从默认堆申请私有堆时操作系统的执行过程:
- 遍历已分配的和空闲的内存块的链接表 ——-(什么玩意
- 寻找一个空闲内存块地址
- 通过将空闲内存快标记为”已分配”来分配新的内存块。(临界区)
- 将新内存块添加给内存块链接表。(临界区)
PS:步骤3、4是临界区。系统会进行独占的检测工作。
函数组成
函数名 作用 返回值 HeapCreate 创建私有堆 成功:返回内存地址
失败:NULL or 出错代码HeapDestory 释放私有堆所有内存快或
返回堆占用的物理内存和保留的地址空间返回系统成功: TRUE
失败HeapAlloc 在堆中分配或释放内存块 成功:内存块指针
失败:NULL或错误代码HeapFree 释放分配的内存块 成功: 非0值<失败>NULL 或 错误代码 HeapReAlloc 重新调整大小 成功:返回新内存块指针 GetProcessHeap 获取默认堆的堆句柄 默认堆句柄 GetProcessHeaps 列出进程中所有的堆 成功:参数lpHeaps获取接受堆句柄的缓冲区,包括默认堆 HeapWalk 遍历堆栈 lpEntry获取包含内存块信息的PROCESS_HEAP_ENTRY结构,
若还有堆则返回true否侧flase
多线程编程中必须使用Heaolock其他函数:
- HeapValidate:验证堆的完整性
- HeapLock、HeapUnlock:锁定堆、解锁堆,某线程锁定堆后即成为该堆所有者,
其他线程访问该堆将被阻塞 - HeapCompact:合并队中的空闲内存快,释放不在使用中的内存页面
- HeapSize:返回堆中某个内存块的大小
HeapCreate中的flOptions参数
- HEAP_NO_SERIALIZE:不进行独占检测(祥见P339)
由上知,建立私有堆,操作系统会进行独占检测,但是也存在不需要独占 检测的情况
1. 进程只使用一个线程
2. 进程使用多个线程,但每个线程之访问属于自己的私有堆
3. 进程使用多个线程,但程序中已经有其他措施来保证他们不会同时取访问同一个私有堆- HEAP_NO_SERIALIZE:不进行独占检测(祥见P339)
- HEAP_GENERATE_EXCEPTIONS:函数失败时返回错误代码
PS: 错误代码必然大于0c0000000h,此后是系统空间,不会和成功的情况冲突。
HeapDistory可以用来释放私有堆,同时也可以释放默认堆,
默认堆的句柄可以通过GetProcessHeap获得HeapReAlloc 中的dwFlags参数:
若指定 HEAP_REALLOC_IN_PLACE_ONLY则函数会在需要的时候自动移动内存块
4)虚拟内存管理函数
- 程序运行时,进程的每个地址每个都可能处于三种状态中的一种
- 占用状态:线程地址已经映射到实际的物理内存中(已提交状态)
- 自由状态:没有映射到物理内存中,线程地址当前也没有被程序使用
- 保留状态:线程地址没有映射到物理内存中,但他不会被使用,知道程序希望使用它位止
任何自由状态的地址在能够被使用前首先被分配位保留或已提交状态。
- 虚拟内存管理函数的功能
- 使用虚拟内存管理函数可指定内存块位于那个线程地址。
- 将内存的状态从提交变为保留
- 将保护模时从PAGE_READWRITE改为PAGE_READONLY
- 锁定一页内存
函数组成
VirtualAlloc 、 VirtualFree:地址空间的分配和释放
- VirtualAlloc可以指定一个地址来保留或提交,保留只是组织其他内存分配函数对该段地址的请求
- VirtualAlloc保留一段地址之后,再多次调用以提交该地址空间的不同页面,故该段地址空间的不同页面可能处于不同状态,而调用VirtualFree要求该地址空间状态相同。
- VirtualAlloc提交时,操作系统根据制定内存块,将制定内存块涉及的页面全部提交(制定内存块不一定是整页面),返回有所偏移的地址
VirtualLock、VirtualUnlock :内存页进行锁定和解锁
- VirtualQuery、VirtualQueryEx:查询内存页状态
- VirtualProtect、VirtualProtectEx:改变内存页的保护属性
假设一个进程要处理一个大小变化的数据流,当CPU忙时,分到的时间片可能无法解决太大的数据,可以使用该函数位一块内存的顶端页设置PAGE_NOACCESS保护位,任何想要访问该内存的请求都会发生一个异常,通过捕获该一场就可以来做出相应对策
- RtlMoveMemory:移动内存
- RtlFillMomory:以dbFill参数填充内存块
- RtlZeroMomory:以0填充内存块
- IsBadCodePtr:测试某个指针指向的单个字节是否可读
- IsBadReadPtr:测试某段内存是否可读
- IsBadWritePtr:测试某段内存是否可写
- IsBadStringPtr:测时一个null-terminated String 是否可读