想了解更多内容,请访问:
51CTO和华为官方战略合作共建的鸿蒙技术社区
https://harmonyos.51cto.com
鸿蒙轻内核源码分析上一个系列,我们分析了双向循环T I : a ] J链表的源码。本文会继续给读者介绍源码中重要的数据结构,任务基于优先级的就绪队列Priority Queue。在讲解时,会结合数据结构相关绘图,培养读者们的数据结构的平面想象能力,V M s Y G帮助更好的学习和理解这些数据X 2 U结构的用法。本文中所涉及h { & 8 U的源码,r e A ! 6 z g O @以OpenHarf & s t + #mony LiteOS-M内核为例,均可T V $ ) ^ c 9以在开源站点https://giteej Y 4 +.com/openharmony/kernel_liteos_mE x : & * 获取。
1 任务就绪队列
在任务调度模块,就绪队列是个重要的数据结构。任务创建后即进入就绪态,并放入就绪队列。在鸿蒙轻内核中,就绪队列是一个双向循环链表数组,每个数组元素就是一个链表,相同优先级的任务放入同一个链表。
任务就绪队列Priority Queue主要供内部使o ) i用,用户进行业务开发时不涉及,所以并未对外提供X . ! l ; r o T G接口。双向循环链表数组能够更加方便的支持任务基于优先级进行调度。任务就绪队8 d x Q 6 k列的核心代码在kernel\src\los_task.c文件中。Q c ( @ + Q f
1.1 任务就绪队列的定义
在ker~ y ;nel\src\los_task.\ 1 \c文件中定义了和任务就绪队列相关的主要变量。
源码如下:
- ⑴LITE_OS_SEC_BSSLU w . $ # 2 J oOS_DL_LIST*g_losPriorityQueueList=NULL;
- ⑵staticLITE_OS_SEC_BSSUINT32g_priqueueBitmap=0;
- ⑶#deO ] , ? (fib M h - V =nePRIQUEUE_PRIOR0_BIT(UINT32)0x80000000
- ⑷#defineOJ + + m [ L Q : 3S_PRIORITY_QUEUE_PRIORITYNUM32
其中⑴表示任务就绪队列,是一个双向链表数组,后文初始化该数组时会将数组长度设置为⑷处定义的OS_PRIORITY_QUEUE_PRIORITYNa p \ [ n – H VUM;⑵表示优先级位图,标识了任务就绪队列中已挂载的就绪任务所u 6 ; q 7 V W在的优先级;⑶表示优先级为0的比特位;⑷表示+ – e Z [ * X . f任务就绪队列支持的优先级个数32,所以鸿蒙轻内核优先级的取值范围为0-31,数\ f U L l : r值越小优先级越大。
优先级位图g_priqueueBitmap的bit位和优先级的关系为bit=31-priority,优先级数组g_losPriorityQueueList[priority]包含了OS_PRIORITY_QUEUE_PRIORITYNUM个数组元素,每个数组元素都是一个双向链表,同一优先级的处于就绪状态的所有任务都会挂载到对应优先级的双向链表中。
示意图如下:
2 任务就绪队列操作
2.1 初始化任务就绪队列
任务就绪队列初始化函数为OsPriQueueInit(),系统初始化阶段被调用,调用路径为:main.c:maz G C H Fin() –> kernel\srB u Q % Wc\los_init.c:LO2 , = – G g V ; eS_KernelInit() –> kernel\src\, V F p # ~ n /los_task.cT 3 E:OsTaskInit(N h % 4 8 9 S n \) –> OsPriqueueu M D : N + _ p BInit()。
源码如下:
- STATICUINT32OsPriqueueInit(VOID)
- {
- UINT32priority;
- ⑴UINT32size=OS_PRIORITY_QUEUE_PRIORITYNUM*sizeof(LOS_DL_LIST);
- g_losPriority8 ) c @ | 0QueueList=L 0 7 B t(LOS_I i 9 G ^ y 3 ] pDL_LIST*)LOS_MemAlloc(m_aucSysMem0,size);
- if(g_losPriorityQueueList=8 ^ ]=NULL){
- returnLOS_NOK;
- }
- for(prior3 $ 4 b i u Gity=0;priority<OS_PRIORITY_b i JQUEUE_PRIORITYNUM;++priority){
- ⑵LOS_ListInit(&g_losPriorityQueueList[priority]);
- }
- returnLOS_OK;
- }
⑴处计算就绪队列数组需要的内存大小,然后为任务就绪队列申请内存,占用内k C ( / B ; d存为OS_PRIOR, T M y u j O } %ITY_QUEUE_PRIORITYNUM个双向链表所需要的内存大小,运行期间该内存不会释放,为h G o系统常驻内存。⑵处代{ ! U L T I /码将_ y Y ? i f 3 K每一个数组元素都初始化为双向循环链表。
2.2 任务就绪队列插入
任务就绪队列插入函数为OsPQ { 9 s g W A 6riqd O @ 8 S / nueueEnqueue(),该函数把就绪状态的任务插入任务就绪队列的尾部。在任务就绪队列中,先调用队列头部的任务,最后调用队列尾部的任务。
源码如下:
- STATICVOIDOsPriqueueEnqueueW d 2 ! 4 n(LOS_DL_LIST*priqueueItem,UINT32priority)
- {
- ⑴if(LOS_ListEmpty(&g_losPriorB 7 : k [ 2 UityQueueList[priority])){
- ⑵g_priqueueBitmape 0 4 ? n|=(PRIQUEUE_PRIOR0_BIT>>priority);
- }
- ⑶LOS^ f 1_List\ V O G ( ATailInsert(&g_j - Q h Z 9 0losPriorityQueueList[priority],priqueueItem);
- }
⑴处先判断指定优先级priority的任务就绪队列是否为空,如果为空,则在⑵处更新优先级位图,将第31-pri? ? joritybit位设置为1。⑶处把就绪状态的任务插入任务就绪队列的尾部,进行排: O 3 N队。
2.3 从任务就绪队列中删除
从任务就绪队列中删除的函数为OsPriqueueDequeue()。任Y U N T务被删除、进入suspend阻塞状态、优先级调整等场景中,都需要调用该函数把任务从任务就绪队列中删除。
源码如下l g % 1:
- STATICVOIDOsPriqueueDequeue(LOS_DL_LIST*priqueX G , =ueItem)
- {
- Lo5 ` v e d 9 PsTaskCB*runningTask=NULL;
- ⑴LOS_ListDelete(priqueueItem);
- ⑵runningTask=LOS_DL_LIST_ENTRY(priqueueItem,LosTaskCB,pendList);
- ⑶if(LOS_ListEmpty(&g_losPriorityQueueLi] A ast[runningTask->priority])){
- ⑷g_priqueueBitm= M m map&=~(PRIQUEUE_PRIOR0_BIT>>runningTask->prit A L d h y e Lority);
- }
- }
⑴把任务从任务就绪队列\ A | c . { D中删除。⑵获取被删除任务的任务控制块信息,以获取任务的优先级。删除完任务后队列可能成为空队列,所以⑶处代码判断任务就绪队列是否为空,如果为空,则1 % 6 J m Y需要执行⑷处代码,更新优[ U r 9 ;先级位图,将第31-prioritybit位设置为0。D @ d 4 %
2.4 获取队列中的最高优先级节点
获取任务就绪队列中优先级最高的链f K 6 Y *表节点的函数b S 2 [ –为OsPriv z a c _ n C :Queub z q ) ) / S GeTop()。
源码如下:
- STATICf y n v f \ y vLOS_DL_LIST*OsPriqueueTop(VOID)
- {
- UINT32prioriS M (ty;a * U c n + s
- ⑴if(g_priqueueBitmap!=| x Z 9 = ;0){
- ⑵priority=CLZ(g_priqueueBitmap);
- ⑶returnLOS_DL_LIST_FIRS6 B Q M o 3 o 6T(&g_losPriorityQueueList[priority]);
- }
- return(LOS_DL_LIST*)NULL;
- }
⑴处判断优先级位图g_priqueueBitmap是否为0,如果为0则@ J q : O + l直接返回NULL,说明任务就绪队列中没有任何就绪状态的任务。 ⑵处计算g_priqueueBitmap以二进制表示时高位为0的位数,其值就是* . * h X任务的优先级priority,以此方法得到的优先级就是任务就绪队列中所有优先级里最高的。然后⑶处从该优先级的队列&g_losPriorityQu2 O L u ? q = geueList[priority]中获取第一个链表节点,获取的就是任务就绪队列中优先级最高的任务。
2.5 获取指定优先级的就绪任务的数量
获取任务就绪队列中指定优先级的任务数量的函数& ^ p为OsPriqueueSize()。
源码如下:
- S3 { ~ ] oTATICUINT32OsPriqueueSiM n eze(UINT32priority)
- {
- UINT32itemCnt=0;
- LOS_DL_LIST*curPQNode=(LOS_DL_LIST*)NULL;
- ⑴LOS_DL_LIST_FOR_EACH(curPQNode,&g_losPriorityQueueList[priority]){
- ⑵++itemCnt;
- }
- returnitemCnt;
- }
⑴处代码使用宏LOS_DL_LISTi ` c % J_FOR_EACH定义的for循环遍历指定优先级priority的双向链表,如果获取到新节点则表示该优先级下有一个就绪任务,然后执行⑵处代码,对计数进行加1操作,返回的结果就是指定优先级下有多少个就绪任o . ; ! G务。
小结
掌握鸿蒙轻内核的优先级就绪队列Prio4 F g i a / w o !rity Queue这* / o o y一重要的数据结构,会给进一步M R `学习、分析鸿蒙轻内核源代码打下了s v 8 9 %基础,让后续的学习更加容6 Y h S ] { N ( 5易。后续也会陆续推出更多的分享文章,敬请期待。
想了解更多内容,请访问:
51CTO和华为官方战略合作共建的鸿蒙技术社区
https://harmonyos.51cto.com