回复
数据结构和操作系统知识在openHarmony源码分析中学习! 原创
HUAWEI_Engineer
发布于 2023-12-14 12:48
浏览
1收藏
数据结构中列表的定义和实现 C++
::: hljs-cent
:::
++谁是鸿蒙内核最重要的结构体 ? 一定是: LOS_DL_LIST(双向链表), 它长这样。++
linux 中是 list_head, 很简单,只有两个指向自己的指针,但因为太简单,所以不简单。站长更愿意将它比喻成人的左右手,其意义是通过寄生在宿主结构体上来体现,可想象成在宿主结构体装上一对对勤劳的双手,它真的很会来事,超级活跃分子,为宿主到处拉朋友,建圈子
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListInit(LOS_DL_LIST *list)
{
list->pstNext = list;
list->pstPrev = list;
}
//将指定节点挂到双向链表头部
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListAdd(LOS_DL_LIST *list, LOS_DL_LIST *node)
{
node->pstNext = list->pstNext;
node->pstPrev = list;
list->pstNext->pstPrev = node;
list->pstNext = node;
}
//将指定节点从链表中删除,自己把自己摘掉
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListDelete(LOS_DL_LIST *node)
{
node->pstNext->pstPrev = node->pstPrev;
node->pstPrev->pstNext = node->pstNext;
node->pstNext = NULL;
node->pstPrev = NULL;
}
//将指定节点从链表中删除,并使用该节点初始化链表
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListDelInit(LOS_DL_LIST *list)
{
list->pstNext->pstPrev = list->pstPrev;
list->pstPrev->pstNext = list->pstNext;
LOS_ListInit(list);
}
强大的宏
除了内联函数,对双向链表的初始化,偏移定位,遍历 等等操作提供了更强大的宏支持。使内核以极其简洁高效的代码实现复杂逻辑的处理
#define LOS_DL_LIST_HEAD(list) LOS_DL_LIST list = { &(list), &(list) }
//获取指定结构体内的成员相对于结构体起始地址的偏移量
#define LOS_OFF_SET_OF(type, member) ((UINTPTR)&((type *)0)->member)
//获取包含链表的结构体地址,接口的第一个入参表示的是链表中的某个节点,第二个入参是要获取的结构体名称,第三个入参是链表在该结构体中的名称
#define LOS_DL_LIST_ENTRY(item, type, member) \
((type *)(VOID *)((CHAR *)(item) - LOS_OFF_SET_OF(type, member)))
//遍历双向链表
#define LOS_DL_LIST_FOR_EACH(item, list) \
for (item = (list)->pstNext; \
(item) != (list); \
item = (item)->pstNext)
//遍历指定双向链表,获取包含该链表节点的结构体地址,并存储包含当前节点的后继节点的结构体地址
#define LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(item, next, list, type, member) \
for (item = LOS_DL_LIST_ENTRY((list)->pstNext, type, member), \
next = LOS_DL_LIST_ENTRY((item)->member.pstNext, type, member); \
&(item)->member != (list); \
item = next, next = LOS_DL_LIST_ENTRY((item)->member.pstNext, type, member))
//遍历指定双向链表,获取包含该链表节点的结构体地址
#define LOS_DL_LIST_FOR_EACH_ENTRY(item, list, type, member) \
for (item = LOS_DL_LIST_ENTRY((list)->pstNext, type, member); \
&(item)->member != (list); \
item = LOS_DL_LIST_ENTRY((item)->member.pstNext, type, member))
LOS_OFF_SET_OF 和 LOS_DL_LIST_ENTRY
这里要重点说下 LOS_OFF_SET_OF和LOS_DL_LIST_ENTRY两个宏,个人认为它们是链表操作中最关键,最重要的宏。在读内核源码的过程会发现LOS_DL_LIST_ENTRY高频的出现,它们解决了通过结构体的任意一个成员变量来找到结构体的入口地址。 这个意义重大,因为在运行过程中,往往只能提供成员变量的地址,那它是如何做到通过个人找到组织的呢?
LOS_OFF_SET_OF 用于找到成员变量在结构体中的相对偏移位置, 在系列篇 (用栈方式篇) 中 已说过 鸿蒙采用的是递减满栈的方式。而使用 ((type *)0)->member 是获取 struct 成员偏移量的技巧,需要编译器的支持,这种方法背后的想法是让编译器假设结构起始地址位于零并计算成员的地址。 以ProcessCB 结构体举例
// ...
LOS_DL_LIST pendList; /**< Block list to which the process belongs | 进程所在的阻塞列表,进程因阻塞挂入相应的链表.*/
LOS_DL_LIST childrenList; /**< Children process list | 孩子进程都挂到这里,形成双循环链表*/
LOS_DL_LIST exitChildList; /**< Exit children process list | 要退出的孩子进程链表,白发人要送黑发人.*/
LOS_DL_LIST siblingList; /**< Linkage in parent's children list | 兄弟进程链表, 56个民族是一家,来自同一个父进程.*/
LOS_DL_LIST subordinateGroupList; /**< Linkage in group list | 进程组员链表*/
LOS_DL_LIST threadSiblingList; /**< List of threads under this process | 进程的线程(任务)列表 */
LOS_DL_LIST waitList; /**< The process holds the waitLits to support wait/waitpid | 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息*/
} LosProcessCB;
©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
标签
赞
2
收藏 1
回复
相关推荐