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队列中等待唤醒
七、其他
- 任务优先级:高优先级优先执行,同等优先级轮流执行。
- 任务状态:就绪态、运行态、阻塞态、挂起态。
- 空闲任务:优先级为0,释放被删除任务的内存,在vTaskStartScheduler被创建。
- 硬件定时器任务:优先级为2,管理软件定时器。
- 调度算法:抢占+时间片轮转+空闲让步(有同样0优先级的任务就绪时,也会让步)

浙公网安备 33010602011771号