鸿蒙轻内核M核源码分析系列六 任务及任务调度(3)任务调度模块

调度,Schedule也称为Dispatch,是操作系统的一个重要模块,它负责选择系统要处理的下一个任务。调度模块需要协调处于就绪状态的任务对资源的竞争,按优先级策略从就绪队列中获取高优先级的任务,给予资源使用权。本文我们来一起学习下调度模块的源代码,文中所涉及的源代码,所涉及的源码,以OpenHarmony LiteOS-M内核为例,均可以在开源站点 https://gitee.com/openharmony/kernel_liteos_m 获取。

下面,我们剖析下任务调度模块的源代码,若涉及开发板部分,以开发板工程targets\cortex-m7_nucleo_f767zi_gcc\为例进行源码分析。

1、调度模块的重要函数

文件kernel\src\los_sched.c中定义了调度模块的几个重要的函数,我们来分析下源码。

1.1 调度初始化函数

调度初始化函数UINT32 OsSchedInit(VOID)在任务初始化函数UINT32 OsTaskInit(VOID)中调用。⑴处会初始化任务就绪队列,⑵处初始化任务排序链表,⑶处初始化调度响应时间全局变量为最大值OS_SCHED_MAX_RESPONSE_TIME

UINT32 OsSchedInit(VOID)
{UINT16 pri;
⑴  for (pri = 0; pri < OS_PRIORITY_QUEUE_NUM; pri++) {LOS_ListInit(&g_priQueueList[pri]);}g_queueBitmap = 0;⑵  g_taskSortLinkList = OsGetSortLinkAttribute(OS_SORT_LINK_TASK);if (g_taskSortLinkList == NULL) {return LOS_NOK;}OsSortLinkInit(g_taskSortLinkList);
⑶  g_schedResponseTime = OS_SCHED_MAX_RESPONSE_TIME;return LOS_OK;
}

1.2 任务调度函数

任务调度函数VOID LOS_Schedule(VOID)是出镜率较高的一个函数。当系统完成初始化开始调度,并且没有锁任务调度时,会调用函数HalTaskSchedule()进行任务调度。该函数定义在kernel\arch\arm\cortex-m7\gcc\los_dispatch.S,由汇编语言实现,后文会详细分析。

VOID LOS_Schedule(VOID)
{if (g_taskScheduled && LOS_CHECK_SCHEDULE) {HalTaskSchedule();}
}

1.3 开启调度函数

函数VOID OsSchedStart(VOID)kernel\src\los_init.c:UINT32 LOS_Start(VOID)-->kernel\arch\arm\cortex-m7\gcc\los_context.c:UINT32 HalStartSchedule(OS_TICK_HANDLER handler)函数依次调用,在系统初始化时开启任务调度。我们看下该函数的源码,⑴处调用函数获取就绪队列中优先级最高的任务,⑵把该任务状态设置为运行状态,接着把当前运行任务和新任务都设置为就绪队列中优先级最高的那个任务。⑶处设置任务调度启动状态全局变量为1,标记任务调度已经开启。⑷处设置新任务的开始运行时间,然后把新任务从就绪队列中出队。⑸处设置全局变量。⑹处调用函数设置该任务的运行过期时间。

VOID OsSchedStart(VOID)
{(VOID)LOS_IntLock();
⑴  LosTaskCB *newTask = OsGetTopTask();⑵  newTask->taskStatus |= OS_TASK_STATUS_RUNNING;g_losTask.newTask = newTask;g_losTask.runTask = g_losTask.newTask;⑶  g_taskScheduled = 1;
⑷  newTask->startTime = OsGetCurrSchedTimeCycle();OsSchedTaskDeQueue(newTask);⑸  g_schedResponseTime = OS_SCHED_MAX_RESPONSE_TIME;g_schedResponseID = OS_INVALID;
⑹  OsSchedSetNextExpireTime(newTask->startTime, newTask->taskID, newTask->startTime + newTask->timeSlice);PRINTK("Entering scheduler\n");
}

1.4 任务调度切换函数

任务切换函数用于实现任务切换,被文件kernel\arch\arm\cortex-m7\gcc\los_dispatch.S中的汇编函数HalPendSV调用。我们分析下该函数的源代码。

⑴处获取当前运行的任务,然后调用函数减去其运行的时间片,开始运行时间设置为当前时间。⑵如果任务处于阻塞等待状态或延迟状态,则把其加入任务排序链表。⑶如果任务不是处于阻塞挂起状态、不是处于阻塞状态,则把其加入就绪队列。⑷处获取就绪队列中优先级最高的任务,⑸处如果当前运行任务和就绪队列汇总优先级最高的任务不是同一个任务,把当前任务状态设置为非运行状态,新任务设置为运行状态,并设置新任务的开始时间为当前任务的开始时间,然后执行⑹标记是否需要任务切换。⑺处把新任务从就绪队列中出队,⑻处计算新任务的运行结束时间,然后执行⑼设置任务到期时间。

BOOL OsSchedTaskSwitch(VOID)
{UINT64 endTime;BOOL isTaskSwitch = FALSE;⑴ LosTaskCB *runTask = g_losTask.runTask;OsTimeSliceUpdate(runTask, OsGetCurrSchedTimeCycle());⑵  if (runTask->taskStatus & (OS_TASK_STATUS_PEND_TIME | OS_TASK_STATUS_DELAY)) {OsAdd2SortLink(&runTask->sortList, runTask->startTime, runTask->waitTimes, OS_SORT_LINK_TASK);} else if (!(runTask->taskStatus & (OS_TASK_STATUS_PEND | OS_TASK_STATUS_SUSPEND | OS_TASK_STATUS_UNUSED))) {
⑶      OsSchedTaskEnQueue(runTask);}⑷  LosTaskCB *newTask = OsGetTopTask();g_losTask.newTask = newTask;if (runTask != newTask) {
#if (LOSCFG_BASE_CORE_TSK_MONITOR == 1)OsTaskSwitchCheck();
#endif
⑸      runTask->taskStatus &= ~OS_TASK_STATUS_RUNNING;newTask->taskStatus |= OS_TASK_STATUS_RUNNING;newTask->startTime = runTask->startTime;
⑹      isTaskSwitch = TRUE;OsHookCall(LOS_HOOK_TYPE_TASK_SWITCHEDIN);}⑺  OsSchedTaskDeQueue(newTask);⑻  if (newTask->taskID != g_idleTaskID) {endTime = newTask->startTime + newTask->timeSlice;} else {endTime = OS_SCHED_MAX_RESPONSE_TIME;}
⑼   OsSchedSetNextExpireTime(newTask->startTime, newTask->taskID, endTime);return isTaskSwitch;
}

2、调度模块汇编函数

文件kernel\arch\arm\cortex-m7\gcc\los_dispatch.S定义了调度模块的汇编函数,我们分析下这些调度接口的源代码。汇编文件中定义了如下几个宏,见注释。

.equ OS_NVIC_INT_CTRL,           0xE000ED04     ; Interrupt Control State Register,ICSR 中断控制状态寄存器
.equ OS_NVIC_SYSPRI2,            0xE000ED20     ; System Handler Priority Register 系统优先级寄存器
.equ OS_NVIC_PENDSV_PRI,         0xF0F00000     ; PendSV异常优先级
.equ OS_NVIC_PENDSVSET,          0x10000000     ; ICSR寄存器的PENDSVSET位置1时,会触发PendSV异常
.equ OS_TASK_STATUS_RUNNING,     0x0010         ; los_task.h中的同名宏定义,数值也一样,表示任务运行状态,

2.1 HalStartToRun汇编函数

开始运行函数HalStartToRun被文件kernel\arch\arm\cortex-m7\gcc\los_context.c中的开始调度函数HalStartSchedule在系统启动阶段调用。我们接下来分析下该函数的汇编代码。

