FreeRTOS的任务管理

一、FreeRTOS的任务介绍

FreeRTOS中的任务就是一个函数,一般是永不返回的,一个函数可以用来创建多个任务,每个任务都有自己的栈空间。任务有以下几种内容:任务的创建和删除、任务调度、任务优先级、任务状态、特殊任务。

二、TCB结构体

typedef struct tskTaskControlBlock
{
  volatile StackType_t	*pxTopOfStack;        // TCB的第一个成员是栈顶指针
  ListItem_t			xStateListItem;       // 任务状态管理用链表成员
  ListItem_t			xEventListItem;       // 事件管理用链表成员
  UBaseType_t			uxPriority;	          // 任务优先级
  StackType_t			*pxStack;             // 栈起始地址
  char				pcTaskName[ configMAX_TASK_NAME_LEN ];  // 任务名称
  UBaseType_t		uxBasePriority;           // 优先级继承记录原始优先级-Mutex
  UBaseType_t		uxMutexesHeld;            // 持有Mutex
  uint32_t          ulNotifiedValue;          // 任务通知-值
  uint8_t           ucNotifyState;            // 任务通知状态
}tskTCB;
typedef tskTCB TCB_t;

三、任务创建函数

// 任务创建
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 )      // 任务句柄
{
  TCB_t *pxNewTCB;
  BaseType_t xReturn;
  StackType_t *pxStack;
  pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );      // 分配栈空间,栈深度以四字节为单位
  pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );                                                   // 分配TCB结构体
  pxNewTCB->pxStack = pxStack;                                                                              // 设置TCB结构体的栈起始地址
  prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );  // 具体的初始化操作
  prvAddNewTaskToReadyList( pxNewTCB );                                                                     // 加入就绪链表
  xReturn = pdPASS;
  return xReturn;
}

void prvInitialiseNewTask(...)
{
  StackType_t *pxTopOfStack;
  ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );  // 使用指定值初始化栈空间
  pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );                                               // 计算栈顶地址
  pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );  // 内存对齐
  // 设置pxNewTCB->pcTaskName,代码略
  // 设置pxNewTCB->uxPriority
  // 设置pxNewTCB->uxBasePriority
  // 设置pxNewTCB->uxMutexesHeld=0
  // 设置pxNewTCB->ulNotifiedValue=0
  // 设置pxNewTCB->ucNotifyState为taskNOT_WAITING_NOTIFICATION
  vListInitialiseItem( &( pxNewTCB->xStateListItem ) );          
  vListInitialiseItem( &( pxNewTCB->xEventListItem ) );        // 初始化链表节点
  listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
  listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );
  listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );
  pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );            // 从栈顶初始化任务栈,不同芯片处理函数不同
  *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;                  // 设置句柄
}
StackType_t *pxPortInitialiseStack(...)
{
  // 栈空间-xPSR、PC、LR、R12、R3、R2、R1、R0、R11-R4
  // PC表示函数的入口地址、LR表示死循环函数的入口地址、R0表示任务传参
  return pxTopOfStack;        // 初始化后栈顶指针减小16
}

void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
  taskENTER_CRITICAL();        // 关中断
  {
    uxCurrentNumberOfTasks++;    // 全局变量,创建任务计数
    if( pxCurrentTCB == NULL )   // 全局变量,没有任务在执行
    {
      pxCurrentTCB = pxNewTCB;
      if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
      {
        prvInitialiseTaskLists();    // 初始化各个优先级的就绪队列、delayed队列、pending队列等等
      }
    }
    else
    {
      if( xSchedulerRunning == pdFALSE )
      {
        if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )        // 调度器没有运行时,取最高优先级的任务作为第一个任务
        {
          pxCurrentTCB = pxNewTCB;
        }
      }
    }
    prvAddTaskToReadyList( pxNewTCB );                  // 加入就绪队列
  }
  taskEXIT_CRITICAL();         // 开中断
  if( xSchedulerRunning != pdFALSE )
  {
    if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
    {
      taskYIELD_IF_USING_PREEMPTION();                                // 调度器运行时,更高优先级的任务应该被立刻执行
    }
  }
}

四、任务删除函数

void vTaskDelete( TaskHandle_t xTaskToDelete )
{
  TCB_t *pxTCB;
  taskENTER_CRITICAL();
  pxTCB = prvGetTCBFromHandle( xTaskToDelete );        // 句柄是NULL,删除自身即pxCurrentTCB
  uxListRemove( &( pxTCB->xStateListItem ) );          // 从就绪队列移除
  uxListRemove( &( pxTCB->xEventListItem ) );          // 如果TCB挂在一个event队列,在等待某些事件,也移除
  if( pxTCB == pxCurrentTCB )
  {
    vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );    // 如果是删除自身,则将任务挂入xTasksWaitingTermination
  }
  else
  {
    prvDeleteTCB( pxTCB );                              // 如果是删除其他,vPortFree释放帧和TCB
  }
  taskEXIT_CRITICAL();
  // 如果调度器正在运行,且删除自身,手动调度一下portYIELD_WITHIN_API();
}

五、任务调度函数

void vTaskStartScheduler( void )
{
  // 创建一个空闲任务,优先级最低
  // 创建一个定时器任务,优先级较低(软件定时器的工作依赖此任务)
  // xPortStartScheduler()和架构相关的调度开始函数
}
BaseType_t xPortStartScheduler( void )
{
  vPortSetupTimerInterrupt();       // 设置定时器相关的中断,硬件定时器中断中完成tick值自加,并根据tick值将符合条件的TCB移到就绪队列,根据调度策略判断是否需要调度
  prvStartFirstTask();              // 启动第一个任务,对于STM32,是初始化MSP、启动中断、并触发SVC中断实现的,SVC中断中找到pxCurrentTCB,恢复上下文,跳转执行
}

六、delay函数

void vTaskDelay( const TickType_t xTicksToDelay );        // 根据当前tick+delaytick计算唤醒时间,挂入delay队列中等待唤醒
BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ); // 根据上次被唤醒tick+delaytick计算唤醒时间,挂入delay队列中等待唤醒

七、其他

  1. 任务优先级:高优先级优先执行,同等优先级轮流执行。
  2. 任务状态:就绪态、运行态、阻塞态、挂起态。
  3. 空闲任务:优先级为0,释放被删除任务的内存,在vTaskStartScheduler被创建。
  4. 硬件定时器任务:优先级为2,管理软件定时器。
  5. 调度算法:抢占+时间片轮转+空闲让步(有同样0优先级的任务就绪时,也会让步)
posted @ 2025-07-01 21:46  gramming  阅读(319)  评论(0)    收藏  举报