FreeRTOS的Task

FreeRTOS创建任务
创建任务实际上是新增一个TCB,并将其添加到就绪表中参加调度。

  1. 申请一个任务控制块TCB,包含一个任务的属性和控制状态信息
  2. 申请任务栈Stack,每个任务拥有一片独立的栈空间
  3. 初始化TCB以及绑定TCB和Stack
  4. 将TCB添加到系统的就绪表

创建任务参数

  1. pxTaskCode,任务主体函数指针
  2. pcName,函数名称,仅debug用
  3. usStackDepth,堆栈深度(大小)
  4. pvParameters,函数传参
  5. uxPrioity,任务优先级
  6. 任务Handle

任务的创建流程

  1. TCB_t *pxNewTCB = pvPortMalloc(sizeof(TCB_t));定义和申请任务控制块内存。
  2. pxNewTCB->pxStack = pvPortMalloc(usStackDepth * sizeof(StackType_t));申请任务栈空间,并绑定到TCB中
  3. prvInitialiseNewTask,初始化任务,实质是将创建任务接口的参数设置TCB中的内容,比如TCB对应任务的入口、优先级、栈指针、计算栈顶指针、任务名称等;
    a. pxTopOfStack = pxNewTCB->pxStack + (ulStackDepth – (uint32_t)1);向下增长类型:栈顶指针 = 栈指针 + 栈深度;
    b. pxTopOfStack = pxNewTCB->pxStack;向上增长类型:栈顶指针 = 栈指针
    c. loop:pxNewTCB->pcTaskName[x] = pcName[x],设置任务名字,结尾处补’\0’;
    d. pxNewTCB->uxPrioity = uxPrioity;设置任务的优先级;
    e. vListInitialiseItem( ( pxNewTCB->xStateListItem ) );初始化TCB中的状态链表,并将链表linkback到TCB上
    f. vListInitialiseItem( ( pxNewTCB->xEventListItem ) );初始化TCB中的事件链表,设置链表值关联任务优先级,并将链表linkback到TCB上
    g. pxNewTCB->uxCriticalNesting = 0;设置中断嵌套层级,用于临界区的控制
  4. prvAddNewTaskToReadyList(pxNewTCB);将新任务的TCB插入就绪表;
    a. taskENTER_CRITICAL();进入临界区,关闭系统调用中断191
    b. 全局变量当前任务数加1:uxCurrentNumberOfTask++;
    c. 判断全局变量当前任务是否为空
    i. 是则表示首次创建任务,并将pxNewTCB赋给pxCurrentTCB
    ii. 否则检查xSchedulerRunning调度器有没有运行,没有的话再判断pxCurrentTCB是否低于pxNewTCB的优先级,低于则将pxNewTCB更新到pxCurrentTCB
    d. 任务数加1:uxTaskNumber++,注意区分不是uxCurrentNumberOfTask,这里是总的任务数,用于跟踪调试
    e. 添加任务TCB到就绪表:prvAddTaskToReadyList(pxNewTCB);
    f. 设置任务TCB:portSETUP_TCB(pxNewTCB);
    g. 判断调度器是否已运行,xSchedulerRunning
    i. xSchedulerRunning != pdFALSE,调度器已经运行,需要判断当前任务的优先级是否低于新任务,是则将新任务替换当前任务;
    h. 退出临界区:taskEXIT_CRITICAL()
  5. 小结一下
    创建任务的本质是创建任务的TCB,并将TCB加入调度。在过程中会有两个点值得注意,一个是添加就绪表时调度器是否已经运行,比如在调度器运行前创建多个任务的场景,只有一个任务会被挂到当前任务,所以创建时需要对比任务的优先级,把优先级最高的任务挂到当前任务上。如果调度器已经运行则在任务创建完成退出临界区之后,判断新任务优先级是否比当前任务高,是则呼叫一次系统调度产生任务切换,否则不需要切换任务。另一个点是栈的生长方向,向下生长的栈顶指针等于栈首指针加上栈深度,使用时栈地址是递减的;向上增长的栈栈顶指针即栈首指针,使用时栈地址是递增的。

任务的删除流程

  1. 任务删除时任务的依据是taskhandle,这个在创建任务时是TCB的首地址强制转换的,需要删除时将其强制转回TCB类型地址即可。TCB_t *pxTCB = prvGetTCBFromHandle(xTaskToDelete);
  2. 将pxTCB从状态链表中移除:uxListRemove(&(pxTCB->xStateListItem))
  3. 就绪表优先级查询值清0,taskRESET_READY_PRIOITY(pxTCB->uxPriority)
  4. 释放内存空间vPortFree

通讯更新

请输入您的电子邮件地址进行订阅