鸿蒙轻内核M核源码分析:数据结构之任务就绪队列
摘要:本文会给读者介绍鸿蒙轻内核M核源码中重要的数据结构,任务基于优先级的就绪队列Priority Queue。
本文分享自华为云社区《鸿蒙轻内核M核源码分析系列三 数据结构-任务就绪队列》,原文作者:zhushy 。
本文会给读者介绍鸿蒙轻内核M核源码中重要的数据结构,任务基于优先级的就绪队列Priority Queue。
在讲解时,会结合数据结构相关绘图,培养读者们的数据结构的平面想象能力,帮助更好的学习和理解这些数据结构的用法。本文中所涉及的源码,以OpenHarmony LiteOS-M内核为例,均可以在开源站点https://gitee.com/openharmony/kernel_liteos_m 获取。
1 任务就绪队列
在任务调度模块,就绪队列是个重要的数据结构。任务创建后即进入就绪态,并放入就绪队列。在鸿蒙轻内核中,就绪队列是一个双向循环链表数组,每个数组元素就是一个链表,相同优先级的任务放入同一个链表。
任务就绪队列Priority Queue主要供内部使用,用户进行业务开发时不涉及,所以并未对外提供接口。双向循环链表数组能够更加方便的支持任务基于优先级进行调度。任务就绪队列的核心代码在kernel\src\los_task.c文件中。
1.1 任务就绪队列的定义
在kernel\src\los_task.c文件中定义了和任务就绪队列相关的主要变量。
源码如下:
⑴ LITE_OS_SEC_BSS LOS_DL_LIST *g_losPriorityQueueList = NULL; ⑵ static LITE_OS_SEC_BSS UINT32 g_priqueueBitmap = 0; ⑶ #define PRIQUEUE_PRIOR0_BIT (UINT32)0x80000000 ⑷ #define OS_PRIORITY_QUEUE_PRIORITYNUM 32
其中⑴表示任务就绪队列,是一个双向链表数组,后文初始化该数组时会将数组长度设置为⑷处定义的OS_PRIORITY_QUEUE_PRIORITYNUM;⑵表示优先级位图,标识了任务就绪队列中已挂载的就绪任务所在的优先级;⑶表示优先级为0的比特位;⑷表示任务就绪队列支持的优先级个数32,所以鸿蒙轻内核优先级的取值范围为0-31,数值越小优先级越大。
优先级位图g_priqueueBitmap的bit位和优先级的关系为bit=31-priority,优先级数组g_losPriorityQueueList[priority]包含了OS_PRIORITY_QUEUE_PRIORITYNUM个数组元素,每个数组元素都是一个双向链表,同一优先级的处于就绪状态的所有任务都会挂载到对应优先级的双向链表中。
示意图如下:

