前言:

终于熬到放寒假了,距离上次更新又过了三月有余了,煮包打算在寒假更新完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中还有两个参数我们是没有介绍到的,也就是一下两个列表:

恭喜你看到了这里,未来的国家栋梁之才啊哈哈哈啊哈,剩下的将在下一节关于任务调度等等内容揭晓嘿嘿嘿嘿~~~~~~~

 

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