FreeRTOS - 대기 열 소스 분석

30809 단어 FreeRTOS
FreeRTOS 의 대기 열 은 작업 과 작업, 작업 과 중단 사이 의 통신 에 사용 되 는 데이터 구조 입 니 다.각 작업 간 의 데이터 통신 은 공 통 된 저장 공간 을 통 해 필요 에 따라 데 이 터 를 가 져 오고 보 냅 니 다. 이 저장 공간 은 대기 열 형식 으로 접근 하여 동적 또는 정적 방식 으로 이 메모 리 를 만 들 수 있 습 니 다.작업 이 대기 열 에 데 이 터 를 보 내 고 데 이 터 를 받 는 것 은 일반적으로 선진 적 인 선 출 (FIFO) 저장 버퍼 체 제 를 사용 합 니 다. 물론 후진 선 출 (LIFO) 형식 도 사용 할 수 있 습 니 다.FreeRTOS 가 대기 열 에 데 이 터 를 보 내 고 받 는 것 은 직접 복사 하 는 형식 으로 보 내 고 받 을 데 이 터 를 memcpy 라 는 함 수 를 사용 하여 해당 메모리 공간 에 복사 하 는 것 입 니 다. 이렇게 하면 좋 은 점 은 변 수 를 전달 하면 이 변 수 를 대기 열 에 넣 으 면 변 량 으로 다른 코드 에 다시 할당 할 수 있 습 니 다.데이터 가 대기 열 에 저장 되 어 있 기 때문에 값 전달 이 라 고 합 니 다.물론 고정 저장 공간의 메모리 포인터 도 대기 열 에 저장 할 수 있 지만 이 공간 이 수정 되 지 않도록 해 야 합 니 다. 그렇지 않 으 면 수신 단 에서 받 은 포인터 에 따라 그 메모리 에 접근 하면 원 하 는 데이터 가 아 닐 수 있 습 니 다.
입 대 와 출 대: 입 대 는 대열 에 보 내 고 대열 에 데 이 터 를 보 내 는 것 은 세 가지 상황 으로 나 뉜 다.첫 번 째 는 대기 열 에 데 이 터 를 보 낼 때 차단 시간 을 설정 하지 않 으 면 대기 열 이 가득 차 있 든 없 든 가입 성공 여부 에 관 계 없 이 가 차 없 이 바로 돌아 갑 니 다.두 번 째 는 대기 열 에 데 이 터 를 보 낼 때 차단 시간 을 설정 합 니 다. 대기 열 이 가득 차지 않 으 면 데 이 터 를 대기 열 에 복사 합 니 다. 대기 열 이 가득 차 면 대기 목록 에 작업 을 걸 어 차단 상태 에 들 어가 고 차단 시간 이 되면 차단 상 태 를 종료 하고 바로 아래로 돌아 갑 니 다.세 번 째 는 대기 열 에 데 이 터 를 보 낼 때 차단 시간 을 최대 값 으로 설정 합 니 다. 대기 열 이 가득 차지 않 으 면 대기 열 에 데 이 터 를 복사 합 니 다. 대기 열 이 가득 차 면 가입 이 성공 할 때 까지 계속 차단 합 니 다.
팀 에서 의 사용: 일반적으로 대기 열 을 사용 하기 전에 대기 열 을 만 든 다음 에 특정한 스 레 드 에서 대기 열 에 정 보 를 보 낼 수 있 습 니 다. 다른 스 레 드 에서 대기 열 에서 정 보 를 얻 고 간단 한 예 로 가 져 올 수 있 습 니 다.
/*        */
QueueHandle_t Led_Queue;

/*     LED        */
void start_task(void *pvParameters)
{
	/*       */
    taskENTER_CRITICAL();           

	/*     Led_Queue */
	Led_Queue = xQueueCreate(1, sizeof(unsigned char));
	
    /*   LED0   */
    xTaskCreate((TaskFunction_t )led0_task,     	
                (const char*    )"led0_task",   	
                (uint16_t       )LED0_STK_SIZE, 
                (void*          )NULL,				
                (UBaseType_t    )LED0_TASK_PRIO,	
                (TaskHandle_t*  )&LED0Task_Handler);   
    /*   LED1   */
    xTaskCreate((TaskFunction_t )led1_task,     
                (const char*    )"led1_task",   
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED1_TASK_PRIO,
                (TaskHandle_t*  )&LED1Task_Handler);         
    /*        */
	vTaskDelete(StartTask_Handler);
	/*       */
    taskEXIT_CRITICAL();            
}

/* LED0      */
void led0_task(void *pvParameters)
{
	unsigned char led_message = 0;
	
    while(1)
    {
        LED0=~LED0;
		led_message =! led_message;
		/*          */
		xQueueSend(Led_Queue, &led_message, 10);
		vTaskDelay(500);
    }
}   

/* LED1     */
void led1_task(void *pvParameters)
{
	unsigned char led_message = 0;
	
    while(1)
    {
		/*          */
		xQueueReceive(Led_Queue, &led_message, 10);
		if(led_message == 1){
			LED1 = 0;
		}
		else{
			LED1 = 1;
		}
		vTaskDelay(10);
    }
}


위의 응용 프로그램 에서 알 수 있 듯 이 첫 번 째 로 사용 한 함 수 는 대기 열 생 성 입 니 다. 대기 열 생 성 후 대기 열 지침 을 되 돌려 줍 니 다. 이 대기 열 지침 은 구조 체 지침 변수 (QueueDefinition *) 입 니 다.
#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
{...}
typedef struct QueueDefinition * QueueHandle_t;

구조 체 변수 (QueueDefinition) 는 대기 열의 설명 정 보 를 저장 하고 있 으 며, 각 대기 열 은 하나의 구조 체 변수 에 대응 하 며, 이 구조 체 를 통 해 대기 열의 현재 상태, 정보 및 공간 등 을 얻 을 수 있 습 니 다.구조 체 의 정의 와 의 미 는 다음 과 같다.
typedef struct QueueDefinition /* The old naming convention is used to prevent breaking kernel aware debuggers. */
{
	int8_t *pcHead;		/*             */			
	int8_t *pcWriteTo;	/*               */		

	union
	{
		QueuePointers_t xQueue;		/*                               */	
		SemaphoreData_t xSemaphore; /*                   */
	} u;

	List_t xTasksWaitingToSend;		/*         ,                      */	
	List_t xTasksWaitingToReceive;	/*         ,                      */ 

	volatile UBaseType_t uxMessagesWaiting;	 /*            */
	UBaseType_t uxLength;		/*          ,      */		
	UBaseType_t uxItemSize;		/*             */	
	volatile int8_t cRxLock;	/*                      */		
	volatile int8_t cTxLock;	/*                      */	

	/*                   pdTURE,            */
	#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
		uint8_t ucStaticallyAllocated;
	#endif

	/*        */
	#if ( configUSE_QUEUE_SETS == 1 )
		struct QueueDefinition *pxQueueSetContainer;
	#endif

	/*         */
	#if ( configUSE_TRACE_FACILITY == 1 )
		UBaseType_t uxQueueNumber;
		uint8_t ucQueueType;
	#endif

} xQUEUE;

대기 열 생 성 (xQueueCreate) 은 실제 매크로 입 니 다. 마지막 으로 xQueueGenericCreate 를 호출 합 니 다.
	#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )

생 성 된 대기 열 형식 인 "queueQUEUE TYPE BASE" 를 지정 하 는 매개 변수 가 있 습 니 다. 실제 대기 열 생 성 은 여러 가지 유형 을 지원 합 니 다.
#define queueQUEUE_TYPE_BASE				( ( uint8_t ) 0U )	/*        */
#define queueQUEUE_TYPE_SET					( ( uint8_t ) 0U )	/*     */
#define queueQUEUE_TYPE_MUTEX 				( ( uint8_t ) 1U )	/*       */
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE	( ( uint8_t ) 2U )	/*        */
#define queueQUEUE_TYPE_BINARY_SEMAPHORE	( ( uint8_t ) 3U )	/*       */
#define queueQUEUE_TYPE_RECURSIVE_MUTEX		( ( uint8_t ) 4U )	/*         */

작업 생 성 함수 의 소스 코드 분석 은 다음 과 같 습 니 다.
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
	{
	Queue_t *pxNewQueue;
	size_t xQueueSizeInBytes;
	uint8_t *pucQueueStorage;

		/*          0(      0) */
		configASSERT( uxQueueLength > ( UBaseType_t ) 0 );

		if( uxItemSize == ( UBaseType_t ) 0 )
		{
			/*         0,        */
			xQueueSizeInBytes = ( size_t ) 0;
		}
		else
		{
			/*        =       *        */
			xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize );
		}


		/*             (       ),Queue_t              */
		pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );

		if( pxNewQueue != NULL )
		{
			/*       ,           */
			pucQueueStorage = ( uint8_t * ) pxNewQueue;
			/*          (        ) */
			pucQueueStorage += sizeof( Queue_t ); 

			#if( configSUPPORT_STATIC_ALLOCATION == 1 )
			{
				/*                       ,           ,             */
				pxNewQueue->ucStaticallyAllocated = pdFALSE;
			}
			#endif

			/*                          ,        ,         ,        ,          */
			prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
		} 
		else
		{
			/*        */
			traceQUEUE_CREATE_FAILED( ucQueueType );
			mtCOVERAGE_TEST_MARKER();
		}

		/*        */
		return pxNewQueue;
	}

#endif

메모리 가 분 배 된 후에 대기 열 을 초기 화하 고 대기 열의 원본 분석 을 초기 화 합 니 다.
static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue )
{
	/*         */
	( void ) ucQueueType;

	if( uxItemSize == ( UBaseType_t ) 0 )
	{
		/*             ,  pcHead     NULL, pcHead         */
		pxNewQueue->pcHead = ( int8_t * ) pxNewQueue;
	}
	else
	{
		/*         ,  pcHead        */
		pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage;
	}
	
	/*          */
	pxNewQueue->uxLength = uxQueueLength;
	pxNewQueue->uxItemSize = uxItemSize;
	/*           */
	( void ) xQueueGenericReset( pxNewQueue, pdTRUE );

	/*             */
	#if ( configUSE_TRACE_FACILITY == 1 )
	{
		pxNewQueue->ucQueueType = ucQueueType;
	}
	#endif 

	/*            */
	#if( configUSE_QUEUE_SETS == 1 )
	{
		pxNewQueue->pxQueueSetContainer = NULL;
	}
	#endif 

	traceQUEUE_CREATE( pxNewQueue );
}

