任务创建是FreeRTOS系统启动的第一个步骤,前面在启动调度器的时候先创建了空闲任务,然后再由调度器跳到任务里面去执行。任务创建函数里面做了很多的工作,先会为任务堆栈和任务控制块分配内存并初始化它们,然后将任务添加到就绪列表里面,等待调度器来调用。
任务创建的接口定义如下:
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 )
形参代表的意义如下:
pxTaskCode 任务函数入口地址
pcName 任务名称
usStackDepth 任务堆栈大小
pvParameters 传递给任务函数的参数
uxPriority 任务优先级
pxCreatedTask 任务控制块句柄
源码分析:
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;
#if( portSTACK_GROWTH > 0 )
{
/* 如果堆栈向上增长,则先分配任务控制块 */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
if( pxNewTCB != NULL )
{
/* 分配任务堆栈,具体内存分配实现在内存管理章节分析 */
pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
if( pxNewTCB->pxStack == NULL )
{
/* 堆栈分配失败就释放之前分配的任务控制块内存 */
vPortFree( pxNewTCB );
pxNewTCB = NULL;
}
}
}
#else
{
StackType_t *pxStack;
/* 如果堆栈向下增长,则先分配任务堆栈 */
pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
if( pxStack != NULL )
{
/* 堆栈分配成功后分配任务控制块空间 */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
if( pxNewTCB != NULL )
{
/* 初始化任务控制块的堆栈指针 */
pxNewTCB->pxStack = pxStack;
}
else
{
/* 任务控制块分配失败就释放之间分配的任务堆栈内存 */
vPortFree( pxStack );
}
}
else
{
pxNewTCB = NULL;
}
}
#endif
if( pxNewTCB != NULL )
{
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
{
/* 可以静态也可以动态分配的情况下默认使用动态分配内存 */
pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
}
#endif
/* 初始化任务 */
prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
/* 把列表项添加到就绪列表中,优先级高过原来的话就请求一次任务切换 */
prvAddNewTaskToReadyList( pxNewTCB );
xReturn = pdPASS;
}
else
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
return xReturn;
}
上面任务创建的过程中先分配任务堆栈和任务控制块,然后初始化任务并把任务添加到就绪列表,分别调用了如下接口来实现
pvPortMalloc
vPortFree
prvInitialiseNewTask
prvAddNewTaskToReadyList
关于内存分配和释放的接口等后面内存管理章节再进行分析,这里分析下任务初始化和添加到就绪列表的过程。
下面任务初始化源码去掉了部分宏定义的代码:
static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint32_t ulStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask,
TCB_t *pxNewTCB,
const MemoryRegion_t * const xRegions )
{
StackType_t *pxTopOfStack;
UBaseType_t x;
configASSERT( pcName );
#if( portSTACK_GROWTH < 0 )
{
/* 如果栈的生长方便是向下生长的话执行这段代码 */
/* 将栈顶指针取出,减1是因为数组小标从0开始计的 */
pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] );
/* 将栈顶指针8字节对齐 */
pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
}
#else/* 如果栈的生长方向是向上生长的话执行这段代码 */
{
pxTopOfStack = pxNewTCB->pxStack;
/* 计算栈的结束地址 */
pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
}
#endif
for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
{
/* 初始化任务控制块成员任务名称 */
pxNewTCB->pcTaskName[ x ] = pcName[ x ];
/* 等于0x00代表名字获取结束 */
if( pcName[ x ] == ( char ) 0x00 )
{
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* 避免名字过长,强制裁剪添加字符串结束符'\0' */
pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
{
/* 优先级最高不能超过configMAX_PRIORITIES (这里定义的是5,即最高优先级不超过4) */
uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 初始化任务控制块成员任务优先级,最大不能超过4 */
pxNewTCB->uxPriority = uxPriority;
#if ( configUSE_MUTEXES == 1 )
{
/* 使能了互斥信号量功能 */
pxNewTCB->uxBasePriority = uxPriority;
pxNewTCB->uxMutexesHeld = 0;
}
#endif
/* 初始化状态列表项,前面章节已经分析过了 */
vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
/* 初始化事件列表项 */
vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
/* 设置状态列表项属于当前任务控制块 */
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
/* 设置事件列表项值(xItemValue越大,优先级越小,因为插入是按xItemValue升序排序的,先插入的优先级高先执行,所以需要做个减法来反转) */
listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );
/* 设置事件列表项属于当前任务控制块 */
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );
/* 使能临界区嵌套 */
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
{
pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U;
}
#endif
/* 使能任务标签功能 */
#if ( configUSE_APPLICATION_TASK_TAG == 1 )
{
pxNewTCB->pxTaskTag = NULL;
}
#endif
/* 使能时间统计功能 */
#if ( configGENERATE_RUN_TIME_STATS == 1 )
{
pxNewTCB->ulRunTimeCounter = 0UL;
}
#endif
/* 使能MPU相关 */
#if ( portUSING_MPU_WRAPPERS == 1 )
{
vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth );
}
#else
{
/* 避免报错 */
( void ) xRegions;
}
#endif
/* 如果使能了相应的宏,初始化线程本地存储指针 */
#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 )
{
for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ )
{
pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL;
}
}
#endif
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
{
/* 初始化通知状态为没等待 */
pxNewTCB->ulNotifiedValue = 0;
pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
}
#endif
/* 使能NEWLIB */
#if ( configUSE_NEWLIB_REENTRANT == 1 )
{
_REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) );
}
#endif
/* 初始化任务堆栈(xpsr、pc、lr、r0-r12),在下面分析 */
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
if( pxCreatedTask != NULL )
{
/* 返回任务控制块指针 */
*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
上面设置列表项的两个函数定义如下:
#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )
#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( ( pxListItem )->xItemValue = ( xValue ) )
初始化任务堆栈的函数定义如下,如果不了解寄存器的具体作用建议参考一下Cortex-M3权威手册:
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
/* xPSR程序状态寄存器,bit24为1表示使用Thumb指令 */
/* #define portINITIAL_XPSR ( 0x01000000 ) */
pxTopOfStack--;
*pxTopOfStack = portINITIAL_XPSR;
/* PC程序计数器,向PC写数据会引发一次程序分支 */
pxTopOfStack--;
*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;
/* LR连接寄存器 */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) prvTaskExitError;
/* 通用目的寄存器R0-R12 */
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;
}
在初始化完任务后会将任务加入到就绪列表,函数实现如下:
static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
/* 进入临界区 */
taskENTER_CRITICAL();
{
/* 当前任务数量递增 */
uxCurrentNumberOfTasks++;
if( pxCurrentTCB == NULL )
{
/* 如果这是第一个创建的任务执行下面的内容 */
pxCurrentTCB = pxNewTCB;
if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
{
/* 初始化所有任务列表 */
prvInitialiseTaskLists();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else /* 如果之前已经初始化过任务列表了 */
{
if( xSchedulerRunning == pdFALSE )
{
/* 如果调度器还未开启 */
if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
{
/* 新任务的任务优先级大过之前创建任务的优先级,则修改一下 */
pxCurrentTCB = pxNewTCB;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* 总任务数量递增 */
uxTaskNumber++;
/* 启动了追踪或调试 */
#if ( configUSE_TRACE_FACILITY == 1 )
{
pxNewTCB->uxTCBNumber = uxTaskNumber;
}
#endif
traceTASK_CREATE( pxNewTCB );
/* 将新创建的任务添加到就绪列表中 */
prvAddTaskToReadyList( pxNewTCB );
portSETUP_TCB( pxNewTCB );
}
taskEXIT_CRITICAL(); /* 退出临界区 */
if( xSchedulerRunning != pdFALSE )
{
/* 如果调度器已经开启 */
if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
{
/* 如果新的任务优先级高于当前任务的优先级则切换一次任务 */
taskYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
将任务添加到就绪列表实际上是一个宏定义
#define prvAddTaskToReadyList( pxTCB ) \
traceMOVED_TASK_TO_READY_STATE( pxTCB ); \
taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \
vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )
taskRECORD_READY_PRIORITY 用于修改当前已就绪的任务里面优先级最高的那个任务
#define taskRECORD_READY_PRIORITY( uxPriority ) \
{ \
if( ( uxPriority ) > uxTopReadyPriority ) \
{ \
uxTopReadyPriority = ( uxPriority ); \
} \
} /* taskRECORD_READY_PRIORITY */
vListInsertEnd 将任务插入到相应优先级就绪列表中,这个函数前面列表和列表项章节已经分析了,这里就不放解析了。上面在将任务加入到就绪列表之前进去了临界区,进去临界区(taskENTER_CRITICAL)主要是为能够互斥访问共享资源,在访问完后需要退出临界区(taskEXIT_CRITICAL),它们的实现如下:
#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 );
}
}
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
/* 定义BASEPRI这个特殊功能寄存器的值,BASEPRI即除能所有优先级不高于某个具体数值的中断*/
/* #define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 */
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
/* (msr:写通用寄存器的值到特殊功能寄存器)中断优先级号大于191的都会被关闭 */
msr basepri, ulNewBASEPRI
dsb (数据同步隔离(与流水线、 MPU 和 cache 等有关))
isb (指令同步隔离(与流水线和 MPU 等有关))
}
}
#define taskEXIT_CRITICAL() portEXIT_CRITICAL()
#define portEXIT_CRITICAL() vPortExitCritical()
void vPortExitCritical( void )
{
configASSERT( uxCriticalNesting );
/* 变量递减,用于记录临界区的嵌套次数 */
uxCriticalNesting--;
if( uxCriticalNesting == 0 )
{
portENABLE_INTERRUPTS();
}
}
#define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 )
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
__asm
{
/* (msr:写通用寄存器的值到特殊功能寄存器)设置为0即打开所有中断 */
msr basepri, ulBASEPRI
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)