- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
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;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
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中的同名宏定义,数值也一样,表示任务运行状态,
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
2.1 HalStartToRun汇编函数
开始运行函数HalStartToRun
被文件kernel\arch\arm\cortex-m7\gcc\los_context.c
中的开始调度函数HalStartSchedule
在系统启动阶段调用。我们接下来分析下该函数的汇编代码。
⑴处设置PendSV
异常优先级为OS_NVIC_PENDSV_PRI
,PendSV
异常一般设置为最低。⑵处往控制寄存器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个字节,其中包含S16
到S31
共16个4字节,R4
到R11
及uwPriMask
共9个4字节的长度,执行指令后,R12
指向任务栈中上下文的UINT32 uwR0
位置。
⑺处代码把任务栈上下文中的UINT32 uwR0-uwR3, UINT32 uwR12; UINT32 uwLR; UINT32 uwPC; UINT32 uwxPSR;
共8个成员变量数值分别加载到寄存器R0-R7
,其中R5
对应UINT32 uwLR
,R6
对应UINT32 uwPC
,此时寄存器R12
指向任务栈上下文的UINT32 uwxPSR
。然后执行下一个指令,指针继续加72字节(=18个4字节长度),即对应S0
到S15
及UINT32 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_SYSPRI2
ldr r5, =OS_NVIC_PENDSV_PRI
str r5, [r4]
⑵ mov r0, #2
msr 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, r12
vpush {S0}
vpop {S0}
#else
⑼ add r12, r12, #36
ldmfd r12!, {r0-r7}
msr psp, r12
#endif
⑽ mov lr, r5
//MSR xPSR, R7
cpsie I
bx r6
.fnend
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
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
.cantunwind
ldr r0, =OS_NVIC_INT_CTRL
ldr r1, =OS_NVIC_PENDSVSET
str r1, [r0]
dsb
isb
bx lr
.fnend
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
3.4 HalPendSV汇编函数
接下来,我们分析下HalPendSV
汇编函数的源代码。⑴处把寄存器PRIMASK
数值写入寄存器r12
,备份中断的开关状态,然后执行指令cpsid I
屏蔽全局中断。⑵处把寄存器r12
、lr
入栈,然后调用上文分析过的任务切换函数OsSchedTaskSwitch
。函数执行完毕,执行⑶处指令出栈,恢复寄存器r12
、lr
数值。⑷处比较寄存器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, PRIMASK
cpsid I
HalTaskSwitch:
⑵ push {r12, lr}
blx OsSchedTaskSwitch
⑶ pop {r12, lr}
⑷ cmp r0, #0
⑸ mov r0, lr
⑹ bne TaskContextSwitch
⑺ msr PRIMASK, r12
bx lr
TaskContextSwitch:
⑻ mov lr, r0
⑼ mrs r0, psp
stmfd 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}
#endif
ldmfd r1!, {r4-r12}
⒃ msr psp, r1
⒄ msr PRIMASK, r12
bx lr
.fnend
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
小结
本文带领大家一起剖析了鸿蒙轻内核调度模块的源代码,包含调用接口及底层的汇编函数实现。
如果大家想更加深入的学习 OpenHarmony 开发的内容,不妨可以参考以下相关学习文档进行学习,助你快速提升自己:

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

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



data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/maniuT/article/details/139482701","extend1":"pc","ab":"new"}">>
id="blogExtensionBox" style="width:400px;margin:auto;margin-top:12px" class="blog-extension-box"> class="blog_extension blog_extension_type2" id="blog_extension">
class="extension_official" data-report-click="{"spm":"1001.2101.3001.6471"}" data-report-view="{"spm":"1001.2101.3001.6471"}">
class="blog_extension_card_left">
class="blog_extension_card_cont">
鸿蒙开发学习资料领取!!!
class="blog_extension_card_cont_r">
微信名片
评论记录:
回复评论: