FreeRTOS的队列是基础,其它的,比如信号量等都是基于队列实现的。
1 #define queueQUEUE_TYPE_BASE ( 0U ) 2 #define queueQUEUE_TYPE_MUTEX ( 1U ) 3 #define queueQUEUE_TYPE_COUNTING_SEMAPHORE ( 2U ) 4 #define queueQUEUE_TYPE_BINARY_SEMAPHORE ( 3U ) 5 #define queueQUEUE_TYPE_RECURSIVE_MUTEX ( 4U )
信号量包括二值信号量、计数信号量、递归信号量、互斥信号量(Mutex: Mut + Exclusion)。
对于二值信号量,对存在优先级反转的问题。
比如任务3、2、1的优先级从高到低,任务3和1通过二值信号量控制访问某个资源,若任务1先锁定该资源,则任务3访问该资源时,会因为得不到资源而阻塞。此时,若任务2运行条件具备,任务2会打断任务1而执行,从而呈现低优先级的任务2优先于高优先级的任务3运行的情景,即优先级反转了。
由于二值信号量的这个问题,于是有了互斥信号量,互斥信号量与二值信号量的区别在于,互斥信号量具有优先级继承的特性。即在任务3获取互斥信号量的时候,若无法获取互斥信号量,则会判断一下当前获取互斥信号量的任务优先级是否比自己低,若是,则将该任务的优先级提高到和自己一样。
queue定义如下,头pcHead和尾pcTail均为指向字节量,pcWriteTo指向第一个成员地址,pcReadFrom指向最后一个成员地址,xTasksWaitingToSend等待向队列发送数据的任务列表,该任务同时也会在挂起(等待时间为无限)或延时列表(等待时间为有限)中。uxMessagesWaiting队列成员个数,虽然名字有个waiting。
1 typedef struct QueueDefinition 2 { 3 signed char *pcHead; /*< Points to the beginning of the queue storage area. */ 4 signed char *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */ 5 6 signed char *pcWriteTo; /*< Points to the free next place in the storage area. */ 7 signed char *pcReadFrom; /*< Points to the last place that a queued item was read from. */ 8 9 xList xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. */ 10 xList xTasksWaitingToReceive; /*< List of tasks that are blocked waiting to read from this queue. Stored in priority order. */ 11 12 volatile unsigned portBASE_TYPE uxMessagesWaiting;/*< The number of items currently in the queue. */ 13 unsigned portBASE_TYPE uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */ 14 unsigned portBASE_TYPE uxItemSize; /*< The size of each items that the queue will hold. */ 15 16 volatile signed portBASE_TYPE xRxLock; /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ 17 volatile signed portBASE_TYPE xTxLock; /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ 18 19 #if ( configUSE_TRACE_FACILITY == 1 ) 20 unsigned char ucQueueNumber; 21 unsigned char ucQueueType; 22 #endif 23 24 } xQUEUE;
任务控制块中有两个列表成员,其中事件列表就是用于队列阻塞时用的。
xGenericListItem是用于将任务串成列表的列表成员,后续该任务加入就绪任务列表还是其他任务列表,都是将该列表成员插入进任务列表。
xEventListItem用于记录该任务是否在等待事件,比如是否向队列发送数据但队列已满、是否从队列读取数据但队列是空的,且设置了等待时间或无限等待。例如,若是向队列发送数据但队列已满,则该任务的xEventListItem会插入该队列的xTasksWaitingToSend列表中;同时将xGenericListItem从就绪任务列表删除,插入到挂起任务队列(若等待时间是无限)或延时任务队列(若等待时间是有限)(该过程由vTaskPlaceOnEventList完成)。若是队列非满了,则会将任务的xEventListItem从xTasksWaitingToSend中移除;同时,将任务的xGenericListItem从挂起任务队列或延时任务队列中移除,并添加到就绪队列中(该过程由xTaskRemoveFromEventList完成)。
xQueueGenericSend和xQueueGenericSendFromISR的区别在与
(1)如果队列已满,则,普通send会阻塞,而fromISR不会阻塞;
(2)如果有任务因读取队列而阻塞且该任务优先级高,则普通send会马上yield,使能任务切换到高优先级任务,而fromISR则是返回一个标识。
1 /*-----------------------------------------------------------*/ 2 3 signed portBASE_TYPE xQueueGenericSend( xQueueHandle pxQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition ) 4 { 5 signed portBASE_TYPE xEntryTimeSet = pdFALSE; 6 xTimeOutType xTimeOut; 7 8 configASSERT( pxQueue ); 9 configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( unsigned portBASE_TYPE ) 0U ) ) ); 10 11 /* This function relaxes the coding standard somewhat to allow return 12 statements within the function itself. This is done in the interest 13 of execution time efficiency. */ 14 for( ;; ) 15 { 16 taskENTER_CRITICAL(); 17 { 18 /* Is there room on the queue now? To be running we must be 19 the highest priority task wanting to access the queue. */ 20 if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) 21 { 22 traceQUEUE_SEND( pxQueue ); 23 prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); 24 25 /* If there was a task waiting for data to arrive on the 26 queue then unblock it now. */ 27 if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) 28 { 29 if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) == pdTRUE ) 30 { 31 /* The unblocked task has a priority higher than 32 our own so yield immediately. Yes it is ok to do 33 this from within the critical section - the kernel 34 takes care of that. */ 35 portYIELD_WITHIN_API(); 36 } 37 } 38 39 taskEXIT_CRITICAL(); 40 41 /* Return to the original privilege level before exiting the 42 function. */ 43 return pdPASS; 44 } 45 else 46 { 47 if( xTicksToWait == ( portTickType ) 0 ) 48 { 49 /* The queue was full and no block time is specified (or 50 the block time has expired) so leave now. */ 51 taskEXIT_CRITICAL(); 52 53 /* Return to the original privilege level before exiting 54 the function. */ 55 traceQUEUE_SEND_FAILED( pxQueue ); 56 return errQUEUE_FULL; 57 } 58 else if( xEntryTimeSet == pdFALSE ) 59 { 60 /* The queue was full and a block time was specified so 61 configure the timeout structure. */ 62 vTaskSetTimeOutState( &xTimeOut ); 63 xEntryTimeSet = pdTRUE; 64 } 65 } 66 } 67 taskEXIT_CRITICAL(); 68 69 /* Interrupts and other tasks can send to and receive from the queue 70 now the critical section has been exited. */ 71 72 vTaskSuspendAll(); 73 prvLockQueue( pxQueue ); 74 75 /* Update the timeout state to see if it has expired yet. */ 76 if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) 77 { 78 if( prvIsQueueFull( pxQueue ) != pdFALSE ) 79 { 80 traceBLOCKING_ON_QUEUE_SEND( pxQueue ); 81 vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); 82 83 /* Unlocking the queue means queue events can effect the 84 event list. It is possible that interrupts occurring now 85 remove this task from the event list again - but as the 86 scheduler is suspended the task will go onto the pending 87 ready last instead of the actual ready list. */ 88 prvUnlockQueue( pxQueue ); 89 90 /* Resuming the scheduler will move tasks from the pending 91 ready list into the ready list - so it is feasible that this 92 task is already in a ready list before it yields - in which 93 case the yield will not cause a context switch unless there 94 is also a higher priority task in the pending ready list. */ 95 if( xTaskResumeAll() == pdFALSE ) 96 { 97 portYIELD_WITHIN_API(); 98 } 99 } 100 else 101 { 102 /* Try again. */ 103 prvUnlockQueue( pxQueue ); 104 ( void ) xTaskResumeAll(); 105 } 106 } 107 else 108 { 109 /* The timeout has expired. */ 110 prvUnlockQueue( pxQueue ); 111 ( void ) xTaskResumeAll(); 112 113 /* Return to the original privilege level before exiting the 114 function. */ 115 traceQUEUE_SEND_FAILED( pxQueue ); 116 return errQUEUE_FULL; 117 } 118 } 119 }
1 /*-----------------------------------------------------------*/ 2 3 signed portBASE_TYPE xQueueGenericSendFromISR( xQueueHandle pxQueue, const void * const pvItemToQueue, signed portBASE_TYPE *pxHigherPriorityTaskWoken, portBASE_TYPE xCopyPosition ) 4 { 5 signed portBASE_TYPE xReturn; 6 unsigned portBASE_TYPE uxSavedInterruptStatus; 7 8 configASSERT( pxQueue ); 9 configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( unsigned portBASE_TYPE ) 0U ) ) ); 10 11 /* Similar to xQueueGenericSend, except we don't block if there is no room 12 in the queue. Also we don't directly wake a task that was blocked on a 13 queue read, instead we return a flag to say whether a context switch is 14 required or not (i.e. has a task with a higher priority than us been woken 15 by this post). */ 16 uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); 17 { 18 if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) 19 { 20 traceQUEUE_SEND_FROM_ISR( pxQueue ); 21 22 prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); 23 24 /* If the queue is locked we do not alter the event list. This will 25 be done when the queue is unlocked later. */ 26 if( pxQueue->xTxLock == queueUNLOCKED ) 27 { 28 if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) 29 { 30 if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) 31 { 32 /* The task waiting has a higher priority so record that a 33 context switch is required. */ 34 if( pxHigherPriorityTaskWoken != NULL ) 35 { 36 *pxHigherPriorityTaskWoken = pdTRUE; 37 } 38 } 39 } 40 } 41 else 42 { 43 /* Increment the lock count so the task that unlocks the queue 44 knows that data was posted while it was locked. */ 45 ++( pxQueue->xTxLock ); 46 } 47 48 xReturn = pdPASS; 49 } 50 else 51 { 52 traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ); 53 xReturn = errQUEUE_FULL; 54 } 55 } 56 portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); 57 58 return xReturn; 59 }
大幅度发