freertos 의 task 에 대한 분석
19958 단어 Freertos
rtos 스케줄 링 의 기본 단 위 는 task (퀘 스 트) 로 그 중요성 은 두말 할 필요 도 없다. 일반적으로 퀘 스 트 의 생 성, 삭제, 차단, 끊 기, 답장 등 을 포함한다.물론 freertos 도 예 외 는 아니다.일반적인 task 는 세 가지 기본 부분 인 TCB 구조, stack 구조, 작업 코드 를 포함한다.다음은 이 몇 가지 측면 에서 말하자면
task 관련 데이터 구조
TCB 구조 체
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; // PSP, , TCB
ListItem_t xStateListItem; //
ListItem_t xEventListItem; //
UBaseType_t uxPriority; //
StackType_t *pxStack; //
char pcTaskName[ configMAX_TASK_NAME_LEN ]; //
#if ( configUSE_TRACE_FACILITY == 1 ) //
UBaseType_t uxTCBNumber;
UBaseType_t uxTaskNumber;
#endif
#if ( configUSE_MUTEXES == 1 ) // mutex
UBaseType_t uxBasePriority;
UBaseType_t uxMutexesHeld;
#endif
#if( configUSE_TASK_NOTIFICATIONS == 1 ) //
volatile uint32_t ulNotifiedValue;
volatile uint8_t ucNotifyState;
#endif
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) // TCb
uint8_t ucStaticallyAllocated;
#endif
#if( INCLUDE_xTaskAbortDelay == 1 ) //
uint8_t ucDelayAborted;
#endif
} tskTCB;
typedef tskTCB TCB_t;
작업 과 관련 된 전역 변수
PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL; // TCB
PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ]; // , , ;
PRIVILEGED_DATA static List_t xDelayedTaskList1; // 1
PRIVILEGED_DATA static List_t xDelayedTaskList2; // 2 , tick ,
PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList; // ,
PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList; //
/*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */
PRIVILEGED_DATA static List_t xPendingReadyList; // , task , ,
#if( INCLUDE_vTaskDelete == 1 )
PRIVILEGED_DATA static List_t xTasksWaitingTermination; // TCB stack ( , ), , ram
PRIVILEGED_DATA static volatile UBaseType_t uxDeletedTasksWaitingCleanUp = ( UBaseType_t ) 0U; // 01 , , 0 。
#endif
#if ( INCLUDE_vTaskSuspend == 1 )
PRIVILEGED_DATA static List_t xSuspendedTaskList; //
#endif
PRIVILEGED_DATA static volatile UBaseType_t uxCurrentNumberOfTasks = ( UBaseType_t ) 0U; //
PRIVILEGED_DATA static volatile TickType_t xTickCount = ( TickType_t ) 0U; // tick
PRIVILEGED_DATA static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY; //
PRIVILEGED_DATA static volatile BaseType_t xSchedulerRunning = pdFALSE; //
PRIVILEGED_DATA static volatile UBaseType_t uxPendedTicks = ( UBaseType_t ) 0U; //suspend tick
PRIVILEGED_DATA static volatile BaseType_t xYieldPending = pdFALSE; //
PRIVILEGED_DATA static volatile BaseType_t xNumOfOverflows = ( BaseType_t ) 0;
PRIVILEGED_DATA static UBaseType_t uxTaskNumber = ( UBaseType_t ) 0U;
PRIVILEGED_DATA static volatile TickType_t xNextTaskUnblockTime = ( TickType_t ) 0U;
/* Initialised to portMAX_DELAY before the scheduler starts. */
PRIVILEGED_DATA static TaskHandle_t xIdleTaskHandle = NULL;
/*< Holds the handle of the idle task. The idle task is created
task 관련 코드 분석
작업 생 성
rtos 에서 자원 사용 스케줄 의 최소 단 위 는 보통 task 입 니 다. 작업 생 성 코드 를 먼저 보 세 요.두 가지 로 나 뉘 는데 하 나 는 정적 static 생 성 task (퀘 스 트 stack, tcb 는 모두 미리 정적 신청) 이 고 다른 하 나 는 동적 생 성 (퀘 스 트 stack, tcb 는 모두 생 성 할 때 신청) 입 니 다. 그 중에서 tcb 구조 에 있 는 ucStatically Allocated 요 소 는 이 tcb 를 기록 하 는 방식 으로 만 들 었 습 니 다.코드 보기
/* , tcb、stack , ,
pxNewTCB、pxStack , prvInitialiseNewTask TCB ,
prvAddNewTaskToReadyList task readylist */
TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint32_t ulStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
StackType_t * const puxStackBuffer,
StaticTask_t * const pxTaskBuffer ) {
TCB_t *pxNewTCB;
TaskHandle_t xReturn;
if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) ){
pxNewTCB = ( TCB_t * ) pxTaskBuffer; /*lint !e740 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */
pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer;
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ){
pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB;
}
#endif
prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL );
prvAddNewTaskToReadyList( pxNewTCB );
}else{
xReturn = NULL;
}
return xReturn;
}
/* , tcb、stack ram,
prvInitialiseNewTask TCB , prvAddNewTaskToReadyList task readylist */
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
{
TCB_t *pxNewTCB;
BaseType_t xReturn;
/* , stack, TCB,
stack , TCb 。。。 */
/* TCb , malloc function */
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;
}
}
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;
}
return xReturn;
}
그 중에서 모두
prvInitialiseNewTask
와 prvAddNewTaskToReadyList
를 호출 했 는데 다음은 이 두 함수 소스 코드 를 보 겠 습 니 다./* task , task , task tcb xStateListItem、xEventListItem
( , ), task */
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;
/* ; . */
#if( portSTACK_GROWTH < 0 ){
pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
/* , 4 8 */
configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
}
#endif /* portSTACK_GROWTH */
/* task */
for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ){
pxNewTCB->pcTaskName[ x ] = pcName[ x ];
/* , ram , */
if( pcName[ x ] == 0x00 ){
break;
}else{
mtCOVERAGE_TEST_MARKER();
}
}
/* 。 */
pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
/* */
if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ){
uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
}else{
mtCOVERAGE_TEST_MARKER();
}
pxNewTCB->uxPriority = uxPriority;
#if ( configUSE_MUTEXES == 1 ){// mutex ,
pxNewTCB->uxBasePriority = uxPriority;
pxNewTCB->uxMutexesHeld = 0;
}
#endif /* configUSE_MUTEXES */
/* tcb xStateListItem xEventListItem list */
vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
/* owner */
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
/* */
listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );
/* TCB , , 。
。 。
, */
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
if( ( void * ) pxCreatedTask != NULL ){
/* , ( tcb ) */
*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
}else{
mtCOVERAGE_TEST_MARKER();
}
}
/* NEW task
1. , ( pxCurrentTCB NEW tcb), prvInitialiseTaskLists
2. , , pxCurrentTCB pxNewTCB , pxNewTCB , TCB
3. , , task pxCurrentTCB pxNewTCB , pxNewTCB , , case1 \ 2 , pxCurrentTCB 。
case , prvAddTaskToReadyList task readylist */
static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
/* 。 */
taskENTER_CRITICAL();{
uxCurrentNumberOfTasks++;
if( pxCurrentTCB == NULL ){
/* task, task 、 , NEW */
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++;
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();
}
}
prvAddNewTaskToReadyList
에서 prvAddTaskToReadyList
이 매크로 와 관련 되 었 습 니 다. 다음 과 같이 준 비 된 최고 우선 순위 작업 을 업데이트 한 다음 에 이 task 를 우선 순위 목록 에 대응 하 는 readylist 목록 끝 에 삽입 합 니 다. 주로 시간 편 순환 스케줄 링 을 실현 합 니 다.#define prvAddTaskToReadyList( pxTCB ) \
taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \
vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
작업 삭제
작업 생 성 이 있 으 면 작업 이 삭 제 됩 니 다. 동적 작업 은 삭제 할 수 있 을 뿐만 아니 라 정적 작업 도 삭제 할 수 있 습 니 다. 동적 작업 은 더욱 철저하게 삭제 되 고 TCB, stack 도 삭 제 됩 니 다. 정적 작업 은 삭제 할 때 TCB, stack 이 정적 으로 분배 되 어 삭제 할 수 없습니다.소스 코드
/* , ,
1. , TCB、stack
2. ,prvDeleteTCB tcb、stack
, , 。*/
void vTaskDelete( TaskHandle_t xTaskToDelete )
{
TCB_t *pxTCB;
taskENTER_CRITICAL();{
/* xTaskToDelete NULL, ,pxTCB = pxCurrentTCB,
pxTCB = xTaskToDelete*/
pxTCB = prvGetTCBFromHandle( xTaskToDelete );
/* , ,
, 0 , */
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
/* , ( ) */
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
/* uxTaskNumber, 。
portPRE_TASK_DELETE_HOOK() Windows
。*/
uxTaskNumber++;// 。。。。。
if( pxTCB == pxCurrentTCB ){//
/* 。 , 。
xTasksWaitingTermination 。
TCB 。*/
vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );
/* uxDeletedTasksWaitingCleanUp , ,
xTasksWaitingTermination 。 */
++uxDeletedTasksWaitingCleanUp;
/* Windows , Windows ,
, ,xYieldPending 。*/
portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
}else{//
--uxCurrentNumberOfTasks; // ,
prvDeleteTCB( pxTCB ); // TCB、Stack ,
/* , task。*/
prvResetNextTaskUnblockTime();
}
}
taskEXIT_CRITICAL();
/* Force a reschedule if it is the currently running task that has just been deleted. */
if( xSchedulerRunning != pdFALSE ){//
if( pxTCB == pxCurrentTCB )
portYIELD_WITHIN_API(); // , pxCurrentTCB
}
}
그 중에서
prvDeleteTCB
와 prvResetNextTaskUnblockTime
를 호출 하여 소스 코드 를 보 았 다./* tcb、stack 。 ram , 。。。*/
static void prvDeleteTCB( TCB_t *pxTCB ){
#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) ){
/* The task can only have been allocated dynamically - free both
the stack and TCB. */
vPortFree( pxTCB->pxStack );
vPortFree( pxTCB );
}
#elif( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE == 1 ){
/* The task could have been allocated statically or dynamically, so
check what was statically allocated before trying to free the
memory. */
if( pxTCB->ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ){
/* Both the stack and TCB were allocated dynamically, so both
must be freed. */
vPortFree( pxTCB->pxStack );
vPortFree( pxTCB );
}else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY ){
/* Only the stack was statically allocated, so the TCB is theonly memory that must be freed. */
vPortFree( pxTCB );
}else{
/* Neither the stack nor the TCB were allocated dynamically, so
nothing needs to be freed. */
configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB )
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
}
/* unblock tine, tick delaylist , */
static void prvResetNextTaskUnblockTime( void ){
TCB_t *pxTCB;
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ){
/* 。xNextTaskUnblockTime ,
if(xTickCount >= xNextTaskUnblockTime) , 。*/
xNextTaskUnblockTime = portMAX_DELAY;
}else{
/* , 。
。 */
( pxTCB ) = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( ( pxTCB )->xStateListItem ) );
}
}
작업 지연
누 드 컴퓨터 프로 그래 밍 에서 우 리 는 일반적으로 시간 을 지연 시 키 는 것 은 CPU 를 공전 시 키 는 것 이다. 그러면 자원 을 크게 낭비 하 게 된다. OS 에서 이런 상황 이 발생 하 는 것 을 기본적으로 허용 하지 않 기 때문에 OS 는 사용자 가 OS 에서 사용 할 수 있 도록 두 개의 함 수 를 제공 했다.
vTaskDelayUntil
과 vTaskDelay
.두 함수 에 경미 한 차이 가 있 으 니, 상세 하 게 분석 하면 코드 를 볼 수 있다./* delay tick , task delaylist xTicksToDelay tick*/
void vTaskDelay( const TickType_t xTicksToDelay ){
BaseType_t xAlreadyYielded = pdFALSE;
/* tick 0 , */
if( xTicksToDelay > ( TickType_t ) 0U ){
configASSERT( uxSchedulerSuspended == 0 );
vTaskSuspendAll();{
traceTASK_DELAY();
/*
, 。
, 。*/
prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
}
xAlreadyYielded = xTaskResumeAll();
}
/* xTaskResumeAll , , slepp */
if( xAlreadyYielded == pdFALSE )
portYIELD_WITHIN_API();
}
/* pxPreviousWakeTime, xTimeIncrement 。 , , , vTaskDelayUntil */
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )
{
TickType_t xTimeToWake;
BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;
vTaskSuspendAll();{//
const TickType_t xConstTickCount = xTickCount;
/* tick */
xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;
if( xConstTickCount < *pxPreviousWakeTime )
{// tick tick ,
/* , */
if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) )
{
xShouldDelay = pdTRUE;
}else{
mtCOVERAGE_TEST_MARKER();
}
}else{
/* tick 。 , 、 tick wake time ,
delay*/
if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) ){
xShouldDelay = pdTRUE;
}else{
mtCOVERAGE_TEST_MARKER();
}
}
/* , */
*pxPreviousWakeTime = xTimeToWake;
if( xShouldDelay != pdFALSE )
/* prvAddCurrentTaskToDelayedList() , , tick */
prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE );
}
xAlreadyYielded = xTaskResumeAll();//
/* xTaskResumeAll , , slepp*/
if( xAlreadyYielded == pdFALSE )
portYIELD_WITHIN_API();
}
태 스 크 걸 기
이 부분 코드 는 주로 작업 연결, 작업 해제 등 을 포함한다.
void vTaskSuspend( TaskHandle_t xTaskToSuspend )
{
TCB_t *pxTCB;
taskENTER_CRITICAL();{
/* , */
pxTCB = prvGetTCBFromHandle( xTaskToSuspend );
/* ready delayed list , xSuspendedTaskList */
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ){
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
}
/* , , */
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ){
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
}
/* task xSuspendedTaskList 。( )*/
vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
}
taskEXIT_CRITICAL();
if( xSchedulerRunning != pdFALSE ){
/* xNextTaskUnblockTime , xNextTaskUnblockTime ( )*/
taskENTER_CRITICAL();{
prvResetNextTaskUnblockTime();
}
taskEXIT_CRITICAL();
}
if( pxTCB == pxCurrentTCB ){//
if( xSchedulerRunning != pdFALSE )
{// ,
/* The current task has just been suspended. */
configASSERT( uxSchedulerSuspended == 0 );
portYIELD_WITHIN_API();
}else{// 。
/* The scheduler is not running, but the task that was pointed
to by pxCurrentTCB has just been suspended and pxCurrentTCB
must be adjusted to point to a different task. */
if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks )
{// , ,
//pxCurrentTCB NULL。
pxCurrentTCB = NULL;
}else{// 。。
vTaskSwitchContext();
}
}
}
}
void vTaskResume( TaskHandle_t xTaskToResume )
{
TCB_t * const pxTCB = ( TCB_t * ) xTaskToResume;
/* NULL,pxCurrentTCB */
if( ( pxTCB != NULL ) && ( pxTCB != pxCurrentTCB ) ){
taskENTER_CRITICAL();{
if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE )
{// pxTCB
/* , , xSuspendedTaskList
task , readylist */
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
prvAddTaskToReadyList( pxTCB );
/* pxTCB pxCurrentTCB , */
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ){
taskYIELD_IF_USING_PREEMPTION();
}
}
}
taskEXIT_CRITICAL();
}
}