![Windows内核编程](https://wfqqreader-1252317822.image.myqcloud.com/cover/592/39130592/b_39130592.jpg)
3.7 链表
内核在很多内部数据结构中使用了环形双向链表。例如,系统中的所有进程使用EPROCESS
结构进行管理,这些结构就用一个环形双向链表连接在一起,其中链表的头部存储在内核变量PsActiveProcessHead
中。
所有这样的链表都使用相似的方式围绕着LIST_ENTRY
结构进行构建。这个结构定义如下:
![000](https://epubservercos.yuewen.com/B7706C/20516006501584606/epubprivate/OEBPS/Images/042-1.jpg?sign=1739288729-Vi1EDG27fh6yDxYLiNykzS4ZxIgfUUot-0-e55720460b85368391dccaedb33d6c96)
图3-2展示了一个这样的链表,它包含一个头和三个(LIST_ENTRY
的)实例。
![000](https://epubservercos.yuewen.com/B7706C/20516006501584606/epubprivate/OEBPS/Images/042-2.jpg?sign=1739288729-oUAtT3S6WyNGXo0pwsqoc9BWT2qlr11M-0-5caae25442e1d30d526a3f7614979ff7)
图3-2 环形链表
将一个LIST_ENTRY
结构嵌入需要成为链表项的真正结构中。比如在EPROCESS
结构中,ActiveProcessLinks
字段就是LIST_ENTRY
类型,指向后一个和前一个EPROCESS
结构中的LIST_ENTRY
对象。链表的头部另行存放,在进程中,是放在PsActiveProcessHead
里。给出LIST_ENTRY
的地址,可以用CONTAINING_RECORD
宏获得指向真正的数据结构的指针。
举个例子,假设现在需要管理一个链表,链表的类型为MyDataItem结构,定义如下:
![000](https://epubservercos.yuewen.com/B7706C/20516006501584606/epubprivate/OEBPS/Images/042-3.jpg?sign=1739288729-hakS5a83kRrQmzCqSKixJOqToWbWh4Af-0-c90bb0bf4504e595ff65a1ee19ec01b8)
在操作这样的链表时,需要有一个存储在变量里的链表头部。这意味着最自然的遍历链表的方式是使用LIST_ENTRY
的Flink
字段,它指向链表中的下一个LIST_ENTRY
。给定一个指向LIST_ENTRY
的指针之后,我们接着想要的是包含这个LIST_ENTRY
字段的MyDataItem
结构。这里就要用到CONTAINING_RECORD
宏了:
![000](https://epubservercos.yuewen.com/B7706C/20516006501584606/epubprivate/OEBPS/Images/042-4.jpg?sign=1739288729-hmjqlm0aQr95zyfUyylaxnIPAAgqyyWp-0-94e0cb7262f9f18a1370547e66202610)
这个宏能够执行正确的偏移量计算,并对结果进行强制类型转换(本例中是转换成MyDataItem)。
表3-5显示了常见的链表操作函数。所有这些函数的执行时间都为常量。
表3-5 环形链表的操作函数
![000](https://epubservercos.yuewen.com/B7706C/20516006501584606/epubprivate/OEBPS/Images/043-1.jpg?sign=1739288729-r5iD0rId7Kzhqerp403fppXvC4mMCt6w-0-8ff407aa80f6614b2e232abbe0bf8423)
表3-5中的最后3个函数使用了一种称为自旋锁(spinlock)的同步原语来执行原子化的操作。自旋锁将在第6章中讨论。