首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >OpenHarmony 轻内核A核源码分析系列一 数据结构-双向循环链表

OpenHarmony 轻内核A核源码分析系列一 数据结构-双向循环链表

原创
作者头像
小帅聊鸿蒙
发布2025-05-29 16:20:13
发布2025-05-29 16:20:13
1210
举报
文章被收录于专栏:鸿蒙开发笔记鸿蒙开发笔记

在学习OpenHarmony鸿蒙轻内核源代码的时候,常常会遇到一些数据结构的使用。如果没有掌握它们的用法,会导致阅读源代码时很费解、很吃力。本文会给读者介绍源码中重要的数据结构,双向循环链表Doubly Linked List。在讲解时,会结合数据结构相关绘图,培养读者们的数据结构的平面想象能力,帮助更好的学习和理解这些数据结构的用法。

1 双向循环链表

双向循环链表Doubly Linked List的源代码在kernel\include\los_list.h双向链表头文件中,包含LOS_DL_LIST结构体定义、inline内联函数LOS_ListXXX,还有相关的函数宏定义LOS_DL_LIST_XXXX。双向链表头文件可以网页访问 kernel/include/los_list.h ,也可以检出到本地阅读。


1.1 双向链表结构体

双向链表节点结构体LOS_DL_LIST定义如下。双向链表的定义,如何使用,这在OpenHarmony LiteOS-A内核和OpenHarmony LiteOS-M内核里是一致的,不再赘述。

代码语言:shell
复制
typedef struct LOS_DL_LIST {
    struct LOS_DL_LIST *pstPrev; /** 指向当前链表节点的前驱节点的指针 */
    struct LOS_DL_LIST *pstNext; /** 指向当前链表节点的后继节点的指针 */
} LOS_DL_LIST;

2 初始化双向链表

OpenHarmony LiteOS-A内核和OpenHarmony LiteOS-M内核中的双向链表的初始化也是一致的,都提供了内联函数LOS_ListInit(),还提供了一个相同功能的函数式宏LOS_DL_LIST_HEAD。可以查看OpenHarmony LiteOS-M内核源码剖析的双向链表部分。

3 判断空链表

3.1 LOS_ListEmpty(LOS_DL_LIST *list)

该内联函数用于判断链表是否为空。和OpenHarmony LiteOS-M内核的函数一致。

源码如下:

代码语言:shell
复制
LITE_OS_SEC_ALW_INLINE STATIC INLINE BOOL LOS_ListEmpty(LOS_DL_LIST *list)
{
    return (BOOL)(list->pstNext == list);
}

4 插入双向链表节点

双向链表提供三种链表节点插入方法,在指定链表节点后面插入LOS_ListAdd、尾部插入LOS_ListTailInsert、头部插入LOS_ListHeadInsert。在头部插入的节点,从头部开始遍历时第一个遍历到,从尾部插入的节点,最后一个遍历到。OpenHarmony LiteOS-A内核除了提供链表节点插入还提供了方法把一个链表插入另外一个双向链表的方法,在指定链表节点后面插入新链表LOS_ListAddList、尾部插入新链表LOS_ListTailInsertList、头部插入新链表LOS_ListHeadInsertList

4.1 INLINE VOID LOS_ListAddList(LOS_DL_LIST oldList, LOS_DL_LIST newList)

该内联函数往双向链表节点*oldList所在的双向链表中插入链表节点*newList所属的双向链表,插入后老双向链表的头结点和新双向链表的尾节点相连,老双向链表的尾节点与新双向链表的头节点相连。使用该方法插入的新的双向链表靠近双向链表的头部,即从头部开始遍历时会首先遍历到新链表的头部节点。如下图所示:

图示:

源码如下:

代码语言:shell
复制
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListAddList(LOS_DL_LIST *oldList, LOS_DL_LIST *newList)
{
    LOS_DL_LIST *oldListHead = oldList->pstNext;
    LOS_DL_LIST *oldListTail = oldList;
    LOS_DL_LIST *newListHead = newList;
    LOS_DL_LIST *newListTail = newList->pstPrev;

    oldListTail->pstNext = newListHead;
    newListHead->pstPrev = oldListTail;
    oldListHead->pstPrev = newListTail;
    newListTail->pstNext = oldListHead;
}

4.2 INLINE VOID LOS_ListTailInsertList(LOS_DL_LIST oldList, LOS_DL_LIST newList)

