[FreeRTOS 内部实现] 创建任务 xTaskCreate函数解析


创建任务 xTaskCreate函数原型

BaseType_t xTaskCreate(    TaskFunction_t pxTaskCode,
            const char * const pcName,
            const configSTACK_DEPTH_TYPE usStackDepth,
            void * const pvParameters,
            UBaseType_t uxPriority,
            TaskHandle_t * const pxCreatedTask )

根据前面的文章可知,要创建一个任务需要 4个要素:运行函数,函数参数,栈,任务优先级。
在RTOS系统中创建任务函数 xTaskCreate,传入参数解析:

  • TaskFunction_t pxTaskCode :运行函数
  • const char * const pcName :函数名字
  • const configSTACK_DEPTH_TYPE usStackDepth :栈大小
  • void * const pvParameters :运行函数的参数
  • UBaseType_t uxPriority : 函数优先级
  • TaskHandle_t * const pxCreatedTask :任务控制块TCB 输出参数

栈深度 usStackDepth 大小如何确定

通过简单的示例来看下,普通的函数会产生多少栈大小,如何计算?
在这里插入图片描述
(1)PUSH {r4-r5,lr}: 将寄存器r4, r5和链接寄存器(lr)压入栈中。这是为了保存这些寄存器的值,以便函数调用结束后可以恢复它们。
(2)SUB sp,sp,#0x194: 从栈指针sp减去0x194(404),为局部变量分配栈空间。
(3)MOV r5, r1: 将r1寄存器的值移动到r5寄存器。这通常是将输入参数的一个值保存到另一个寄存器中。
(4)MOV r4, r0: 将r0寄存器的值移动到r4寄存器。同样,这可能是保存另一个输入参数。
(5)MOV r1, #0x18C: 将立即数0x18C(404)移动到r1寄存器。
(6)ADD r0, sp, #0x04: 将栈指针sp加上4,然后将结果移动到r0寄存器。
(7)BL.W 0x0800020C __aeabi_memclr4: BL.W是ARM的分支链接指令,用于远距离跳转,并在链接寄存器中保存返回地址。
(8)LDR r0, [pc, #24]: 从程序计数器pc地址加上24的位置加载数据到r0寄存器。
(9)LDR r0, [r0, #0x00]: 从r0寄存器指向的地址加载数据到r0。
(10)STR r0, [sp, #0x00]: 将r0寄存器的值存储到栈指针sp地址偏移0的位置。
(11)LDR r0, [r4, #0x00]: 从r4寄存器指向的地址加载数据到r0。
(12)STR r0, [sp, #0x190]: 将r0寄存器的值存储到栈指针sp地址偏移0x190的位置。
(13)LDR r0, [r5, #0x00]: 从r5寄存器指向的地址加载数据到r0。
(14)LDR r1, [sp, #0x190]: 从栈指针sp地址偏移0x190的位置加载数据到r1。
(15)ADD r0, r0, r1: 将r0和r1寄存器的值相加,结果存储在r0。
(16)STR r0, [sp, #0x190]: 将r0寄存器的值再次存储到栈指针sp地址偏移0x190的位置。
(17)STR r0, [r4, #0x00]: 将r0寄存器的值存储到r4寄存器指向的地址。
(18)ADD sp, sp, #0x194: 将栈指针sp加上0x194,释放之前分配的栈空间。
(19)POP {r4-r5,pc}: 从栈中弹出之前保存的r4, r5和pc值,恢复它们,并返回到调用这个函数的代码处。
在这里插入图片描述
栈大小可以根据任务中使用的临时变量估算出需要多大的栈空间,当然函数传入的栈大小要比估算的大些,以免超出越界访问。(一般为估算大小的两倍)

任务堆空间分配

在这里插入图片描述
#define configTOTAL_HEAP_SIZE ((size_t)3072)
RTOS系统中的栈是ucHeap数据,大小为3072字节
每创建一个任务都会在ucHeap堆空间申请出自己的堆空间。


任务控制块 TCB

作用:在栈中为每个任务分配一个任务控制块(TCB)。存储任务状态信息,包括指向任务上下文的指针(任务的运行时环境,包括寄存器值)。完成创建任务后,会将任务的TCB赋给 pxCreatedTask 参数

*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;

结构体TCB

typedef struct tskTaskControlBlock
{
    volatile StackType_t    *pxTopOfStack;    /* 指向任务堆栈中最后一项的位置。这必须是TCB结构体的第一个成员。*/
    

    
    ListItem_t            xStateListItem;    /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */
    ListItem_t            xEventListItem;        /*< Used to reference a task from an event list. */
    UBaseType_t            uxPriority;            /* 任务优先级。  0 是优先级最低的。 */
    StackType_t            *pxStack;            /* 栈的最开始指针 */
    char            pcTaskName[ configMAX_TASK_NAME_LEN ];/* 任务名称 */ 


} tskTCB;

由于任务会在栈中会以TCB结构体的形式描述,因此 xTaskCreate函数传入的参数都应该在这个结构中体现。

const char * const pcName :函数名字char pcTaskName[ configMAX_TASK_NAME_LEN ];
UBaseType_t uxPriority : 函数优先级UBaseType_t uxPriority;
const configSTACK_DEPTH_TYPE usStackDepthvolatile :栈深度StackType_t *pxTopOfStack
StackType_t *pxStack;

发现在TCB结构体中没有最重要的运行函数以及参数,而在TCB结构体中存在两个链表。
任务在创建的时候,会将PC指向函数名字,R0寄存器存的参数。在任务切换的时候,要将全部环境保存下来,当下次获得CPU使用权的时候,恢复现场运行任务。因此任务栈中保存一套任务环境。
在这里插入图片描述

StackType_t *pxStack;

pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
if( pxNewTCB != NULL )
{
    /* Store the stack location in the TCB. */
    pxNewTCB->pxStack = pxStack;
}
  • 22
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bazinga bingo

您的鼓励就是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值