不论是哪个操作系统都会涉及链表的操作, Linux、 Qemu 、 VirtualBox 以及RTthread、 Ucos等 RTOS都有链表。链表的操作主要有: 初始化链表头、头部插入、尾部插入、遍历结点、任意节点交互等。 在阅读 edk2代码时,内核并不提供链表的遍历接口, 要想遍历则需要使用 for循环进行链表操作。
edk2中链表操作和l inux内核的没有什么太大差异
代码路径: // uefi\edk2\MdePkg\Library\BaseLib\LinkedList.c
1- 链表初始化
LIST_ENTRY *
EFIAPI
InitializeListHead (
IN OUT LIST_ENTRY *ListHead
)
{
ASSERT (ListHead != NULL);
ListHead->ForwardLink = ListHead;
ListHead->BackLink = ListHead;
return ListHead;
}
上述代码构建一个双向循环链表头
2- 尾部插入
LIST_ENTRY *
EFIAPI
InsertTailList (
IN OUT LIST_ENTRY *ListHead,
IN OUT LIST_ENTRY *Entry
)
{
//
// ASSERT List not too long and Entry is not one of the nodes of List
//
ASSERT_VERIFY_NODE_IN_VALID_LIST (ListHead, Entry, FALSE);
Entry->ForwardLink = ListHead;
Entry->BackLink = ListHead->BackLink;
Entry->BackLink->ForwardLink = Entry;
ListHead->BackLink = Entry;
return ListHead;
}
如下图所示:
3- 头部插入
LIST_ENTRY *
EFIAPI
InsertHeadList (
IN OUT LIST_ENTRY *ListHead,
IN OUT LIST_ENTRY *Entry
)
{
//
// ASSERT List not too long and Entry is not one of the nodes of List
//
ASSERT_VERIFY_NODE_IN_VALID_LIST (ListHead, Entry, FALSE);
Entry->ForwardLink = ListHead->ForwardLink;
Entry->BackLink = ListHead;
Entry->ForwardLink->BackLink = Entry;
ListHead->ForwardLink = Entry;
return ListHead;
}
如下图所示:
4- 其它链表操作
GetPreviousNode (LIST_ENTRY *List, LIST_ENTRY *Node)
GetNextNode (LIST_ENTRY *List, LIST_ENTRY *Node)
InsertTailList (LIST_ENTRY *ListHead, LIST_ENTRY *Entry)
InsertHeadList (LIST_ENTRY *ListHead, LIST_ENTRY *Entry)
// 以下时 linux内核链表参数
void list_add(struct list_head *new, struct list_head *head)
void list_add_tail(struct list_head *new, struct list_head *head)
Linux 内核链表操作代码是相当犀利,覆盖了各个场景,加锁的,hash及红黑树之类的。熟悉linux 内核中链表操作,再看其他代码,相对简单很多
5- 总结
链表是构建复杂系统的基石,学习时要注意
- 结合代码画图
- edk2中链表操作和 linux内核参数是相反的
- edk2中不提供遍历接口,需要手动 for 循环
- edk2中使用 CR宏和 container_of一样的效果