⑴处设置PendSV异常优先级为OS_NVIC_PENDSV_PRIPendSV异常一般设置为最低。⑵处往控制寄存器CONTROL写入二进制的10,表示使用PSP栈,特权级的线程模式。⑶处把全局变量地址加载到寄存器r1,类似于C语言的r1=&g_losTask。⑷处[r1 , #4]&g_losTask地址加4个字节来获取g_losTask->newTask,此时寄存器r0数值为newTask的TaskCB的内存地址。

⑸处把[r0]的值即新任务的栈指针g_losTask->newTask->stackPointer加载到寄存器R12,现在R12指向任务栈的栈指针,任务栈现在保存的是上下文,对应定义在kernel\arch\arm\cortex-m7\gcc\los_arch_context.h中的结构体TaskContext。如果支持浮点寄存器,则执行⑹,把R12加100个字节,其中包含S16S31共16个4字节,R4R11uwPriMask共9个4字节的长度,执行指令后,R12指向任务栈中上下文的UINT32 uwR0位置。

⑺处代码把任务栈上下文中的UINT32 uwR0-uwR3, UINT32 uwR12; UINT32 uwLR; UINT32 uwPC; UINT32 uwxPSR;共8个成员变量数值分别加载到寄存器R0-R7,其中R5对应UINT32 uwLRR6对应UINT32 uwPC,此时寄存器R12指向任务栈上下文的UINT32 uwxPSR。然后执行下一个指令,指针继续加72字节(=18个4字节长度),即对应S0S15UINT32 FPSCR; UINT32 NO_NAME等上下文的18个成员。此时,寄存器R12指向任务栈的栈底,紧接着执行⑻把寄存器R12写入寄存器psp

如果不支持浮点寄存器,则执行⑼,从栈指针加36字节,然后寄存器R12指向任务栈中上下文的UINT32 uwR0位置。接着把上下文中的寄存器信息加载到寄存器R0-R7,紧接着把寄存器R12写入寄存器psp

最后,执行⑽处指令,把寄存器R5写入lr寄存器,开中断,然后跳转到R6对应的上下文的PC对应的函数VOID OsTaskEntry(UINT32 taskID),去执行任务的入口函数。

    .type HalStartToRun, %function.global HalStartToRun
HalStartToRun:.fnstart.cantunwind⑴  ldr     r4, =OS_NVIC_SYSPRI2ldr     r5, =OS_NVIC_PENDSV_PRIstr     r5, [r4]⑵  mov     r0, #2msr     CONTROL, r0⑶  ldr     r1, =g_losTask
⑷  ldr     r0, [r1, #4]
⑸  ldr     r12, [r0]
#if ((defined(__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \(defined(__FPU_USED) && (__FPU_USED == 1U)))
⑹  add     r12, r12, #100⑺  ldmfd   r12!, {r0-r7}add     r12, r12, #72
⑻  msr     psp, r12vpush   {S0}vpop    {S0}
#else
⑼  add     r12, r12, #36ldmfd   r12!, {r0-r7}msr     psp, r12
#endif
⑽   mov     lr, r5//MSR     xPSR, R7cpsie   Ibx      r6.fnend

2.2 OsTaskSchedule汇编函数

汇编函数HalTaskSchedule实现新老任务的切换调度。从上文可以知道,被任务调度函数VOID LOS_Schedule(VOID)调用。我们看看这个汇编函数的源代码,首先往中断控制状态寄存器OS_NVIC_INT_CTRL中的OS_NVIC_PENDSVSET位置1,触发PendSV异常。执行完毕HalTaskSchedule函数,返回上层调用函数。PendSV异常的回调函数是HalPendSV汇编函数,下文会分析此函数。汇编函数HalTaskSchedule如下:

    .type HalTaskSchedule, %function.global HalTaskSchedule
HalTaskSchedule:.fnstart.cantunwindldr     r0, =OS_NVIC_INT_CTRLldr     r1, =OS_NVIC_PENDSVSETstr     r1, [r0]dsbisbbx      lr.fnend

3.4 HalPendSV汇编函数

接下来,我们分析下HalPendSV汇编函数的源代码。⑴处把寄存器PRIMASK数值写入寄存器r12,备份中断的开关状态,然后执行指令cpsid I屏蔽全局中断。⑵处把寄存器r12lr入栈,然后调用上文分析过的任务切换函数OsSchedTaskSwitch。函数执行完毕,执行⑶处指令出栈,恢复寄存器r12lr数值。⑷处比较寄存器r0即任务切换函数OsSchedTaskSwitch的返回值与0,然后执行⑸使用r0寄存器保存lr寄存器的值,如果⑷处的比较不相等,则执行⑹跳转到标签TaskContextSwitch进行任务上下文切换。⑺处恢复中断状态,然后返回。

我们来看下需要任务上下文切换的情况,接着看标签TaskContextSwitch。⑻处从r0寄存器恢复lr寄存器的值。⑼处使用r0寄存器指示栈指针,然后把寄存器r4-r12的数值压入当前任务栈。如果支持浮点寄存器,还需要执行⑽,把寄存器d8-d15的数值压入当前任务栈,r0为任务栈指针。

⑾处指令把全局变量g_losTask地址加载到寄存器r5,⑿获取当前运行任务的栈指针,然后更新当前运行任务的栈指针。⒀处指令获取新任务newTask的地址,接着的指令把新任务地址赋值给当前运行任务,即runTask = newTask。⒁处指令把r1寄存器表示新任务的栈指针。如果支持浮点,⒂指令把新任务栈中的数据加载到寄存器d8-d15寄存器,继续执行后续指令继续加载数据到r4-r12寄存器,然后执行⒃处指令更新psp任务栈指针。⒄处指令恢复中断状态,然后执行跳转指令,后续继续执行C代码VOID OsTaskEntry(UINT32 taskId)进入任务执行入口函数。

    .type HalPendSV, %function.global HalPendSV
HalPendSV:.fnstart.cantunwind⑴  mrs     r12, PRIMASKcpsid   IHalTaskSwitch:
⑵  push    {r12, lr}blx     OsSchedTaskSwitch
⑶  pop     {r12, lr}
⑷  cmp     r0, #0
⑸  mov     r0, lr
⑹  bne     TaskContextSwitch
⑺  msr     PRIMASK, r12bx      lrTaskContextSwitch:
⑻  mov     lr, r0
⑼  mrs     r0, pspstmfd   r0!, {r4-r12}#if ((defined(__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \(defined(__FPU_USED) && (__FPU_USED == 1U)))
⑽   vstmdb   r0!, {d8-d15}
#endif
⑾  ldr     r5, =g_losTask
⑿  ldr     r6, [r5]str     r0, [r6]⒀  ldr     r0, [r5, #4]str     r0, [r5]⒁  ldr     r1, [r0]#if ((defined(__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \(defined(__FPU_USED) && (__FPU_USED == 1U)))
⒂  vldmia   r1!, {d8-d15}
#endifldmfd   r1!, {r4-r12}
⒃  msr     psp,  r1⒄  msr     PRIMASK, r12bx      lr.fnend

小结

本文带领大家一起剖析了鸿蒙轻内核调度模块的源代码,包含调用接口及底层的汇编函数实现。

如果大家想更加深入的学习 OpenHarmony 开发的内容,不妨可以参考以下相关学习文档进行学习,助你快速提升自己:

OpenHarmony 开发环境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源码解析》:https://qr18.cn/CgxrRy

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……

系统架构分析:https://qr18.cn/CgxrRy

  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

OpenHarmony 设备开发学习手册:https://qr18.cn/CgxrRy

在这里插入图片描述

OpenHarmony面试题(内含参考答案):https://qr18.cn/CgxrRy

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/22997.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于map并发读写问题的解决方案考虑小计

叠甲&#xff1a;未详细看底层实现&#xff0c;只是大概看了一下涉及的api的源码&#xff0c;理解错的地方勿喷 问题背景 公司业务中用到了一个map&#xff0c;作用是存储需要屏蔽的数据&#xff0c;请求打过来后会去其中匹配 若命中则直接退出 该map的使用有以下几个特点&am…

欧洲历史的五个阶段

欧洲的历史基本上都是分裂的&#xff0c;大致可以分为五个时期&#xff0c;分别为古希腊时代、罗马帝国时代、中世纪时代&#xff0c;文艺复兴时代、工业革命时代。 一&#xff0c;古希腊时代 古希腊是西方文明的源头&#xff0c;也是最重要和最直接的文明起源&#xff0c;首…

nexus搭建npm前端项目的私服

一、为什么要搭建私库 节省外网带宽加速maven构建部署第三方构件&#xff08;特别是无法从公共仓库下载的构件&#xff09;提高稳定性&#xff08;内网部署&#xff0c;更少地依赖外网&#xff09;降低中央仓库的负荷 构件&#xff0c;好比我们的藏书&#xff0c;去书店或商城…

深度学习——TensorBoard的使用

官方文档torch.utils.tensorboard — PyTorch 2.3 documentation TensorBoard简介 TensorBoard是一个可视化工具&#xff0c;它可以用来展示网络图、张量的指标变化、张量的分布情况等。特别是在训练网络的时候&#xff0c;我们可以设置不同的参数&#xff08;比如&#xff1…

【kubernetes】探索k8s集群的pod控制器详解(Deployment、StatefulSet、DaemonSet、Job、CronJob)

目录 一、Pod控制器及其功用 二、pod控制器有多种类型 2.1ReplicaSet 2.1.1ReplicaSet主要三个组件组成 2.2Deployment 2.3DaemonSet 2.4StatefulSet 2.5Job 2.6Cronjob 三、Pod与控制器之间的关系 3.1Deployment 3.2SatefulSet 3.2.1StatefulSet三个组件 3.2.2为…

力扣 54.螺旋矩阵

题目描述&#xff1a; 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5]示例 2&#xff1a; 输入&#…

什么是Spark RDD?(RDD的介绍与创建)

什么是Spark RDD&#xff1f;(RDD的介绍与创建) 一、RDD介绍 1、特点2、RDD的存储和指向3、RDD与DAG4、RDD的特性5、RDD分区6、RDD操作类型 二、RDD创建 1、引入必要的 Spark 库2、配置 Spark3、RDD创建4、示例代码 一、RDD介绍 RDD: 弹性分布式数据集&#xff08;Resilient…

【PCB]射频电路pcb设计

学习改变命运&#xff0c;技能成就未来&#xff01;❤~~ 1射频信号的基础知识及工作原理介绍 射频的基础知识介绍 2射频板PCB的布局要求 3射频板布局要求 4屏蔽帐设计 5射频板的层叠阻抗设计 6射频板的PCB布线原则 7射频板的PCB布线要求 8射频板的设计实战

10个令人惊叹的Python自动化脚本

大家好&#xff0c;Python凭借其简单和通用性&#xff0c;能够为解决每天重复同样的工作提供最佳方案。本文将介绍10个Python自动化脚本&#xff0c;可以帮助自动化完成任务&#xff0c;提高工作效率&#xff0c;它们可以成为项目运行中的便捷工具&#xff0c;可以收藏这些脚本…

AI办公自动化:用kimi批量提取音频中的标题并重命名

很多音频文件&#xff0c;文件名很乱&#xff0c;需要根据音频信息中的标题聪明吗 在kimi中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;一步步的思考&#xff0c;完成以下脚本的撰写&#xff1a; 打开文件夹&#xff1a;E:\有声\a16z播客 读取里面所有的mp3格…

ARM的工作模式

ARM的几种工作模式 User : 非特权模式&#xff0c;大部分任务执行在这种模式 FIQ : 当一个高优先级&#xff08;fast) 中断产生时将会进入这种模式IRQ : 当一个低优先级&#xff08;normal) 中断产生时将会进入这种模式 Supervisor&#xff08;SVC&#xff09; : 当复位或软中断…

GPT-4 Turbo 和 GPT-4 的区别

引言 人工智能&#xff08;AI&#xff09;领域的发展日新月异&#xff0c;OpenAI 的 GPT 系列模型一直是这一领域的佼佼者。GPT-4 和 GPT-4 Turbo 是目前市场上最先进的语言模型之一。本文将详细探讨 GPT-4 和 GPT-4 Turbo 之间的区别&#xff0c;以帮助用户更好地理解和选择适…

vue3中 window绑定scroll事件滚动页面获取不到e.target.scrollTop

遇到的问题 vue3项目 onMounted(() > {window.addEventListener(scroll, (e) > {console.log(e.target.scrollTop)}) })想要监听页面中的滚动&#xff0c;然后获取滚动距离实现一些功能&#xff0c;发现event参数中获取不到e.target.scrollTop&#xff08;印象中以前使…

React项目目录结构与组件基础结构

在React中开发项目并扩展组件时&#xff0c;一个清晰合理的目录结构是至关重要的。它不仅可以帮助你更好地组织代码&#xff0c;还能提高项目的可维护性和扩展性。下面是一个基本的React项目目录结构大纲&#xff0c;你可以根据自己的项目需求进行调整&#xff1a; my-app/ ├…

面试被问准备多久要孩子?这样回答

听说有人面试被问到多久要孩子的问题&#xff0c;当时觉得很尴尬&#xff0c;不知如何回答&#xff0c;怕回答的不好不被录用&#xff0c;其实你可以这样回答&#xff0c;让面试官心满意足。 A 面试官&#xff1a;结婚了吗&#xff1f; 我&#xff1a;结婚了 面试官&#xff1…

Vuforia AR篇(六)— Mid Air 半空识别

目录 前言一、什么是Mid Air&#xff1f;二、使用步骤三、示例代码四、效果 前言 增强现实&#xff08;AR&#xff09;技术正在改变我们与数字世界的互动方式。Vuforia作为先进的AR开发平台&#xff0c;提供了多种工具来创造引人入胜的AR体验。其中&#xff0c;Mid Air功能以其…

俄罗斯人有哪些常用的口头禅,柯桥零基础俄语培训

Хватит! 够了&#xff01; -Хватит, не стоит больше шуметь! 够了, 不要再吵了! -Это тебя не касается! 这与你无关&#xff01; Блин! 靠&#xff01; Блин这个词绝对是俄罗斯人最爱用的口语表达之一&#xff0c;…

右键Open with VSCode打开Vue3项目

之前看到一些同事能够对项目根目录进行右键打开项目到 Microsoft VS Code &#xff0c;当时觉得挺不错的&#xff0c;于是乎今天自己折腾了一遍。 目录 1、创建vue3项目 2、更改注册表 # 打开注册表编辑器&#xff08;Registry Editor&#xff09; # 导航到以下注册表路径 …

前端_防抖节流

目录 一、防抖&#xff08;debounce&#xff09; 1.使用场景 2.js代码实现 3.lodash工具库使用 二、节流&#xff08;throttle&#xff09; 1.使用场景 2.js代码实现 3.lodash工具库使用 前端做项目&#xff0c;为了防止用户因为网络不好数据响应慢&#xff0c;导致进行…

信不信,马上教会你Purple Pi OH开发板之ADB常用命令

开源鸿蒙硬件方案领跑者 触觉智能 本文适用于在Purple Pi OH开发板进行分区镜像烧录。触觉智能的Purple Pi OH鸿蒙开源主板&#xff0c;是华为Laval官方社区主荐的一款鸿蒙开发主板。 该主板主要针对学生党&#xff0c;极客&#xff0c;工程师&#xff0c;极大降低了开源鸿蒙开…