该内联函数往链表节点*oldList所在的双向链表中插入链表节点*newList所属的链表,插入后老双向链表的尾结点和新双向链表的头节点相连,老双向链表的头节点与新双向链表的尾节点相连。使用该方法插入的新的双向链表的尾部靠近老双向链表的头部,即从头部开始遍历时会首先遍历到新链表的尾部节点。。如下图所示:

图示:

源码如下:

代码语言:shell
复制
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListTailInsertList(LOS_DL_LIST *oldList, LOS_DL_LIST *newList)
{
    LOS_ListAddList(oldList->pstPrev, newList);
}
DD一下:欢迎大家关注工粽号<程序猿百晓生>,可以了解到以下知识点。
代码语言:erlang
复制
`欢迎大家关注工粽号<程序猿百晓生>,可以了解到以下知识点。`
1.OpenHarmony开发基础
2.OpenHarmony北向开发环境搭建
3.鸿蒙南向开发环境的搭建
4.鸿蒙生态应用开发白皮书V2.0 & V3.0
5.鸿蒙开发面试真题(含参考答案) 
6.TypeScript入门学习手册
7.OpenHarmony 经典面试题(含参考答案)
8.OpenHarmony设备开发入门【最新版】
9.沉浸式剖析OpenHarmony源代码
10.系统定制指南
11.【OpenHarmony】Uboot 驱动加载流程
12.OpenHarmony构建系统--GN与子系统、部件、模块详解
13.ohos开机init启动流程
14.鸿蒙版性能优化指南
.......

4.3 INLINE VOID LOS_ListHeadInsertList(LOS_DL_LIST oldList, LOS_DL_LIST newList)

该内联函数和LOS_ListAddList()实现同样的功能。

源码如下:

代码语言:shell
复制
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListHeadInsertList(LOS_DL_LIST *oldList, LOS_DL_LIST *newList)
{
    LOS_ListAddList(oldList, newList);
}

5 删除双向链表节点

双向链表提供两种链表节点的删除方法,删除指定节点LOS_ListDelete()、删除并初始化为一个新链表LOS_ListDelInit()


6 获取双向链表节点

双向链表还提供宏定义获取指定链表节点的前驱节点LOS_DL_LIST_LAST,获取指定链表节点的后继节点LOS_DL_LIST_FIRSTOpenHarmony LiteOS-M内核源码分析时,已经了解这2个宏,我们继续看下2个判断的宏,即LOS_DL_LIST_IS_ENDLOS_DL_LIST_IS_ON_QUEUE

6.1 LOS_DL_LIST_IS_END

通过判断两个链表节点是否相等,通常第一个为尾节点,第二个是变化的链表节点,判断是否遍历到尾节点。

源码如下:

代码语言:c
复制
#define LOS_DL_LIST_IS_END(list, node) ((list) == (node) ? TRUE : FALSE)

6.2 LOS_DL_LIST_IS_ON_QUEUE

用于判断链表节点是否在循环链表中,即前序、后继节点都不为空。

源码如下:

代码语言:c
复制
#define LOS_DL_LIST_IS_ON_QUEUE(node) ((node)->pstPrev != NULL && (node)->pstNext != NULL)

7 遍历双向循环链表节点

双向循环链表提供两种遍历双向链表的方法,LOS_DL_LIST_FOR_EACHLOS_DL_LIST_FOR_EACH_SAFEOpenHarmony LiteOS-M内核源码分析时,已经分析过这2个宏。


8 获取链表节点所在结构体

