关于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 }

大幅度发

原文地址:https://www.cnblogs.com/yanhc/p/12675967.html