循环合并nump数组_鸿蒙内核源码分析:双向循环链表篇

63a94d2a3e617e00c099418243957dfc.png

作者 | 深入研究鸿蒙,鸿蒙内核发烧友

出品 | CSDN(ID:CSDNnews)

7797d98baa0b50472faf0e5ad219d6f8.png

为何鸿蒙内核源码分析系列开篇就说 LOS_DL_LIST?

因为它在鸿蒙 LOS 内核中无处不在,可以说在整个内核占了极大的比重,基本通过它把所有的结构体像胶水一样粘在一起,豪不夸张的说理解LOS_DL_LIST及相关函数 是读懂鸿蒙内核的关键。前后指针就像人的两只左右手一样灵活的指挥着系统精准的运行,越是深入分析内核源码,越是能体会在内核开发者对LOS_DL_LIST的非凡的驾驭能力,笔者仿佛看到了无数双手前后相连,拉起了无数个双向循环链表,把指针妙处运用到了极致,这也许就是编程的艺术吧!

致敬鸿蒙内核开发者,鸿蒙内核源码可以作为大学操作系统,数据结构两门课的教学项目。

/** * @ingroup los_list * Structure of a node in a doubly linked list. */typedef struct LOS_DL_LIST { struct LOS_DL_LIST *pstPrev; /**< Current node's pointer to the previous node */ struct LOS_DL_LIST *pstNext; /**< Current node's pointer to the next node */} LOS_DL_LIST;LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListInit(LOS_DL_LIST *list){ list->pstNext = list; list->pstPrev = list;}

真的是无处不在吗?答:是真的,看看使用它的源码吧,无处不在。

77a7b24ecf285d5d697f803604690dbb.png a57bbf20d3f13a6675851098bf0662e2.png 22a168e520483b65f2bd601fa0c65de4.png

基本概念

双向链表是指含有往前和往后两个方向的链表,即每个结点中除存放下一个节点指针外,还增加一个指向其前一个节点的指针。其头指针head是唯一确定的。

从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点,这种数据结构形式使得双向链表在查找时更加方便,特别是大量数据的遍历。由于双向链表具有对称性,能方便地完成各种插入、删除等操作,但需要注意前后方向的操作。

9d99da2c577a1a3519c6bbd45819284f.png

功能接口

Huawei LiteOS系统中的双向链表模块为用户提供下面几个接口。

功能分类

接口名

描述

初始化链表

LOS_ListInit

对链表进行初始化。

增加节点

LOS_ListAdd

将新节点添加到链表中。

在链表尾端插入节点

LOS_ListTailInsert

将节点插入到双向链表尾端。

删除节点

LOS_ListDelete

将指定的节点从链表中删除。

判断双向链表是否为空

LOS_ListEmpty

判断链表是否为空。

删除节点并初始化链表

LOS_ListDelInit

将指定的节点从链表中删除,使用该节点初始化链表。

链表中插入链表LOS_ListAddList两个循环链表合成一个大循环链表
从尾部插入节点LOS_ListTailInsert
(LOS_DL_LIST *list, LOS_DL_LIST *node)
从头部插入节点LOS_ListHeadInsert(LOS_DL_LIST *list, LOS_DL_LIST *node)
从尾部插入链表LOS_ListTailInsertList
(LOS_DL_LIST *oldList, LOS_DL_LIST *newList)
从头部插入链表LOS_ListTailInsertList(LOS_DL_LIST *oldList, LOS_DL_LIST *newList)

鸿蒙使用了双向循环链表来实现结构体数据结构之间的关联,支持单个节点的头尾插入,更精妙的是链表中支持插入另一个链表,将两个循环链表合成一个大循环链表,实现极为巧妙和简单。详见代码

//双向链表初始化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 BOOL LOS_ListEmpty(LOS_DL_LIST *list){ return (BOOL)(list->pstNext == list);}//从链表中删除节点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 = ; node->pstPrev = ;}//指针互换,具体向双向循环链表中插入节点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_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;}// 这里与其说插入不如说合并,同样支持从头或尾部合并LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListTailInsertList(LOS_DL_LIST *oldList, LOS_DL_LIST *newList){ LOS_ListAddList(oldList->pstPrev, newList);}LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListHeadInsertList(LOS_DL_LIST *oldList, LOS_DL_LIST *newList){ LOS_ListAddList(oldList, newList);}

大家在阅读鸿蒙内核源码要实时带着LOS_DL_LIST 这理解 代码之间的关联,构想运行时的场景是怎样的,就能体会到内核代码之精练美妙。

2f00a13d4164ecfaf8a8b1ddb65f47ef.png

具体的使用场景

看下它其中的一个使用场景吧,体验设计者的奇妙用心,上代码。

typedef struct ProcessCB { CHAR processName[OS_PCB_NAME_LEN]; /**< Process name */ UINT32 processID; /**< process ID = leader thread ID */ UINT16 processStatus; /**< [15:4] process Status; [3:0] The number of threads currently running in the process */ LOS_DL_LIST pendList; /**< Block list to which the process belongs */ LOS_DL_LIST childrenList; /**< my children process list */ LOS_DL_LIST exitChildList; /**< my exit children process list */ LOS_DL_LIST siblingList; /**< linkage in my parent's children list */ ProcessGroup *group; /**< Process group to which a process belongs */ LOS_DL_LIST subordinateGroupList; /**< linkage in my group list */ UINT32 threadGroupID; /**< Which thread group , is the main thread ID of the process */ UINT32 threadScheduleMap; /**< The scheduling bitmap table for the thread group of the process */ LOS_DL_LIST threadSiblingList; /**< List of threads under this process */ LOS_DL_LIST threadPriQueueList[OS_PRIORITY_QUEUE_NUM]; /**< The process's thread group schedules the LOS_DL_LIST waitList; /**< The process holds the waitLits to support } LosProcessCB;

这是LosProcessCB(进程控制块),因为结构体很复杂,省去了其他定义,留下LOS_DL_LIST 相关的,大家自行对照内核源码阅读,LosProcessCB包含了 七个双向循环链表,而进程组的队列是个数组,又包含了32个就绪队列的双向循环链表。这些链表承载的是一个进程在期生命周期内的运行过程逻辑,进程和线程的关系逻辑,线程的运行过程逻辑等等,是的,必须要有这么复杂的数据结构才能描述进程从出生到消亡的过程。

78496fc5bc125787ef2cfa393b512abb.png

任务队列涉及的相关代码

这些是 任务队列出队入队操作,背后都是LOS_DL_LIST的增删过程。

#define OS_PROCESS_PRI_QUEUE_SIZE(processCB) OsPriQueueProcessSize(g_priQueueList, (processCB)->priority)#define OS_TASK_PRI_QUEUE_ENQUEUE(processCB, taskCB)  OsPriQueueEnqueue((processCB)->threadPriQueueList, &((processCB)->threadScheduleMap),  &((taskCB)->pendList), (taskCB)->priority)#define OS_TASK_PRI_QUEUE_ENQUEUE_HEAD(processCB, taskCB)  OsPriQueueEnqueueHead((processCB)->threadPriQueueList, &((processCB)->threadScheduleMap),  &((taskCB)->pendList), (taskCB)->priority)#define OS_TASK_PRI_QUEUE_DEQUEUE(processCB, taskCB)  OsPriQueueDequeue((processCB)->threadPriQueueList, &((processCB)->threadScheduleMap), &((taskCB)->pendList))#define OS_TASK_SCHED_QUEUE_ENQUEUE(taskCB, status) OsTaskSchedQueueEnqueue(taskCB, status)#define OS_TASK_SCHED_QUEUE_DEQUEUE(taskCB, status) OsTaskSchedQueueDequeue(taskCB, status)#define OS_PROCESS_PRI_QUEUE_ENQUEUE(processCB)  OsPriQueueEnqueue(g_priQueueList, &g_priQueueBitmap, &((processCB)->pendList), (processCB)->priority)#define OS_PROCESS_PRI_QUEUE_ENQUEUE_HEAD(processCB)  OsPriQueueEnqueueHead(g_priQueueList, &g_priQueueBitmap, &((processCB)->pendList), (processCB)->priority)#define OS_PROCESS_PRI_QUEUE_DEQUEUE(processCB) OsPriQueueProcessDequeue(&((processCB)->pendList))#define OS_TASK_PRI_QUEUE_SIZE(processCB, taskCB) OsPriQueueSize((processCB)->threadPriQueueList, (taskCB)->priority)#define OS_TASK_GET_NEW(processCB) LOS_DL_LIST_ENTRY(OsPriQueueTop((processCB)->threadPriQueueList,  &((processCB)->threadScheduleMap)),  LosTaskCB, pendList)
e6cf855e2b8af01bfdd80a62d76d0d4c.png

内联函数 inline

鸿蒙内核大量的使用了内联函数,内联函数的好处是什么?不明白自己去查,这里不普及基本知识。源码中只有los_list.h 是.h 文件,木有.c 文件!这些调用最最频繁的内联函数,免去了像普通函数要出栈入栈的时间和空间,效率极高。

/* Define OS code data sections *//* The indicator function is inline */#ifndef LITE_OS_SEC_ALW_INLINE#define LITE_OS_SEC_ALW_INLINE /* __attribute__((always_inline)) */#endifLITE_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_ListTailInsert(LOS_DL_LIST *list, LOS_DL_LIST *node){ LOS_ListAdd(list->pstPrev, node);}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值