在学习OpenHarmony
鸿蒙轻内核源代码的时候,常常会遇到一些数据结构的使用。如果没有掌握它们的用法,会导致阅读源代码时很费解、很吃力。本文会给读者介绍源码中重要的数据结构,双向循环链表Doubly Linked List
。在讲解时,会结合数据结构相关绘图,培养读者们的数据结构的平面想象能力,帮助更好的学习和理解这些数据结构的用法。
1 双向循环链表
双向链表LOS_DL_LIST
的源代码在utils\los_list.h
双向链表头文件中,包含LOS_DL_LIST
结构体定义、inline
内联函数LOS_ListXXX
,还有相关的函数宏定义LOS_DL_LIST_XXXX
。双向链表头文件可以网页访问utils/los_list.h,也可以检出到本地阅读。
1.1 双向链表结构体
双向链表节点结构体LOS_DL_LIST
定义如下。其结构非常简单、通用、抽象,只包含前驱、后继两个节点,负责承上启下的双向链表作用。双向链表不包含任何业务数据信息,一般不会单独使用。通常,双向链表节点和业务数据信息作为结构体成员,一起组成业务结构体来使用,使用示例稍后会有讲述。
typedef struct LOS_DL_LIST {
struct LOS_DL_LIST *pstPrev; /** 指向当前链表节点的前驱节点的指针 */
struct LOS_DL_LIST *pstNext; /** 指向当前链表节点的后继节点的指针 */
} LOS_DL_LIST;
从双向链表中的任意一个节点开始,都可以很方便地访问它的前驱节点和后继节点,这种环状数据结构形式使得双向链表在查找、插入、删除等操作上非常方便。业务场景使用双向链表时,可以定义一个LOS_DL_LIST
类型的全局变量作为双向循环链表Head
头结点,业务结构体的链表成员节点依次挂载在头结点上。还有些业务结构体的双向链表节点作为Head
头节点,依次挂载其他业务结构体的链表成员节点。从Head
节点可以依次遍历下一个节点,Head
节点的前驱节点就是Tail
尾节点。
下面通过鸿蒙轻内核代码中互斥锁结构体LosMuxCB
定义,来了解如何使用双向链表结构体:
typedef struct {
UINT8 muxStat; /**< 互斥锁状态 */
UINT16 muxCount; /**< 互斥锁当前被持有的次数 */
UINT32 muxID; /**< 互斥锁编号ID */
LOS_DL_LIST muxList; /**< 互斥锁的双向链表 */
LosTaskCB *owner; /**< 当前持有锁的任务TCB */
UINT16 priority; /**< 持有互斥锁的任务优先级 */
} LosMuxCB;
互斥锁结构体中包括双向链表LOS_DL_LIST muxList
成员变量和其他包含互斥锁业务信息的成员变量,这里通过双向链表把各个互斥锁链接起来,挂载在头结点LOS_DL_LIST g_unusedMuxList
;通过其他业务成员变量承载业务数据,链表和其他业务成员关系如下图所示:
2 初始化双向链表
2.1 LOS_ListInit(LOS_DL_LIST *list)
LOS_DL_LIST
的两个成员pstPrev
和pstNext
, 是LOS_DL_LIST
结构体类型的指针。需要为双向链表节点申请长度为sizeof(LOS_DL_LIST)
的一段内存空间。为链表节点申请到内存后,可以调用初始化LOS_ListInit(LOS_DL_LIST *list)
方法,把这个节点链接为环状的双向链表。初始化链表时,只有一个链表节点,这个节点的前驱和后继节点都是自身。链表节点初始化为链表,如图所示:
源码如下:
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListInit(LOS_DL_LIST *list)
{
list->pstNext = list;
list->pstPrev = list;
}
2.2 LOS_DL_LIST_HEAD(LOS_DL_LIST list)
除了LOS_ListInit()
,还提供了一个相同功能的函数式宏LOS_DL_LIST_HEAD
,通过直接定义一个双向链表节点,实现将该节点初始化为双向链表。区别于LOS_ListInit()
,在调用函数式宏前,不需要动态申请内存空间。
#define LOS_DL_LIST_HEAD(list) LOS_DL_LIST li