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