2 任务就绪队列操作
2.1 初始化任务就绪队列
任务就绪队列初始化函数为OsPriQueueInit(),系统初始化阶段被调用,调用路径为:main.c:main() --> kernel\src\los_init.c:LOS_KernelInit() --> kernel\src\los_task.c:OsTaskInit() --> OsPriqueueInit()。
源码如下:
STATIC UINT32 OsPriqueueInit(VOID)
{
UINT32 priority;
⑴ UINT32 size = OS_PRIORITY_QUEUE_PRIORITYNUM * sizeof(LOS_DL_LIST); g_losPriorityQueueList = (LOS_DL_LIST *)LOS_MemAlloc(m_aucSysMem0, size);
if (g_losPriorityQueueList == NULL) {
return LOS_NOK;
} for (priority = 0; priority < OS_PRIORITY_QUEUE_PRIORITYNUM; ++priority) {
⑵ LOS_ListInit(&g_losPriorityQueueList[priority]);
}
return LOS_OK;
}
⑴处计算就绪队列数组需要的内存大小,然后为任务就绪队列申请内存,占用内存为OS_PRIORITY_QUEUE_PRIORITYNUM个双向链表所需要的内存大小,运行期间该内存不会释放,为系统常驻内存。⑵处代码将每一个数组元素都初始化为双向循环链表。
2.2 任务就绪队列插入
任务就绪队列插入函数为OsPriqueueEnqueue(),该函数把就绪状态的任务插入任务就绪队列的尾部。在任务就绪队列中,先调用队列头部的任务,最后调用队列尾部的任务。
源码如下:
STATIC VOID OsPriqueueEnqueue(LOS_DL_LIST *priqueueItem, UINT32 priority)
{
⑴ if (LOS_ListEmpty(&g_losPriorityQueueList[priority])) {
⑵ g_priqueueBitmap |= (PRIQUEUE_PRIOR0_BIT >> priority);
} ⑶ LOS_ListTailInsert(&g_losPriorityQueueList[priority], priqueueItem);
}
⑴处先判断指定优先级priority的任务就绪队列是否为空,如果为空,则在⑵处更新优先级位图,将第31-prioritybit位设置为1。⑶处把就绪状态的任务插入任务就绪队列的尾部,进行排队。
2.3 从任务就绪队列中删除
从任务就绪队列中删除的函数为OsPriqueueDequeue()。任务被删除、进入suspend阻塞状态、优先级调整等场景中,都需要调用该函数把任务从任务就绪队列中删除。
源码如下:
STATIC VOID OsPriqueueDequeue(LOS_DL_LIST *priqueueItem)
{
LosTaskCB *runningTask = NULL;
⑴ LOS_ListDelete(priqueueItem); ⑵ runningTask = LOS_DL_LIST_ENTRY(priqueueItem, LosTaskCB, pendList);
⑶ if (LOS_ListEmpty(&g_losPriorityQueueList[runningTask->priority])) {
⑷ g_priqueueBitmap &= ~(PRIQUEUE_PRIOR0_BIT >> runningTask->priority);
}
}
⑴把任务从任务就绪队列中删除。⑵获取被删除任务的任务控制块信息,以获取任务的优先级。删除完任务后队列可能成为空队列,所以⑶处代码判断任务就绪队列是否为空,如果为空,则需要执行⑷处代码,更新优先级位图,将第31-prioritybit位设置为0。
2.4 获取队列中的最高优先级节点
获取任务就绪队列中优先级最高的链表节点的函数为OsPriQueueTop()。
源码如下:
STATIC LOS_DL_LIST *OsPriqueueTop(VOID)
{
UINT32 priority; ⑴ if (g_priqueueBitmap != 0) {
⑵ priority = CLZ(g_priqueueBitmap);
⑶ return LOS_DL_LIST_FIRST(&g_losPriorityQueueList[priority]);
} return (LOS_DL_LIST *)NULL;
}
⑴处判断优先级位图g_priqueueBitmap是否为0,如果为0则直接返回NULL,说明任务就绪队列中没有任何就绪状态的任务。 ⑵处计算g_priqueueBitmap以二进制表示时高位为0的位数,其值就是任务的优先级priority,以此方法得到的优先级就是任务就绪队列中所有优先级里最高的。然后⑶处从该优先级的队列&g_losPriorityQueueList[priority]中获取第一个链表节点,获取的就是任务就绪队列中优先级最高的任务。
2.5 获取指定优先级的就绪任务的数量
获取任务就绪队列中指定优先级的任务数量的函数为OsPriqueueSize()。
源码如下:
STATIC UINT32 OsPriqueueSize(UINT32 priority)
{
UINT32 itemCnt = 0;
LOS_DL_LIST *curPQNode = (LOS_DL_LIST *)NULL; ⑴ LOS_DL_LIST_FOR_EACH(curPQNode, &g_losPriorityQueueList[priority]) {
⑵ ++itemCnt;
} return itemCnt;
}
⑴处代码使用宏LOS_DL_LIST_FOR_EACH定义的for循环遍历指定优先级priority的双向链表,如果获取到新节点则表示该优先级下有一个就绪任务,然后执行⑵处代码,对计数进行加1操作,返回的结果就是指定优先级下有多少个就绪任务。
小结
掌握鸿蒙轻内核的优先级就绪队列Priority Queue这一重要的数据结构,会给进一步学习、分析鸿蒙轻内核源代码打下了基础,让后续的学习更加容易。后续也会陆续推出更多的分享文章,敬请期待,也欢迎大家分享学习、使用鸿蒙轻内核的心得,有任何问题、建议,都可以留言给我们: https://gitee.com/openharmony/kernel_liteos_m/issues 。为了更容易找到鸿蒙轻内核代码仓,建议访问 https://gitee.com/openharmony/kernel_liteos_m ,关注Watch、点赞Star、并Fork到自己账户下,谢谢。
鸿蒙轻内核M核源码分析:数据结构之任务就绪队列的更多相关文章
- 鸿蒙轻内核M核源码分析:LibC实现之Musl LibC
摘要:本文学习了LiteOS-M内核Musl LibC的实现,特别是文件系统和内存分配释放部分. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十九 Musl LibC>,作者:zhus ...
- 深层剖析鸿蒙轻内核M核的动态内存如何支持多段非连续性内存
摘要:鸿蒙轻内核M核新增支持了多段非连续性内存区域,把多个非连续性内存逻辑上合一,用户不感知底层的不同内存块. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列九 动态内存Dynamic Mem ...
- 鸿蒙轻内核M核的故障管家:Fault异常处理
摘要:本文先简单介绍下Fault异常类型,向量表及其代码,异常处理C语言程序,然后详细分析下异常处理汇编函数实现代码. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十八 Fault异常处理& ...
- Linux 内核调度器源码分析 - 初始化
导语 上篇系列文 混部之殇-论云原生资源隔离技术之CPU隔离(一) 介绍了云原生混部场景中CPU资源隔离核心技术:内核调度器,本系列文章<Linux内核调度器源码分析>将从源码的角度剖析内 ...
- 【TencentOS tiny】深度源码分析(4)——消息队列
消息队列 在前一篇文章中[TencentOS tiny学习]源码分析(3)--队列 我们描述了TencentOS tiny的队列实现,同时也点出了TencentOS tiny的队列是依赖于消息队列的, ...
- zeromq源码分析笔记之无锁队列ypipe_t(3)
在上一篇中说到了mailbox_t的底层实际上使用了管道ypipe_t来存储命令.而ypipe_t实质上是一个无锁队列,其底层使用了yqueue_t队列,ypipe_t是对yueue_t的再包装,所以 ...
- ARMv8 Linux内核head.S源码分析
ARMv8Linux内核head.S主要工作内容: 1. 从el2特权级退回到el1 2. 确认处理器类型 3. 计算内核镜像的起始物理地址及物理地址与虚拟地址之间的偏移 4. 验证设备树的地址是否有 ...
- Windows内核遍历驱动模块源码分析
要获取windows 内核中所有驱动模块信息,调用 系统服务函数 NtQuerySystemInformation,参数SystemInformationClass 传入SystemModuleInf ...
- Volley源码分析(1)----Volley 队列
Android网络框架很多,但是基于Google自己的volley,无疑是优秀的一款. 网络框架,无外乎解决一下几个问题,队列,缓存,图片异步加载,统一的网络请求和处理等. 一.Volley 队列 启 ...
- HashMap从源码分析数据结构
1. HashMap在链表中存储的是键值对 2. 数组是一块连续的固定长度的内存空间,再好的哈希函数也不能保证得到的存储地址绝对不发生冲突.那么哈希冲突如何解决呢?哈希冲突的解决方案有多种:开放定址法 ...
随机推荐
- mysql语句操作
1.从login表中选出name字段包含admin的前10条结果所有信息的sql语句 select * from login where name like %admin% limit 0 ,10; ...
- Python 利用pandas和mysql-connector获取Excel数据写入到MySQL数据库
如何将Excel数据插入到MySQL数据库中 在实际应用中,我们可能需要将Excel表格中的数据导入到MySQL数据库中,以便于进行进一步的数据分析和处理.本文将介绍如何使用Python将Excel表 ...
- DFS洛谷4961(求联通块)
说实话这个题审题把我卡了半天,还是我太菜 直接上代码吧 偷个懒用万能库. #include"bits/stdc++.h" using namespace std; int mp[1 ...
- RL 基础 | Policy Iteration 的收敛性证明
(其实是专业课作业 感觉算法岗面试可能会问,来存一下档) 目录 问题:证明 Policy Iteration 收敛性 0 Background - 背景 1 Policy Evaluation con ...
- YCSB对MongoDB数据库性能测试
一.安装部署 1.1前置条件 Install Java and Maven Go to http://www.oracle.com/technetwork/java/javase/downloads/ ...
- 【scipy 基础】--积分和微分方程
对于手工计算来说,积分计算是非常困难的,对于一些简单的函数,我们可以直接通过已知的积分公式来求解,但在更多的情况下,原函数并没有简单的表达式,因此确定积分的反函数变得非常困难. 另外,相对于微分运算来 ...
- Windows文件句柄无效
今天我用FreeFileSync从移动硬盘复制一个名为Con的文件夹到本地硬盘,复制失败. 通过文件夹资源管理器Explorer直接访问文件夹则提示"禁止访问",右键属性切换到安全 ...
- python中pip下载慢或报错的解决方法
一:问题 python的pip在安装包时,有时会报错超时,排除包名写错的原因,一般这种问题是因为网络下载过慢,导致超时 二:解决方案 我们可以设置pip镜像源下载,能够提升pip下载速度,解决报错问题 ...
- 35个超实用excel快捷键
以下是一些常用的Excel快捷键,希望对你有所帮助.如果你想要了解更多快捷键,可以参考Excel的官方文档或者在网上搜索相关信息. Ctrl + C:复制选定的单元格或单元格范围. Ctrl + X: ...
- 🔥🔥Java开发者的Python快速实战指南:实用工具之PDF转DOCX文档(可视化界面)
首先,大家对Python语法的了解已经基本完成,现在我们需要开始进行各种练习.我为大家准备了一些练习题目,比如之前的向量数据库等,这些题目可以参考第三方的SDK来进行操作,文档也是比较完善的.这个过程 ...