關於FreeRTOS的信號量、隊列


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 }

 

 

大幅度發


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM