FreeRTOS - 대기 열 소스 분석
30809 단어 FreeRTOS
입 대 와 출 대: 입 대 는 대열 에 보 내 고 대열 에 데 이 터 를 보 내 는 것 은 세 가지 상황 으로 나 뉜 다.첫 번 째 는 대기 열 에 데 이 터 를 보 낼 때 차단 시간 을 설정 하지 않 으 면 대기 열 이 가득 차 있 든 없 든 가입 성공 여부 에 관 계 없 이 가 차 없 이 바로 돌아 갑 니 다.두 번 째 는 대기 열 에 데 이 터 를 보 낼 때 차단 시간 을 설정 합 니 다. 대기 열 이 가득 차지 않 으 면 데 이 터 를 대기 열 에 복사 합 니 다. 대기 열 이 가득 차 면 대기 목록 에 작업 을 걸 어 차단 상태 에 들 어가 고 차단 시간 이 되면 차단 상 태 를 종료 하고 바로 아래로 돌아 갑 니 다.세 번 째 는 대기 열 에 데 이 터 를 보 낼 때 차단 시간 을 최대 값 으로 설정 합 니 다. 대기 열 이 가득 차지 않 으 면 대기 열 에 데 이 터 를 복사 합 니 다. 대기 열 이 가득 차 면 가입 이 성공 할 때 까지 계속 차단 합 니 다.
팀 에서 의 사용: 일반적으로 대기 열 을 사용 하기 전에 대기 열 을 만 든 다음 에 특정한 스 레 드 에서 대기 열 에 정 보 를 보 낼 수 있 습 니 다. 다른 스 레 드 에서 대기 열 에서 정 보 를 얻 고 간단 한 예 로 가 져 올 수 있 습 니 다.
/* */
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 는 인 터 럽 트 에서 대기 열 에서 데 이 터 를 얻 기 위 한 조작 을 하고 앞의 작업 급 입 대 와 인 터 럽 트 급 입 대 를 분석 한 결과 실제 이 조작 과정 은 앞 과 똑 같 습 니 다. 여기 서 분석 을 하지 않 고 장절 이 너무 길 지 않도록 합 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
AWS Iot EduKit L 치카 처리을 순서대로 해 보아 확실히, MQTT Test Client에서 장치에서 보낸 메시지를 표시합니다. 장치가 구독하는 주제를 콘솔의 AWS IoT MQTT 클라이언트에서 게시하여 LED 막대가 깜박임을 시작하거나 중지...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.