通过宏LOS_DL_LIST_ENTRY可以获取成员变量相对于结构体的内存地址偏移量。通过宏LOS_DL_LIST_ENTRY``即可以获取双向链表节点所在的业务结构体的内存地址。OpenHarmony LiteOS-M内核源码分析时,已经分析过这2个宏。我们分析下OpenHarmony LiteOS-A内核中独有的三个宏,即LOS_ListPeekHeadTypeLOS_ListRemoveHeadTypeLOS_ListNextType`。

8.1 LOS_ListPeekHeadType

函数宏中的三个参数分别为:业务结构体类型名称type,作为结构体成员的双向链表成员变量名称element,双向链表头节点指针list。通过调用该宏函数LOS_ListPeekHeadType即可以获取双向链表第一个业务链表节点所在的业务结构体的内存地址。

⑴处表示如果是只有头链表结点的空双向循环链表,则返回NULL。⑵如果链表不为空,调用宏LOS_DL_LIST_ENTRY获取业务结构体的内存地址。该宏相对宏LOS_DL_LIST_ENTRY更安全,如果是空的双向链表,调用后者,返回的内存地址不是业务结构体的地址,会发生踩内存错误。

源码如下:

代码语言:shell
复制
#define LOS_ListPeekHeadType(list, type, element) ({             \
    type *__t;                                                   \
⑴  if ((list)->pstNext == list) {                               \
        __t = NULL;                                              \
    } else {                                                     \
⑵      __t = LOS_DL_LIST_ENTRY((list)->pstNext, type, element); \
    }                                                            \
    __t;                                                         \
})

8.2 LOS_ListRemoveHeadType

函数宏中的三个参数分别为:业务结构体类型名称type,作为结构体成员的双向链表成员变量名称element,双向链表头节点指针list。通过调用该宏函数LOS_ListRemoveHeadType即可以获取双向链表第一个业务链表节点所在的业务结构体的内存地址,并把第一个业务链表节点从双向链表中删除。

⑴处表示如果是只有头链表结点的空双向循环链表,则返回NULL。⑵如果链表不为空,获取业务结构体的内存地址,然后调用LOS_ListDelete删除链表节点。

源码如下:

代码语言:shell
复制
#define LOS_ListRemoveHeadType(list, type, element) ({           \
    type *__t;                                                   \
    if ((list)->pstNext == list) {                               \
⑴      __t = NULL;                                              \
    } else {                                                     \
⑵      __t = LOS_DL_LIST_ENTRY((list)->pstNext, type, element); \
        LOS_ListDelete((list)->pstNext);                         \
    }                                                            \
    __t;                                                         \
})

8.3 LOS_ListNextType

函数宏中的四个参数分别为:业务结构体类型名称type,作为结构体成员的双向链表成员变量名称element,双向链表头节点指针list,双向链表中的一个节点item。通过调用该宏函数LOS_ListNextType即可以获取双向链表指定链表节点的下一个节点所在的业务结构体的内存地址。

⑴处表示如果指定链表节点的下一个节点是头结点,则返回NULL。否则执行⑵,获取指定链表节点的下一个节点所在的业务结构体的内存地址。

源码如下:

代码语言:shell
复制
#define LOS_ListNextType(list, item, type, element) ({           \
    type *__t;                                                   \
    if ((item)->pstNext == list) {                               \
⑴      __t = NULL;                                              \
    } else {                                                     \
⑵      __t = LOS_DL_LIST_ENTRY((item)->pstNext, type, element); \
    }                                                            \
    __t;                                                         \
})

9 遍历包含双向链表的结构体

双向链表提供三个宏定义来遍历包含双向链表成员的结构体,LOS_DL_LIST_FOR_EACH_ENTRYLOS_DL_LIST_FOR_EACH_ENTRY_SAFELOS_DL_LIST_FOR_EACH_ENTRY_HOOKOpenHarmony LiteOS-M内核源码分析时,已经分析过这3个宏。


小结

掌握鸿蒙轻内核的双向循环链表LOS_DL_LIST这一重要的数据结构,会给进一步学习、分析鸿蒙轻内核源代码打下了基础,让后续的学习更加容易。

写在最后

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:

  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力;
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识;
  • 想要获取更多完整鸿蒙最新学习知识点,可关注B站:码牛课堂;

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 双向循环链表
    • 1.1 双向链表结构体
  • 2 初始化双向链表
  • 3 判断空链表
    • 3.1 LOS_ListEmpty(LOS_DL_LIST *list)
  • 4 插入双向链表节点
    • 4.1 INLINE VOID LOS_ListAddList(LOS_DL_LIST oldList, LOS_DL_LIST newList)
    • 4.2 INLINE VOID LOS_ListTailInsertList(LOS_DL_LIST oldList, LOS_DL_LIST newList)
      • DD一下:欢迎大家关注工粽号<程序猿百晓生>,可以了解到以下知识点。
    • 4.3 INLINE VOID LOS_ListHeadInsertList(LOS_DL_LIST oldList, LOS_DL_LIST newList)
  • 5 删除双向链表节点
  • 6 获取双向链表节点
    • 6.1 LOS_DL_LIST_IS_END
    • 6.2 LOS_DL_LIST_IS_ON_QUEUE
  • 7 遍历双向循环链表节点
  • 8 获取链表节点所在结构体
    • 8.1 LOS_ListPeekHeadType
    • 8.2 LOS_ListRemoveHeadType
    • 8.3 LOS_ListNextType
  • 9 遍历包含双向链表的结构体
  • 小结
  • 写在最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档