RTOS内功修炼记—— 任务入口函数执行完毕之后去哪里?
Posted Mculover666
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RTOS内功修炼记—— 任务入口函数执行完毕之后去哪里?相关的知识,希望对你有一定的参考价值。
1. 专栏说明
本专栏是作者深入研究RTOS内核时记录的笔记,目前包含的8篇文章覆盖了RTOS内核基础原理:
- 01-任务到底应该怎么写
- 02-优先级抢占调度到底是怎么回事
- 03-内核到底是如何管理中断的
- 04-小小的时钟节拍,撑起了内核半边天
- 05-任务间同步机制的实现,万变不离其宗
- 06-任务间通信为什么不用全局变量
- 07-动态内存和静态内存管理机制
- 08-CMSIS RTOS API用法详解及示例
在工作过程中,我发现在实际使用RTOS完成项目时,理解这些知识仅能达到会用RTOS的水平,要想用好RTOS,还需要了解一些比较细节的机制,否则容易掉坑进去,花大量时间定位问题。
所以,我决定继续更新这个专栏,分享更多使用RTOS过程中的经验。
2. 任务的通常写法
遵循“不使用就让出”的原则,任务通常有两种写法。
① 阻塞等待某个事件处理,等待到之后处理:
void task1_entry(void *arg)
{
// init...
while (1) {
// 1. wait some kernel object...
// eg. tos_sem_pend, tos_mutex_pend, tos_event_pend.
// 2. wait success, handle!
}
}
这种写法中,在没有事件发生的时候,任务会因为等待某个内核对象而被挂起,让出CPU不参与调度。
② 定时执行
void task1_entry(void *arg)
{
// init...
while (1) {
// 1. do some thing...
// 2. sleep!
// eg. tos_task_delay, tos_sleep_ms.
}
}
这种写法中,任务在干完活之后,会主动进入睡眠状态,让出CPU不参与调度。
3. 一次性任务
上面两种写法的共性是都有主循环,不需要考虑任务入口函数退出的情况,但在一些场景中任务只需要执行一次即可:
void task1_entry(void *arg)
{
// init...
// do some thing...
// exit?
}
这个时候就要思考一个问题:任务入口函数执行完毕之后去了哪里?
4. 寻找答案
首先,任务入口函数本质上是一个函数,跳转函数的指令是BL
,CPU在执行该指令跳转到某个函数执行时,会将当前PC地址作为函数返回地址、加载到LR寄存器中、保证函数执行完可以返回到这儿继续执行,再将函数地址加载到PC寄存器、程序接着执行就到了函数中。
那么,任务入口函数没有被别的函数主动调用,是如何被拉起来执行的呢?
任务切换分为两步:保存上文、切换下文。切换下文就是指将保存在任务栈中的CPU寄存器组的值、加载到CPU中。
所以,当任务栈中初始保存的CPU寄存器组的值中、PC寄存器值为该任务的任务入口函数地址时,切换下文加载之后,由于PC指向任务入口函数,所以CPU接着运行就到了任务入口函数中,也就是该任务在运行。
同样的道理,任务栈中初始保存的CPU寄存器组的值中、LR寄存器的值决定了、任务入口函数退出时候返回到哪里。
由于不同CPU架构的CPU寄存器组不同,所以初始化任务栈的代码与架构强相关,在arch目录下都有不同架构对应的实现。
这里我们以ARM Cortex-M4为例(Arm-v7m)看看代码如何实现:
从代码里可以看到,TencentOS-Tiny默认退出函数为exit参数指定的值,接下来我们看看退出函数~
5. 任务退出函数
在创建任务的API tos_task_create
中,初始化任务栈的过程中会指定退出函数为 task_exit
:
task->sp = cpu_task_stk_init((void *)entry, arg, (void *)task_exit, stk_base, stk_size);
task_exit 函数主要完成销毁自身的工作,具体实现如下:
__STATIC__ void task_exit(void)
{
tos_task_destroy(K_NULL);
}
该销毁函数传入的参数为NULL表示销毁自身,如果是静态任务则按以下步骤销毁(动态任务销毁值得用一篇文章去讲述):
- 将任务从就绪列表移除
- 将任务从等待列表移除
- 将任务从统计列表移除
- 任务状态置为K_TASK_STATE_DELETED
6. 总结
本文讲述了任务的两种常规写法,以及任务函数执行完毕之后去了哪里?
当任务函数执行完毕退出时,会执行到哪里由任务栈初始化时LR寄存器的值决定,RTOS内核都会提供一个默认退出函数,TencentOS-Tiny提供的任务退出函数中,会自动销毁任务自身。
所以在编写一次性任务时,就不需要主动调用销毁API销毁自身啦~
接收更多精彩文章及资源推送,欢迎订阅我的微信公众号:『mculover666』。
以上是关于RTOS内功修炼记—— 任务入口函数执行完毕之后去哪里?的主要内容,如果未能解决你的问题,请参考以下文章