FreeRTOS 源码解析笔记

剖析 FreeRTOS 内核源码:任务动态创建全流程、任务堆栈初始化、控制块与就绪列表、挂起与恢复的实现、临界段保护的嵌套实现。

1.动态创建详细过程

动态创建

1.首先tasks.c中

结构体并给别名,所以初始化可以用TCB_t

typedef struct tskTaskControlBlock

{

…

}tskTCB; typedef tskTCB TCB_t;

2.因此在xTaskCreate()实现中

TCB_t * pxNewTCB;创建了任务控制块

3.任务控制块的结构体中

状态列表 事件列表 优先级 栈指针 任务名

image-20250802183613953

4.回到xTaskCreate

判断生长方向,STM32栈区向下生长

#if ( portSTACK_GROWTH > 0 )

进入的

#else中

其中先定义任务堆栈大小:

StackType_t * pxStack;//这个申请的代表首地址

pxStack = pvPortMallocStack( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );//如128*4字节,128字×每字字节数=字节数

判断是否申请成功任务堆栈,成功则进入

 if( pxStack != NULL )
 {
                pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); //任务控制块信息也需要空间,因此申请
                if( pxNewTCB != NULL ) //如果申请了一块
                {
                    memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );
                    pxNewTCB->pxStack = pxStack;//然后将首地址给到任务控制块的这个成员变量
                }
                else
                {
                    vPortFreeStack( pxStack );//否则就释放掉任务堆栈
                }
            }
            else
            {
                pxNewTCB = NULL;
            }

再下面判断是否申请成功任务控制块

prvInitialiseNewTask:这个用来初始化任务控制块中的成员

prvAddNewTaskToReadyList:将任务添加到就绪列表

 if( pxNewTCB != NULL )
        {
            prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
            prvAddNewTaskToReadyList( pxNewTCB );
            xReturn = pdPASS;//就是成功的返回值
        }
        else
        {
            xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
        }

5.到prvInitialiseNewTask

( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );

这句pxStack是任务堆栈的首地址,将( size_t ) ulStackDepth * sizeof( StackType_t )大小的地址赋值为( int ) tskSTACK_FILL_BYTE

而这个值是

#define tskSTACK_FILL_BYTE ( 0xa5U )

意思就是整个任务堆栈赋值成a5,那就可以通过判断多少a5被改变,得到剩下的空间

6.判断 #if ( portSTACK_GROWTH < 0 )

是否向下生长?

STM32:是的

//首地址的最低位,取得栈顶
pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] );

判断名字是否为空

//pcName是一个入口参数
if( pcName != NULL )
    {
        for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
        {
            pxNewTCB->pcTaskName[ x ] = pcName[ x ]; //给到任务控制块的这个成员里
            if( pcName[ x ] == ( char ) 0x00 )
            {
                break;
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

判断优先级

优先级大于最大优先级,就改为最大优先级

if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
    {
        uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
    }

pxNewTCB->uxPriority = uxPriority;

然后初始化状态列表项和事件列表项

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 );

7.进入pxPortInitialiseStack

里面栈顶陆续减,就可以往下陆续给赋值。

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 and R1. */
    *pxTopOfStack = ( StackType_t ) pvParameters;                        /* R0 */
    pxTopOfStack -= 8;                                                   /* R11, R10, R9, R8, R7, R6, R5 and R4. */

    return pxTopOfStack;
}

PC是任务函数指针,切换到A任务时,就通过这个指针切换到任务函数A

image-20250802194920338

这个实际就是最初传入的函数指针

image-20250802195354593

而R0传入的是任务函数参数

image-20250802195543474

程序结束后返回更新后的pxTopOfStack

8.回到prvInitialiseNewTask

将任务句柄给到任务控制块

if( pxCreatedTask != NULL ) { *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; }

9.再到prvAddNewTaskToReadyList()

这是将任务添加到就绪列表

先进入临界区 taskENTER_CRITICAL();

增加任务数量uxCurrentNumberOfTasks++;

如果当前没有任务,这是第一个任务pxCurrentTCB = pxNewTCB;

如果这确实是第一个if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )

就初始化任务列表prvInitialiseTaskLists()

image-20250802201105871

10.进入prvInitialiseTaskLists()

初始化32个就绪列表0~31

for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )
    {
        vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );
    }

如果使能删除列表,就初始化一个等待删除列表

如果使能挂起列表,就初始化一个挂起列表

#if ( INCLUDE_vTaskDelete == 1 )
    {
        vListInitialise( &xTasksWaitingTermination );
    }
    #endif /* INCLUDE_vTaskDelete */

    #if ( INCLUDE_vTaskSuspend == 1 )
    {
        vListInitialise( &xSuspendedTaskList );
    }
    #endif /* INCLUDE_vTaskSuspend */

