FreeRTOS任务创建时消失的三个形参?
本文详细解析了FreeRTOS任务创建过程中的关键细节,重点分析了任务栈的内存分布机制。作者首先回顾了任务创建函数原型中缺失的pxTaskCode、usStackDepth和pvParameters三个参数在TCB中的转化过程。通过分析源码中的pxPortInitialiseStack函数,揭示了任务栈初始化的核心机制:函数入口地址(pxCode)被存入栈中特定位置,在任务切换时可恢复到PC寄存器
前言:
终于熬到放寒假了,距离上次更新又过了三月有余了,煮包打算在寒假更新完FreeRTOS的内部实现机制(哈哈哈,最后更新多少看情况啦~~,毕竟学不完,根本学不完呜呜呜呜呜);上次煮包分享了FreeRTOS任务创建在内存上的表现,这次主要是依据源码对上次进行一些细节补充,重点就是任务创建完成时,初始任务栈的空间分布,当然还有些小破烂知识补充,就等大伙自个儿瞧啦~~~哈哈哈;读完本节内容,相信一定可以更加深入的理解FreeRTOS中的任务创建;好啦,话不多说,看正文吧~
一、总结写在前面吊胃口哈哈哈:
当看完本节内容时,我们已经了解完任务创建的70%啦~(胡乱起的),总结一下就是讲完了TCB中的栈顶指针,栈起始位置,任务优先级,任务名字等,也深入内核讲解了任务栈初始栈空间的分布,最后剩下两个链表留着在本文最后揭晓哈哈哈哈;
二、任务栈的三点思考:
首先回顾一下创建任务时传入的参数,这是煮包截图的FreeRTOS任务创建的源码部分,也就是任务创建函数的原型;可以发现其实有三个参数是TCB中没有的,分别是pxTaskCode、usStackDepth、pvParameters;准确来说是两个哈哈哈哈,因为由usStackDepth通过一些计算可以得到TCB中的栈顶指针和栈起始地址;
1.任务栈大小依赖什么?
任务栈就是用来保存传入的任务函数中的局部变量,函数调用的栈帧,以及发生任务切换时的CPU寄存器,也即是这个任务本身的任务上下文;
所以决定任务栈大小的就是依赖传入的任务函数中的局部变量,函数调用的栈帧,以及发生任务切换时的CPU寄存器;
2.任务栈从哪里分配?
所谓栈就是一片空闲内存,这块空闲内存我们可以自己管理,甚至可以是一个巨大的数组;

这里是我截取FreeRTOS内存管理算法中的heap4.c中的一部分,可以看到,FreeRTOS的做法就是定义了一个巨大的静态全局数组,当我们传入了usStackDepth参数时,FreeRTOS就会通过自己封装过的malloc函数,从该数组中分配一部分给任务做自己的任务栈。然后将这部分空间的起始地址存到TCB中的pxStack字段中,也就是保存任务栈的起始地址;
补充知识小破站:该静态全局数组实际是属于.bss段,会在上电后的__main函数中进行清零操作,属于SRAM部分且不占用Flash空间;
3.任务栈创建时的栈空间分布
前面我们提到,从TCB中消失的三个参数,其中有一个通过各种计算化身成为了TCB中的栈顶指针和栈起始位置,现在还剩下两个自然是会在初始任务栈的分布中看到它们的身影:
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
TaskFunction_t pxCode,
void * pvParameters )
{
pxTopOfStack--;
*pxTopOfStack = portINITIAL_XPSR; /* xPSR */
pxTopOfStack--;
*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */
pxTopOfStack -= 5; /*R12,R3,R2,R1*/
*pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
pxTopOfStack--;
*pxTopOfStack = portINITIAL_EXC_RETURN;
pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */
return pxTopOfStack;
}
核心就是这段函数,因为很多内核相关的知识煮包已经在第一篇文章《深入内核理解ARM单片机的中断机制》里面讲过,其他更多的内核知识应该在更新完FreeRTOS内部实现机制后深入学习并更新,这里只提到两个重要的:
即我们传入的pxTaskCode函数入口地址将被存放到任务栈中的某个位置,这个位置满足当我们进行任务切换恢复任务上下文时,刚好将函数入口地址恢复到ARM内核的PC寄存器,这个值恢复到PC寄存器中内核就可以跳转到函数执行,就是完成了任务切换;
而我们传入的pvParameters任务参数将被用相同的原理存放到任务栈中的某个位置,这个位置满足任务切换恢复任务上下文时,将pvParameters的值弹出到内核的R0寄存器;
至此关于本小节关于任务创建的补充就完成啦~~希望对大家有帮助,最后做一个下一期的预告吧~因为我们可以发现,TCB中还有两个参数我们是没有介绍到的,也就是一下两个列表:

恭喜你看到了这里,未来的国家栋梁之才啊哈哈哈啊哈,剩下的将在下一节关于任务调度等等内容揭晓嘿嘿嘿嘿~~~~~~~
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)