鸿蒙轻内核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. 数组是一块连续的固定长度的内存空间,再好的哈希函数也不能保证得到的存储地址绝对不发生冲突.那么哈希冲突如何解决呢?哈希冲突的解决方案有多种:开放定址法 ...
随机推荐
- 关于通过StringTemplate模板生成xml转成excel后office无法打开的问题解决
说明:本人最近在着手实现导出日志数据,由于日志数据过多,在网上查找java 导出大量数据到excel的例子. 后发现园子里某位老哥通过StringTemplate模板生成excel格式的xml,这个思 ...
- JavaScript(ES6):变量的解构赋值
解构赋值定义: 允许按照一定模式从数组或对象中提取值,然后对变量进行赋值. 数组的解构赋值 注:数组的元素要一次排序的,变量的值由他的位置决定. 基本用法 // ES6 解构赋值 let [a, b, ...
- STL multiset容器
multiset容器的概念和性质 set在英文中的意义是:集合.而multi−前缀则表示:多重的.所以multiset容器就叫做:有序多重集合. multiset的很多性质和使用方式和set容器差不了 ...
- 手撕Vuex-安装模块方法
前言 经过上一篇文章的介绍,我们实现了将模块安装到 store 中,那么本章我们就来介绍一下怎么安装模块当中的方法也就是 actions.mutations.getters. 所以本次文章的目标就是将 ...
- DOS(Terminal)常用命令
DOS是一款在20世纪末期流行的操作系统,它是一款面向磁盘的系统软件.它的用途非常广泛,大名鼎鼎的 Windows 98 就是基于它的.DOS依然活跃,比如FreeDOS. cmd是指命令行提示符,是 ...
- C/C++ 运用Npcap发送UDP数据包
Npcap 是一个功能强大的开源网络抓包库,它是 WinPcap 的一个分支,并提供了一些增强和改进.特别适用于在 Windows 环境下进行网络流量捕获和分析.除了支持通常的网络抓包功能外,Npca ...
- 华为ar502H物联网边缘计算网关,在容器内控制/dev/do0开关命令
执行以下命令进行开关do继电开关,可以听见电位器声音. echo -en "\x01" > /dev/do0 echo -en "\x00" > ...
- 泛微OA与ERP集成的关键要点
泛微OA办公系统与ERP系统的集成是为了实现企业内部各个系统之间的数据共享和协同工作,提高工作效率和管理水平.下面将详细介绍泛微OA办公系统如何与ERP系统集成以及轻易云数据集成平台在该过程中发挥的重 ...
- 用友vs金蝶产品分析(云星空与YonSuite)
产品定位 用友与金蝶二者面对的客户群体是相同的:都是为成长型企业提供一体化服务,由于金蝶云星空发展较早,在部分产品功能上具备一定的先发优势:在产品的架构上,由于YS采用目前最先进的云原生和微服务架构, ...
- 【uniapp】学习笔记day03 | 页面制作【外包杯】
一.开发层级结构介绍 page.json 对uniapp进行全局配置,决定页面文件的路径.窗口样式.原生的导航栏.底部的原生tabbbar manifest.json 应用的配置文件,指定应用的名称. ...