nt内核里的堆管理(2):RtlAllocHeap
ring3里的API HeapAlloc, HeapFree在Kernel32.dll里实现,最后都转到NTDLL.dll里的RtlAllocHeap函数和RtlFreeHeap函数。ring0里要操作堆直接调用ntdll.exe里的RtlAllocHeap函数和RtlFreeHeap函数。这两处地方的两个函数名字一样,所作事情也基本没差别,但千万记住它们是不同的,一个在ring0,一个在ring3。gussing.cnblogs.com
RtlAllocHeap接受一个UINT类型的参数,指明本次操作需要申请多大的内存。nt的堆管理以8字节大小为单位,不管你想申请多大的内存,它返回的都是8倍数大小的区块。再加上每个堆块都需要的管理域(块头),堆管理器内部算出来需要申请的内存大小AllocatSize=(size/8+1)*8(16,24,32。。。),相对应的index=AllocateSize/16(1,2,3…)。根据这个index我们可以在LookAside list组或FreeList组里定位到需要的空闲队列。考虑到队列组只有128大小,index必须小于等于127才能在这两个组内搜索;若index大于127且小于64k,则在FreeList[0]里搜索;若index大于64k的阈值,则直接调用ZwAllocateVirtualMemory申请虚存。让我们分情况看:gussing.cnblogs.com
- index<128gussing.cnblogs.com
先搜索LookAsideList[index],若有合适的,直接返回。LookAsideList[index]里什么也没有,则搜索FreeList[index],若还是没有,则顺着FreeList[index+1], FreeList[index+1]一路找下去,直到找到足够大的块为止。假设找到的块是Block1, 因为它比我们需要的AllocateSize要大,所以就需要进行拆分,前半部为AllocateSize大小返回给调用者,后半部大小BlockSize看情况处理:倘若Block1是所在队列里的最后一块,就调用RtlInsertFreeBlock插回到FreeList里去。RtlInsertFreeBlock会根据传入的block大小进行拆分操作,若block比127大,会分成几份小于127的插到相应的队列中。若Block1后边还跟着一块空闲块,就把该块从相应的list里取出,跟Block1合成一块,然后调用RtlInsertFreeBlock。但是如果搜完整个FreeList都找不到,就需要做下面的事情:
- 128<=index<64kgussing.cnblogs.com
index已经超过了LookAsideList和FreeList的范围,或者这俩队列里都没有合适的块,所以没法从它们里取,但是它又没大到一定程度,直接申请虚存也不合适,这时候就需要FreeList[0]出场了。如前一篇所说,算出来的block index是不可能为0的,所以FreeList[0]就划出来专门存放大块内存。FreeList[0]队列里的堆块和其他队列不同,因为他们的大小是不一致的。FreeList[1]队列里的堆块block index一定是1,FreeList[2]队列里的堆块Block index一定是2,但是FreeList[0]里堆块block index可以是128到64k间的任何数值。从FreeList[0]申请内存时,需要顺着该队列一路搜索直到找到足够大的Block。若能找到这样的块Block2,则将之取出,做与前面Block1一摸一样的拆分。若找不到足够大的堆块,就要在本Segment的保留内存区域里提交一块相应大小的块,然后返回。若倒霉到家连Segment里保留的内存区域也用完了,则申请新的Segment。新申请的Segment内存大部分都是保留的,只提交了AllocateSize大小的块,以便返回给用户。gussing.cnblogs.com
- index>=64kgussing.cnblogs.com
申请如此巨大的内存,无论哪个队列都派不上用场了,所以直接申请虚存,申请到的块保留在VirtualAllocatedBlocks队列里,释放的时候也是直接释放虚存,而不是插入到相应的队列里缓存起来。
最后让我们看看批发的大块内存时如何被打成一堆堆小碎片的。起初的时候Heap里只有一个Segment,保留了一片巨大的内存区域但是没提交,这时候我们开始申请小内存:找LookAsideList,没有;搜索FreeList,也没有;开始搜索FreeList[0],还是什么也没有;只好到Segment里提交一块,这次总算有了。就这样经过多次小额申请后,Segment里的大块内存就被分成了一片片的小碎片。gussing.cnblogs.com