① 定义一个链表头,单链表为SINGLE_LIST_ENTRY,双链表为LIST_ENTRY。然后要对链表头进行初始化。单链表链表头初始化要将它的Next域设为NULL,双链表直接调用InitializeListHead。
② 往链表中插入元素:
对于单链表使用PushEntryList双链表可以从链表头也可以从链表尾插入:InsertHeadList,InsertTailList。这三个函数都有两个参数,第一个参数为指向链表头的指针,第二个参数为要插入的元素的链接域。
③ 从链表中删除元素:
与插入元素类似,单链表使用PtryList,双链表使用RemoveEntryList,RemoveHeadList,RemoveTailList。他们的参数都是指向链表头的指针。
④ 为了得到链表中的元素,我们使用CONTAINING_RECORD宏。
后备链表
如果你需要固定尺寸的内存块,但是你事先并不知道它的大小和使用频率,这样的话为了性能的原因,你还是使用后备列表(Lookaside Lists)吧,后备列表是只有内核模式才有的。
后备列表是一组事先分配的相同尺寸的内存块。这些块有些在使用,有些没被使用。当有内存分配请求的时候,系统会遍历这个列表寻找最近的未分配的块。如果未分配的块找到了,分配请求就很快被满足了。否则系统必须从分页或不分页内存池去分配。根据列表中分配行为发生的频率,系统会自动调整未分配块的数量来满足分配请求,分配的频率越高,会有越多的块被存储在后备列表中。后备列表如果总是不被使用,也会自动减少空间大小。
使用后备链表:
① 为一个PAGED_LOOKASIDE_LIST或NPAGED_LOOKASIDE_LIST对象保留非分页内存。
这里这两种对象都是用来管理后备链表,它本身只能位于非分页内存,因为系统需要在提升的IRQL级上访问它。这两个对象十分相似,但分页类型的链表使用FAST_MUTEX来同步,而非分页类型的链表要使用自旋锁。
② 调用相应的初始化函数对Lookaside链表进行初始化。
③ 从Lookaside链表上面分配存储,使用AllocateFrom()函数。
④ 使用完之后,将从Lookaside上面申请的存储空间返回Lookaside。
⑤ 调用Delete函数删除链表。
服务函数 |
描述 |
ExInitializeNPagedLookasideList |
初始化lookaside链表 |
ExAllocateFromNPagedLookasideList |
分配一个固定大小的内存块 |
ExFreeToNPagedLookasideList |
将一个内存块释放回lookaside链表 |
ExDeleteNPagedLookasideList |
删除lookaside链表 |