11.继续prvAddNewTaskToReadyList()

否则的话,如果当前任务控制块优先级小于新创建的任务控制块

那新创建的就赋给当前

if( xSchedulerRunning == pdFALSE )
            {
                if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
                {
                    pxCurrentTCB = pxNewTCB;
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }

下面是任务标号,就可以通过这个查询这是第几个创建的任务

image-20250802201852893

12.进入prvAddTaskToReadyList( pxNewTCB );

进入 taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );

进如portRECORD_READY_PRIORITY

得到

#define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL « ( uxPriority ) )

表示就绪列表,哪个位置一,哪个位就有任务,从上往下读就知道哪个任务优先级最高

image-20250802202706272

13.继续prvAddNewTaskToReadyList()

退出临界区taskEXIT_CRITICAL();

如果任务调度器开启,当前任务优先级,小于新的优先级,执行任务切换,到优先级最高的

image-20250802202950218

2.任务挂起与恢复详细过程

任务挂起

1.进入prvGetTCBFromHandle

如果句柄为NULL,返回当前任务控制块句柄,否则返回传入任务控制块句柄

#define prvGetTCBFromHandle( pxHandle )    ( ( ( pxHandle ) == NULL ) ? pxCurrentTCB : ( pxHandle ) )

2.回到vTaskSuspend

if内是不管当前状态是什么,并移除列表项,移除成功则进入

 if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
            {
                taskRESET_READY_PRIORITY( pxTCB->uxPriority );
            }

3.进入taskRESET_READY_PRIORITY

if(listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[( uxPriority ) ] ) ) == ( UBaseType_t ) 0 ) 
        {                                                                               
            portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) );        
        }  

4.回到vTaskSuspend

移除事件列表项

if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
            {
                ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
            }

插入挂起列表项末尾

vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );

如果带挂起的任务控制块在等待消息通知,清除消息通知

for( x = 0; x < configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ )
                {
                    if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION )
                    {
                        pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION;
                    }
                }

判断调度器是否正在运行

如果正在运行,更新下一个阻塞超时时间

if( xSchedulerRunning != pdFALSE )
        {
            taskENTER_CRITICAL();
            {
                prvResetNextTaskUnblockTime();
            }
            taskEXIT_CRITICAL();
        }

判断挂起是否为任务自己,如果挂起的是自己,强制进行任务切换。

如果不是,判断当前挂起的任务总数等于创建的任务总数,表明全部在挂起,则将任务控制块赋值为NULL,

if( pxTCB == pxCurrentTCB )
        {
            if( xSchedulerRunning != pdFALSE )
            {
                configASSERT( uxSchedulerSuspended == 0 );
                portYIELD_WITHIN_API();
            }
            else
            {
                if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) 
                {
                    pxCurrentTCB = NULL;
                }
                else
                {
                    vTaskSwitchContext();		//查找下一个最高优先级任务
                }
            }
        }

任务恢复

任务句柄赋值给任务控制块

TCB_t * const pxTCB = xTaskToResume;

恢复的任务控制块不能等于当前任务和NULL

if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) )

如果成立进入,判断任务控制块是否在挂起列表中

if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE )

如果成立进入,在挂起列表中移除

( void ) uxListRemove( &( pxTCB->xStateListItem ) );

并添加到就绪列表

prvAddTaskToReadyList( pxTCB );

在判断优先级是否大于当前优先级,如果是,则任务切换

taskYIELD_IF_USING_PREEMPTION();

 if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) )
        {
            taskENTER_CRITICAL();
            {
                if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE )
                {
                    traceTASK_RESUME( pxTCB );
                    ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
                    prvAddTaskToReadyList( pxTCB );
                    if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
                    {
                        taskYIELD_IF_USING_PREEMPTION();
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            taskEXIT_CRITICAL();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }

3.临界段代码保护

通过++,–实现了嵌套,本质还是基于portDISABLE_INTERRUPTS();和portENABLE_INTERRUPTS();

/*进入临界段*/
#define taskENTER_CRITICAL()               portENTER_CRITICAL()
#define portENTER_CRITICAL()               vPortEnterCritical()

void vPortEnterCritical( void )
{
    portDISABLE_INTERRUPTS();
    uxCriticalNesting++;
    if( uxCriticalNesting == 1 )
    {
        configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
    }
}

/*退出临界段*/
void vPortExitCritical( void )
{
    configASSERT( uxCriticalNesting );
    uxCriticalNesting--;
    if( uxCriticalNesting == 0 )
    {
        portENABLE_INTERRUPTS();
    }
}

4.任务调度

使用 Hugo 构建
主题 Stack 由 Jimmy 设计