그 중에서 인터페이스 xQueueGenericReset 을 호출 하여 대기 열 다른 정 보 를 초기 화 하 는 소스 코드 분석 은 다음 과 같다.
BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue )
{
Queue_t * const pxQueue = xQueue;

	configASSERT( pxQueue );

	/*       */
	taskENTER_CRITICAL();
	{
		/*          ,                  */
		pxQueue->u.xQueue.pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize ); 
		pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U;
		pxQueue->pcWriteTo = pxQueue->pcHead;
		pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - 1U ) * pxQueue->uxItemSize );
		pxQueue->cRxLock = queueUNLOCKED;
		pxQueue->cTxLock = queueUNLOCKED;

		/*                       */
		if( xNewQueue == pdFALSE )
		{
			/*                      ,        ,    
			             (        )。               
			       ,        ,             ,      */
			   
			/*                */
			if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
			{
				/*                 */
				if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
				{
					/*            */
					queueYIELD_IF_USING_PREEMPTION();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		/*                 */
		else
		{
			/*                */
			vListInitialise( &( pxQueue->xTasksWaitingToSend ) );
			vListInitialise( &( pxQueue->xTasksWaitingToReceive ) );
		}
	}
	/*       */
	taskEXIT_CRITICAL();

	/*      */
	return pdPASS;
}

위의 이 함 수 는 동적 메모리 로 작업 을 만 듭 니 다. 물론 정적 분배 방식 도 있 습 니 다. 사용자 가 스스로 메모리 공간 을 분배 하고 메모리 의 지침 을 전달 해 야 합 니 다. 그의 정 의 는 다음 과 같 습 니 다.
xQueueCreateStatic(uxQueueLength, uxItemSize, pucQueueStorage, pxQueueBuffer)
#define xQueueCreateStatic( uxQueueLength, uxItemSize, pucQueueStorage, pxQueueBuffer ) xQueueGenericCreateStatic( ( uxQueueLength ), ( uxItemSize ), ( pucQueueStorage ), ( pxQueueBuffer ), ( queueQUEUE_TYPE_BASE ) )

이 함수 의 실현 은 위의 xQueue Generic 과 거의 같 습 니 다. 일부 대기 열 매개 변수 설정 에 차이 가 있 는 것 을 제외 하고 이 코드 가 관심 이 있 으 면 직접 보 세 요.대기 열 을 만 든 후 대기 열 을 조작 하여 대기 열 에 데 이 터 를 보 내 고 대기 열 에서 데 이 터 를 가 져 올 수 있 습 니 다.먼저 대기 열 에 데 이 터 를 보 내 는 소스 코드 를 분석 하고 대기 열 에 데 이 터 를 보 내 는 것 은 FreeRTOS 에 여러 개의 인터페이스 함수 가 있 습 니 다. 하 나 는 작업 간 통신 이 고 하 나 는 서비스 중단 함수 입 니 다. 이 는 인터페이스 함수 가 8 개 입 니 다. 다음 과 같 습 니 다.
	xQueueSend(xQueue, pvItemToQueue, xTicksToWait);       /*     ,          */
	xQueueSendToBack(xQueue, pvItemToQueue, xTicksToWait); /*           */
	xQueueSendToFront(xQueue, pvItemToQueue, xTicksToWait);/*           */
	xQueueOverwrite(xQueue, pvItemToQueue);  /*               ,             */

	xQueueSendFromISR(xQueue, pvItemToQueue, pxHigherPriorityTaskWoken);       /*                   */
	xQueueSendToBackFromISR(xQueue, pvItemToQueue, pxHigherPriorityTaskWoken); /*                   */
	xQueueSendToFrontFromISR(xQueue, pvItemToQueue, pxHigherPriorityTaskWoken);/*                   */
	xQueueOverwriteFromISR(xQueue, pvItemToQueue, pxHigherPriorityTaskWoken);  /*                    */

앞의 네 개의 퀘 스 트 급 입 대 함 수 는 모두 호출 된 같은 인터페이스 로 대기 열 에 데 이 터 를 기록 합 니 다. 다만 형 삼 에 서 는 어떤 방식 으로 기록 하 는 지 알려 줍 니 다.
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT )
#define xQueueOverwrite( xQueue, pvItemToQueue ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE )

여기 서 먼저 입대 함수 의 소스 코드 를 분석 합 니 다.
BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
{
BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = xQueue;

	configASSERT( pxQueue );
	configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
	configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );
	#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
	{
		configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
	}
	#endif

	for( ;; )
	{
		/*       */
		taskENTER_CRITICAL();
		{
			
			/*                      */
			if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
			{
				traceQUEUE_SEND( pxQueue );

				#if ( configUSE_QUEUE_SETS == 1 )
				{
					/*             */
				}
				#else
				{
					/*          */
					xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );

					/*            ,                 */
					if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
					{
						/*               ,              */
						if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
						{
							/*                 ,         */
							queueYIELD_IF_USING_PREEMPTION();
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					else if( xYieldRequired != pdFALSE )
					{
						queueYIELD_IF_USING_PREEMPTION();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				#endif
				
				/*       */
				taskEXIT_CRITICAL();
				/*   pdPASS,       */
				return pdPASS;
			}
			else /*            */
			{
				if( xTicksToWait == ( TickType_t ) 0 )
				{
					/*     ,         0             */
					taskEXIT_CRITICAL();

					traceQUEUE_SEND_FAILED( pxQueue );
					return errQUEUE_FULL;
				}
				/*            */
				else if( xEntryTimeSet == pdFALSE )
				{
					/*     ,                API          */
					vTaskInternalSetTimeOutState( &xTimeOut );
					/*           */
					xEntryTimeSet = pdTRUE;
				}
				/*                   */
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
		/*       */
		taskEXIT_CRITICAL();

		/*      ,           ,       0 */
		vTaskSuspendAll();
		/*      */
		prvLockQueue( pxQueue );

		/*                    */
		if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
		{
			/*                */
			if( prvIsQueueFull( pxQueue ) != pdFALSE )
			{
				traceBLOCKING_ON_QUEUE_SEND( pxQueue );
				/*           ,             */
				vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );

				/*      */
				prvUnlockQueue( pxQueue );

				/*       */
				if( xTaskResumeAll() == pdFALSE )
				{
					/*                ,          */
					portYIELD_WITHIN_API();
				}
			}
			else/*     ,              */
			{
				/*            */
				prvUnlockQueue( pxQueue );
				( void ) xTaskResumeAll();
			}
		}
		/*             ,       */
		else
		{
			/*      */
			prvUnlockQueue( pxQueue );
			/*       */
			( void ) xTaskResumeAll();
			/*        */
			traceQUEUE_SEND_FAILED( pxQueue );
			return errQUEUE_FULL;
		}
	}
}

인 터 럽 트 안에서 대기 열 에 데 이 터 를 쓰 는 것 은 모두 호출 된 인 터 럽 트 급 입 대 함수 입 니 다. 다음 과 같 습 니 다.
#define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
#define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
#define xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT )
#define xQueueOverwriteFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueOVERWRITE )

xQueue GenericSend FromISR 인 터 럽 트 급 유 니 버 설 입 대 함수 와 앞의 작업 이 대체적으로 일치 합 니 다. 임계 구역 에 들 어가 거나 대기 열 에 잠 금 을 잠 그 거나 잠 금 을 푸 는 작업 이 약간 다 릅 니 다. 소스 코드 분석 은 다음 과 같 습 니 다.
BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition )
{
BaseType_t xReturn;
UBaseType_t uxSavedInterruptStatus;
Queue_t * const pxQueue = xQueue;

	configASSERT( pxQueue );
	configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
	configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );

	portASSERT_IF_INTERRUPT_PRIORITY_INVALID();

	/*         */
	uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
	{
		/*                */
		if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
		{
			/*     cTxLock,           */
			const int8_t cTxLock = pxQueue->cTxLock;

			traceQUEUE_SEND_FROM_ISR( pxQueue );

			/*          */
			( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );

			/*                  */
			if( cTxLock == queueUNLOCKED )
			{
				#if ( configUSE_QUEUE_SETS == 1 )
				{
					/*             */
				}
				#else
				{
					/*            ,                */
					if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
					{
						/*               */
						if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
						{
							/*                 */
							if( pxHigherPriorityTaskWoken != NULL )
							{
								/*                   */
								*pxHigherPriorityTaskWoken = pdTRUE;
							}
							else
							{
								mtCOVERAGE_TEST_MARKER();
							}
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				#endif 
			}
			/*             */
			else
			{
				/*           cTxLock  ,           ,                */
				pxQueue->cTxLock = ( int8_t ) ( cTxLock + 1 );
			}
			
			xReturn = pdPASS;
		}
		else
		{
			/*       errQUEUE_FULL */
			traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue );
			xReturn = errQUEUE_FULL;
		}
	}
	/*         */
	portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );

	return xReturn;
}

다음은 입 대 함수 에 나타 난 일부 인터페이스 가 어떻게 실현 되 는 지 차례대로 분석 하고 나타 난 인터페이스 함 수 는 다음 과 같다.
/*           */
static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition );

/*               */
BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList );

/*      */
prvLockQueue( pxQueue );

/*      */
static void prvUnlockQueue( Queue_t * const pxQueue );

/*            */
BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait );

/*                */
void vTaskPlaceOnEventList( List_t * const pxEventList, const TickType_t xTicksToWait );

