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 }
大幅度發