그리고 몇 개의 인 터 페 이 스 는 간단 한 할당 작업 일 뿐 분석 을 놓 지 않 습 니 다. 먼저 데 이 터 를 대기 열 에 복사 하 는 것 이 어떻게 실현 되 는 지 보 겠 습 니 다.
static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition )
{
BaseType_t xReturn = pdFALSE;
UBaseType_t uxMessagesWaiting;

	/*          */
	uxMessagesWaiting = pxQueue->uxMessagesWaiting;

	if( pxQueue->uxItemSize == ( UBaseType_t ) 0 )
	{
		/*      (              ) */
		#if ( configUSE_MUTEXES == 1 )
		{
			if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
			{
				xReturn = xTaskPriorityDisinherit( pxQueue->u.xSemaphore.xMutexHolder );
				pxQueue->u.xSemaphore.xMutexHolder = NULL;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif
	}
	/*      */
	else if( xPosition == queueSEND_TO_BACK )
	{
		/*                     */
		( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); 
		/*                */
		pxQueue->pcWriteTo += pxQueue->uxItemSize; 
		if( pxQueue->pcWriteTo >= pxQueue->u.xQueue.pcTail ) 
		{
			/*          */
			pxQueue->pcWriteTo = pxQueue->pcHead;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	/*           */
	else
	{
		/*                     */
		( void ) memcpy( ( void * ) pxQueue->u.xQueue.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize );
		pxQueue->u.xQueue.pcReadFrom -= pxQueue->uxItemSize;
		if( pxQueue->u.xQueue.pcReadFrom < pxQueue->pcHead ) 
		{
			/*          */
			pxQueue->u.xQueue.pcReadFrom = ( pxQueue->u.xQueue.pcTail - pxQueue->uxItemSize );
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		if( xPosition == queueOVERWRITE )
		{
			if( uxMessagesWaiting > ( UBaseType_t ) 0 )
			{
				/*        ,         ,         ,           */
				--uxMessagesWaiting;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}

	/*         1 */
	pxQueue->uxMessagesWaiting = uxMessagesWaiting + ( UBaseType_t ) 1;

	return xReturn;
}

대기 열 에 데 이 터 를 보 낸 후 대기 열 데 이 터 를 읽 기 위해 차단 되 는 작업 이 있 는 지 확인 할 수 있 습 니 다. 있 으 면 대기 목록 에서 작업 을 삭제 해 야 합 니 다. xTask Remove FromEventList 라 는 인 터 페 이 스 를 사용 하면 원본 분석 은 다음 과 같 습 니 다.
BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList )
{
TCB_t *pxUnblockedTCB;
BaseType_t xReturn;

	/*                     */
	pxUnblockedTCB = listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); 
	
	configASSERT( pxUnblockedTCB );
	/*               */
	( void ) uxListRemove( &( pxUnblockedTCB->xEventListItem ) );

	/*             */
	if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
	{
		/*             */
		( void ) uxListRemove( &( pxUnblockedTCB->xStateListItem ) );
		/*            */
		prvAddTaskToReadyList( pxUnblockedTCB );
	}
	else/*        */
	{
		/*             ,               */
		vListInsertEnd( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) );
	}

	/*                                        */
	if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority )
	{
		xReturn = pdTRUE;
		xYieldPending = pdTRUE;
	}
	else
	{
		/*           ,                 */
		xReturn = pdFALSE;
	}

	#if( configUSE_TICKLESS_IDLE != 0 )
	{
		/*                      */
		prvResetNextTaskUnblockTime();
	}
	#endif

	return xReturn;
}

입 대 함 수 는 업데이트 시간 이 초과 되 기 전과 그 후에 각각 대기 열 잠 금 해제 함 수 를 사 용 했 습 니 다. 대기 열 잠 금 원본 코드 는 다음 과 같 습 니 다.
#define prvLockQueue( pxQueue )								\
	taskENTER_CRITICAL();									\
	{														\
		if( ( pxQueue )->cRxLock == queueUNLOCKED )			\
		{													\
			( pxQueue )->cRxLock = queueLOCKED_UNMODIFIED;	\
		}													\
		if( ( pxQueue )->cTxLock == queueUNLOCKED )			\
		{													\
			( pxQueue )->cTxLock = queueLOCKED_UNMODIFIED;	\
		}													\
	}														\
	taskEXIT_CRITICAL()

대기 열 잠 금 해 제 는 간단 합 니 다. 대기 열 구조 체 변수의 cRxLock 과 cTxLock 을 queueLOCKED 로 설정 하 는 것 입 니 다.UNMODIFIED, 다음 대기 열 잠 금 해제 의 소스 코드 분석:
static void prvUnlockQueue( Queue_t * const pxQueue )
{
	/*       */
	taskENTER_CRITICAL();
	{
		/*                    */
		int8_t cTxLock = pxQueue->cTxLock;

		/*                (                  ,cTxLock    ) */
		while( cTxLock > queueLOCKED_UNMODIFIED )
		{
			#if ( configUSE_QUEUE_SETS == 1 )
			{
				/*         */
			}
			#else 
			{
				/*               */
				if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
				{
					/*               */
					if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
					{
						/*              */
						vTaskMissedYield();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				/*               */
				else
				{
					break;
				}
			}
			#endif
			/*            ,              */
			--cTxLock;
		}
		/*         */
		pxQueue->cTxLock = queueUNLOCKED;
	}
	taskEXIT_CRITICAL();

	/*                     ,             ,           */
	taskENTER_CRITICAL();
	{
		int8_t cRxLock = pxQueue->cRxLock;

		while( cRxLock > queueLOCKED_UNMODIFIED )
		{
			if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
			{
				if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
				{
					vTaskMissedYield();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}

				--cRxLock;
			}
			else
			{
				break;
			}
		}
		pxQueue->cRxLock = queueUNLOCKED;
	}
	taskEXIT_CRITICAL();
}

입 대 함수 가 시간 초과 시간 을 설정 하면 대기 열 이 가득 차고 시간 이 되 지 않 으 면 차단 상태 에 들 어 갑 니 다. 시간 초과 시간 을 어떻게 판단 하 는 지 는 시간 초과 검사 함수 xTask CheckForTimeOut 을 분석 해 야 합 니 다. 소스 코드 분석 은 다음 과 같 습 니 다.
BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait )
{
BaseType_t xReturn;

	configASSERT( pxTimeOut );
	configASSERT( pxTicksToWait );

	/*       */
	taskENTER_CRITICAL();
	{
		/*           */
		const TickType_t xConstTickCount = xTickCount;
		/*        -           =         */
		const TickType_t xElapsedTime = xConstTickCount - pxTimeOut->xTimeOnEntering;

		#if ( INCLUDE_vTaskSuspend == 1 )
			if( *pxTicksToWait == portMAX_DELAY )
			{
				/*              pdFALSE,           */
				xReturn = pdFALSE;
			}
			else
		#endif

		if( ( xNumOfOverflows != pxTimeOut->xOverflowCount ) && ( xConstTickCount >= pxTimeOut->xTimeOnEntering ) ) 
		{
			/*        */
			xReturn = pdTRUE;
		}
		/*         */
		else if( xElapsedTime < *pxTicksToWait ) 
		{
			/*          */
			*pxTicksToWait -= xElapsedTime;
			/*           */
			vTaskInternalSetTimeOutState( pxTimeOut );
			xReturn = pdFALSE;
		}
		/*        */
		else
		{
			/*       0 */
			*pxTicksToWait = 0;
			/*        */
			xReturn = pdTRUE;
		}
	}
	/*       */
	taskEXIT_CRITICAL();
	/*      */
	return xReturn;
}

시간 초과 가 되 지 않 으 면 해당 이벤트 목록 과 지연 목록 에 작업 을 삽입 합 니 다. 함수 vTask Place OnEventList 를 사용 하여 이 루어 집 니 다. 소스 코드 분석 은 다음 과 같 습 니 다.
void vTaskPlaceOnEventList( List_t * const pxEventList, const TickType_t xTicksToWait )
{
	configASSERT( pxEventList );

	/*               ,             */
	vListInsert( pxEventList, &( pxCurrentTCB->xEventListItem ) );

	/*            ,               */
	prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
}

입 대 함수 분석 이 끝 났 습 니 다. 다음 에 출 대 함 수 를 분석 해 보 겠 습 니 다. 출 대 함수 인 터 페 이 스 는 4 개 입 니 다.
/*         ,             */
xQueueReceive(QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait);
/*         ,              */
xQueuePeek(QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait);

/*           ,         ,             */
xQueueReceiveFromISR(QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken);
/*           ,         ,              */
xQueuePeekFromISR(QueueHandle_t xQueue, void * const pvBuffer);

실제 출 대 함수 분석 이 끝 난 후 입 대 함수 가 하 는 조작 은 기본적으로 출 대 와 대동소이 하 다. xQueueReceive 입 대 함수 의 소스 코드 분석 은 다음 과 같다.
BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
{
BaseType_t xEntryTimeSet = pdFALSE;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = xQueue;

	configASSERT( ( pxQueue ) );
	configASSERT( !( ( ( pvBuffer ) == NULL ) && ( ( pxQueue )->uxItemSize != ( UBaseType_t ) 0U ) ) );
	#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
	{
		configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
	}
	#endif

	for( ;; )
	{
		/*       */
		taskENTER_CRITICAL();
		{
			/*         */
			const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;

			/*          */
			if( uxMessagesWaiting > ( UBaseType_t ) 0 )
			{
				/*             */
				prvCopyDataFromQueue( pxQueue, pvBuffer );
				traceQUEUE_RECEIVE( pxQueue );
				/*        */
				pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1;

				/*             */
				if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
				{
					/*          ,             (        ,           ,            ) */
					if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
					{
						/*                  */
						queueYIELD_IF_USING_PREEMPTION();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
				/*       */
				taskEXIT_CRITICAL();
				return pdPASS;
			}
			else/*        */
			{
				/*                */
				if( xTicksToWait == ( TickType_t ) 0 )
				{
					/*       */
					taskEXIT_CRITICAL();
					traceQUEUE_RECEIVE_FAILED( pxQueue );
					/*        */
					return errQUEUE_EMPTY;
				}
				/*             */
				else if( xEntryTimeSet == pdFALSE )
				{
					/*      ,                API          */
					vTaskInternalSetTimeOutState( &xTimeOut );
					xEntryTimeSet = pdTRUE;
				}
				/*                   */
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
		/*       */
		taskEXIT_CRITICAL();

		/*       */
		vTaskSuspendAll();
		/*      */
		prvLockQueue( pxQueue );

		/*       ,        */
		if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
		{
			/*      */
			if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
			{
				traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );
				/*            ,          */
				vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
				/*      */
				prvUnlockQueue( pxQueue );
				/*       */
				if( xTaskResumeAll() == pdFALSE )
				{
					/*          */
					portYIELD_WITHIN_API();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				/*                  ,         */
				prvUnlockQueue( pxQueue );
				( void ) xTaskResumeAll();
			}
		}
		/*       , systick                               */ */
		else
		{
			/*     ,                        ,
			         ,       ,                 */
			prvUnlockQueue( pxQueue );
			/*       */
			( void ) xTaskResumeAll();

			/*      */
			if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
			{
				/*                   */
				traceQUEUE_RECEIVE_FAILED( pxQueue );
				return errQUEUE_EMPTY;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
	} 
}

또 하나의 퀘 스 트 급 출 대 함수 xQueuePeek 도 있 습 니 다. 이 함 수 는 xQueueReceive 와 거의 일치 합 니 다. 출 대 후 대기 열 데 이 터 를 삭제 하지 않 고 간단 한 태그 작업 을 했 습 니 다.그리고 인 터 럽 트 급 출 대 함수 xQueueReceiveFromISR 와 xQueuePeekFromISR 는 인 터 럽 트 에서 대기 열 에서 데 이 터 를 얻 기 위 한 조작 을 하고 앞의 작업 급 입 대 와 인 터 럽 트 급 입 대 를 분석 한 결과 실제 이 조작 과정 은 앞 과 똑 같 습 니 다. 여기 서 분석 을 하지 않 고 장절 이 너무 길 지 않도록 합 니 다.

좋은 웹페이지 즐겨찾기