博客很久没更新了,忙着写简历、刷题、找实习去了,实习offer倒是拿到几个,但是导师不让我出去实习,哎…….. 苦逼牛马研究生的无奈
FreeRTOS内部机制剖析
FreeRTOS
是一个嵌入式实时系统,内核设计十分精简,核心代码也比较少,是一个运行在单核cpu的系统 ,支持多种架构比如ARM
、RISCV
等
1.FreeRTOS编译运行
移植此系统之前我们先来看一下如何编译运行,我们还是在qemu
上运行。首先去下载源码:
我下载的是GIthub
的FreeRTOSv202107.00版本,FreeRTOS
源码文件夹主要组成如下:
source
为FreeRTOS
源代码,包含不同平台移植相关的源码和FreeRTOS
内核本身的代码
Demo
为不同平台下运行的示例
我们直接来到FreeRTOS/Demo/RISC-V-Qemu-virt_GCC
文件夹下,这个demo
工程就是官方为Qemu-virt
平台适配的,在此目录下直接make
编译,大概率是会直接报错的,错误大概有如下几项:
target emulation elf32-littleriscv' does not match elf64-littleriscv
这个错误是编译工具链的选择问题,在Makefile
第一行中,官方将默认编译器设置成了
CROSS = riscv64-unknown-elf-
|
修改成32位的编译器即可
CROSS = riscv32-unknown-elf-
|
Error: unrecognized opcode csrc mstatus,8, extension zicsr required
这个错误是在编译时需要去读取控制寄存器但是却没有启用控制寄存器相关的拓展,在编译选项后加入_zicsr
的拓展即可
can't link double-float modules with soft-float modules
”这个错误编译环境中存在浮点数支持配置的不一致性。具体来说,项目中有些模块是用硬浮点(double-float)编译的,而有些则是用软浮点(soft-float)编译的。通过统一浮点支持配置来解决,加上d
拓展就可以了
解决上述问题后再编译应该就不会报错了,接下来是使用qemu
运行,在demo
工程下官方有一个readme
来说明了如何运行:
qemu-system-riscv32
可以直接apt
下载
运行试试看,出现下面的打印就说明运行成功了
2.FreeRTOS常用API
任务创建
任务控制
RTOS内核控制
队列
信号量/互斥锁
软件定时器
事件组
3. 任务管理与调度
3.1 FreeRTOS的TCB
typedef struct tskTaskControlBlock { volatile StackType_t *pxTopOfStack;
#if ( portUSING_MPU_WRAPPERS == 1 ) xMPU_SETTINGS xMPUSettings; #endif
ListItem_t xStateListItem; ListItem_t xEventListItem; UBaseType_t uxPriority; StackType_t *pxStack; char pcTaskName[ configMAX_TASK_NAME_LEN ];
#if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) StackType_t *pxEndOfStack; #endif
#if ( portCRITICAL_NESTING_IN_TCB == 1 ) UBaseType_t uxCriticalNesting; #endif
#if ( configUSE_TRACE_FACILITY == 1 ) UBaseType_t uxTCBNumber; UBaseType_t uxTaskNumber; #endif
#if ( configUSE_MUTEXES == 1 ) UBaseType_t uxBasePriority; UBaseType_t uxMutexesHeld; #endif
#if ( configUSE_APPLICATION_TASK_TAG == 1 ) TaskHookFunction_t pxTaskTag; #endif
#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; #endif
#if( configGENERATE_RUN_TIME_STATS == 1 ) uint32_t ulRunTimeCounter; #endif
#if ( configUSE_NEWLIB_REENTRANT == 1 ) struct _reent xNewLib_reent; #endif
#if( configUSE_TASK_NOTIFICATIONS == 1 ) volatile uint32_t ulNotifiedValue; volatile uint8_t ucNotifyState; #endif
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) uint8_t ucStaticallyAllocated; #endif
#if( INCLUDE_xTaskAbortDelay == 1 ) uint8_t ucDelayAborted; #endif
#if( configUSE_POSIX_ERRNO == 1 ) int iTaskErrno; #endif
} tskTCB; typedef tskTCB TCB_t;
|
3.2 任务创建
xTaskCreate
TCB_t * pxNewTCB; BaseType_t xReturn; StackType_t * pxStack; pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); if( pxStack != NULL ) { pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); if( pxNewTCB != NULL ) { pxNewTCB->pxStack = pxStack; } else { vPortFree( pxStack ); } } else { pxNewTCB = NULL; } if( pxNewTCB != NULL ) { #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) { pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; } #endif prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); prvAddNewTaskToReadyList( pxNewTCB ); xReturn = pdPASS; } else { xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; }
return xReturn;
|
- 首先是创建一个
TCB
,分配一片内存
- 然后是为创建的任务分配栈空间
- 调用
prvInitialiseNewTask
初始化任务,会做如下操作:
- 此函数内部会去调用
pxPortInitialiseStack
来初始化任务的栈,任务栈中存储的是任务的执行状态的寄存器的值,pxPortInitialiseStack
会构建一份初始化的值将这个任务切换上下文对应寄存器的值全部设置为0
- 填充任务的名字
- 初始化任务优先级
- 设置栈溢出地址
- 将此任务挂载到对应优先级的
ready
链表中
3.3 多优先级链式TCB连接
在FreeRTOS
中任务有多种状态和多种优先级
typedef enum { eRunning = 0, eReady, eBlocked, eSuspended, eDeleted, eInvalid } eTaskState;
|
任务切换的逻辑如下:
- 就绪态:任务在创建完成时是
ready
状态,就绪态的任务等待调度器调度
- 运行态:任务独占
cpu
正在运行
- 阻塞态:等待某个事件的到来,定时或者同步
- 挂起态:退出调度系统,调度器不可见,只能使用vTaskSuspend()挂起和vTaskResume()唤醒后进入就绪态
PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL; PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ]; PRIVILEGED_DATA static List_t xDelayedTaskList1; PRIVILEGED_DATA static List_t xDelayedTaskList2; PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList; PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList; PRIVILEGED_DATA static List_t xPendingReadyList;
|
pxCurrentTCB
是一个TCB_t
类型的全局指针,用于指向当前运行任务的地址
pxReadyTasksLists[ configMAX_PRIORITIES ];
是一个数组,数组下标表示优先级,其每一个数组元素都是一个链表根节点数据结构。如下图所示。在FreeRTOS
中,优先级数值越大,优先级越高。
typedef struct xLIST { volatile UBaseType_t uxNumberOfItems; ListItem_t * configLIST_VOLATILE pxIndex; MiniListItem_t xListEnd; listSECOND_LIST_INTEGRITY_CHECK_VALUE } List_t;
struct xMINI_LIST_ITEM { configLIST_VOLATILE TickType_t xItemValue; struct xLIST_ITEM * configLIST_VOLATILE pxNext; struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; }; typedef struct xMINI_LIST_ITEM MiniListItem_t;
struct xLIST_ITEM { configLIST_VOLATILE TickType_t xItemValue; struct xLIST_ITEM * configLIST_VOLATILE pxNext; struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; void * pvOwner; struct xLIST * configLIST_VOLATILE pxContainer; }; typedef struct xLIST_ITEM ListItem_t;
|
对于数组中的每个优先级的任务链表,构成如下:
在TCB
的定义中:定义了两个ListItem_t
类型的变量,因此任务挂载到任务链表上实际上就是通过第一个xStateListItem
来实现的
ListItem_t xStateListItem;
ListItem_t xEventListItem;
|
任务链表:xLIST即根节点,可以是就绪链表、阻塞链表、挂起链表等。
链表中的节点:每个红框为一个节点,通过pvOwner
与任务控制块联系到一起,通过pvContainer
挂载到就绪、阻塞、挂起链表上。
就绪列表(数组)、任务控制块(TCB) 的拓扑结构如下:
就绪列表是含有多个链表的数组集合,数组的索引代表优先级。
任务可以通过TCB
中的xLIST_ITEM
挂在任意一个优先级上的链表中。如上图就表示一个有256个优先级的就绪列表。其中优先级为254的链表上有两个节点,这两个节点分别属于两个任务的TCB。因此这两个TCB的任务优先级就是254,而且目前处于就绪态。假设此时有一个优先级为0的任务从其他状态转换到就绪态。则该任务插入就绪列表的过程如下:该 任务的TCB
中的xLIST_ITEM
会链接(link)到 就绪列表数组的0号元素根节点xList
中的MiniListItem_t
上。·
3.4 任务调度方式
抢占式调度
在可抢占式调度中,任务可以被更高优先级的任务抢占。当一个高优先级任务变得可用时,它可以打断当前正在执行的低优先级任务,从而使系统立即切换到高优先级任务执行。
在FreeRTOS
中,任务调度是基于任务优先级的。当一个任务抢占另一个任务时,它会立即执行,无论被抢占的任务是否已经执行完其时间片。这种方式确保了高优先级任务能够及时响应,并在需要时立即执行,不受低优先级任务的阻碍。
在FreeRTOS
中通过配置configUSE_PREEMPTION
来决定是否启动抢占。
如果高优先级不让出cpu,那么低优先级的任务将一直得不到执行
时间片轮转调度
时间片轮转是指操作系统为每个任务分配一个时间片,即预定义的时间量。在时间片轮转调度方式下,每个任务可以执行一个时间片,然后系统将控制权移交给下一个就绪的任务。如果一个任务在其时间片结束前没有完成,系统会暂停该任务,将控制权交给下一个就绪的任务。
FreeRTOS
允许你在配置系统时启用或禁用时间片轮转。时间片的大小可以根据应用程序的需要进行调整。这种调度方式有助于确保任务之间的公平性,避免某些任务长时间占用处理器,同时允许多个任务分享处理时间。
在FreeRTOS
中通过配置configUSE_TIME_SLICING
来决定是否启动时间片轮转
对于同优先级的任务才会启用时间片轮转
如何让出CPU
- 任务主动让出:任务可以调用
vTaskDelay()
函数或者vTaskDelayUntil()
函数,将自己挂起一段时间,以便其他任务能够运行。这种方式是任务主动放弃CPU的一种方式
- 阻塞等待事件:任务可以调用
FreeRTOS
提供的阻塞函数,如xQueueReceive()、xSemaphoreTake()
等,来等待特定事件的发生。当任务在等待某个事件时,它会被置于阻塞状态,从而释放CPU,直到事件发生后才会被唤醒。
- 任务进入阻塞状态:任务在执行过程中,如果发生某些阻塞事件,如等待一个队列满足条件、等待互斥信号量等,会自动进入阻塞状态,这时会释放CPU。一旦阻塞条件得到满足,任务将被重新置于就绪状态。
4.消息队列和信号量
消息队列是用于进程间通讯的,且可以实现互斥的访问,避免多进程间由于任务切换导致的竞态问题
xQueueCreate
创建一个队列
xQueueSend
写队列
xQueueReceive
读队列
4.1 队列创建和初始化
首先我们来看一下队列在FreeRTOS
中的定义:
typedef struct QueuePointers { int8_t * pcTail; int8_t * pcReadFrom; } QueuePointers_t; typedef struct QueueDefinition { int8_t * pcHead; int8_t * pcWriteTo;
union { QueuePointers_t xQueue; SemaphoreData_t xSemaphore; } u;
List_t xTasksWaitingToSend; List_t xTasksWaitingToReceive; volatile UBaseType_t uxMessagesWaiting; UBaseType_t uxLength; UBaseType_t uxItemSize;
volatile int8_t cRxLock; volatile int8_t cTxLock;
#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) uint8_t ucStaticallyAllocated; #endif
#if ( configUSE_QUEUE_SETS == 1 ) struct QueueDefinition * pxQueueSetContainer; #endif
#if ( configUSE_TRACE_FACILITY == 1 ) UBaseType_t uxQueueNumber; uint8_t ucQueueType; #endif } xQUEUE;
|
队列是通过xQueueCreate
这个宏来创建的,这个宏内部会去调用xQueueGenericCreate
函数来创建一个队列,例如:
QueueHandle_t xMyQueueHandle; xMyQueueHandle = xQueueCreate(20,sizeof(int32_t));
|
创建了一个长度为20个元素、每个元素为int32_t
类型的队列:
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) { Queue_t * pxNewQueue; size_t xQueueSizeInBytes; uint8_t * pucQueueStorage;
configASSERT( uxQueueLength > ( UBaseType_t ) 0 ); xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); configASSERT( ( uxItemSize == 0 ) || ( uxQueueLength == ( xQueueSizeInBytes / uxItemSize ) ) );
configASSERT( ( sizeof( Queue_t ) + xQueueSizeInBytes ) > xQueueSizeInBytes );
pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); if( pxNewQueue != NULL ) { pucQueueStorage = ( uint8_t * ) pxNewQueue; pucQueueStorage += sizeof( Queue_t );
#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) { pxNewQueue->ucStaticallyAllocated = pdFALSE; } #endif prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue ); } else { traceQUEUE_CREATE_FAILED( ucQueueType ); mtCOVERAGE_TEST_MARKER(); }
return pxNewQueue; }
|
对列在创建完毕分配内存后会进行初始化,调用prvInitialiseNewQueue
函数,此函数内部会调用xQueueGenericReset
去做一些初始化操作
static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t * pucQueueStorage, const uint8_t ucQueueType, Queue_t * pxNewQueue ) { ( void ) ucQueueType;
if( uxItemSize == ( UBaseType_t ) 0 ) { pxNewQueue->pcHead = ( int8_t * ) pxNewQueue; } else { pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage; }
pxNewQueue->uxLength = uxQueueLength; pxNewQueue->uxItemSize = uxItemSize; ( void ) xQueueGenericReset( pxNewQueue, pdTRUE );
#if ( configUSE_TRACE_FACILITY == 1 ) { pxNewQueue->ucQueueType = ucQueueType; } #endif
#if ( configUSE_QUEUE_SETS == 1 ) { pxNewQueue->pxQueueSetContainer = NULL; } #endif
traceQUEUE_CREATE( pxNewQueue ); } BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ) { Queue_t * const pxQueue = xQueue;
configASSERT( pxQueue );
taskENTER_CRITICAL(); { pxQueue->u.xQueue.pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize ); pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U; pxQueue->pcWriteTo = pxQueue->pcHead; pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - 1U ) * pxQueue->uxItemSize ); pxQueue->cRxLock = queueUNLOCKED; pxQueue->cTxLock = queueUNLOCKED;
if( xNewQueue == pdFALSE ) { if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) { if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) { queueYIELD_IF_USING_PREEMPTION(); } else { mtCOVERAGE_TEST_MARKER(); } } else { mtCOVERAGE_TEST_MARKER(); } } else { vListInitialise( &( pxQueue->xTasksWaitingToSend ) ); vListInitialise( &( pxQueue->xTasksWaitingToReceive ) ); } } taskEXIT_CRITICAL(); return pdPASS; }
|
队列在初始化完成后内存分布如下:
pcHead
和pcTail
这两个指针用来记录队列的起始地址和结束地址,这两个值始终不变。数据的写入和读取分别使用pvWriteTo
和pcReadFrom
两个指针,pcReadFrom
始终指向pvWriteTo
指向元素的前一个元素的地址。
4.2 写队列
xQueueSend
会去调用xQueueGenericSend
写队列
BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ) { BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired; TimeOut_t xTimeOut; Queue_t * const pxQueue = ( Queue_t * ) xQueue;
configASSERT( pxQueue ); configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) ); #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) { configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); } #endif
for( ;; ) { taskENTER_CRITICAL(); { if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ) { traceQUEUE_SEND( pxQueue ); xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
#if ( configUSE_QUEUE_SETS == 1 ) { if( pxQueue->pxQueueSetContainer != NULL ) { if( prvNotifyQueueSetContainer( pxQueue, xCopyPosition ) != pdFALSE ) { queueYIELD_IF_USING_PREEMPTION(); } } else { if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) { if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) {
queueYIELD_IF_USING_PREEMPTION(); } } else if( xYieldRequired != pdFALSE ) { queueYIELD_IF_USING_PREEMPTION(); } } } #else #endif
taskEXIT_CRITICAL(); return pdPASS; } else { if( xTicksToWait == ( TickType_t ) 0 ) { taskEXIT_CRITICAL(); traceQUEUE_SEND_FAILED( pxQueue ); return errQUEUE_FULL; } else if( xEntryTimeSet == pdFALSE ) { vTaskSetTimeOutState( &xTimeOut ); xEntryTimeSet = pdTRUE; } } } taskEXIT_CRITICAL();
vTaskSuspendAll(); prvLockQueue( pxQueue ); if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) { if( prvIsQueueFull( pxQueue ) != pdFALSE ) { traceBLOCKING_ON_QUEUE_SEND( pxQueue ); vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );
prvUnlockQueue( pxQueue );
if( xTaskResumeAll() == pdFALSE ) { portYIELD_WITHIN_API(); } } else { prvUnlockQueue( pxQueue ); ( void ) xTaskResumeAll(); } } else { prvUnlockQueue( pxQueue ); ( void ) xTaskResumeAll();
traceQUEUE_SEND_FAILED( pxQueue ); return errQUEUE_FULL; } } }
|
- 当前进程去写队列时,如果发现队列满了,则会将自己挂起,然后将自己挂到队列的等待写任务链表中,然后执行任务切换
- 如果队列未满,即当前队列可写,则会调用
prvCopyDataToQueue
函数去写队列,写完数据后会从队列的delay
任务链表中取出一个想要读数据的任务,将此任务添加到就绪链表,然后执行任务切换
static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, const void * pvItemToQueue, const BaseType_t xPosition ) { BaseType_t xReturn = pdFALSE; UBaseType_t uxMessagesWaiting;
uxMessagesWaiting = pxQueue->uxMessagesWaiting;
if( pxQueue->uxItemSize == ( UBaseType_t ) 0 ) { #if ( configUSE_MUTEXES == 1 ) { if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) { xReturn = xTaskPriorityDisinherit( pxQueue->u.xSemaphore.xMutexHolder ); pxQueue->u.xSemaphore.xMutexHolder = NULL; } else { mtCOVERAGE_TEST_MARKER(); } } #endif } else if( xPosition == queueSEND_TO_BACK ) { ( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); pxQueue->pcWriteTo += pxQueue->uxItemSize;
if( pxQueue->pcWriteTo >= pxQueue->u.xQueue.pcTail ) { pxQueue->pcWriteTo = pxQueue->pcHead; } else { mtCOVERAGE_TEST_MARKER(); } } else { ( void ) memcpy( ( void * ) pxQueue->u.xQueue.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); pxQueue->u.xQueue.pcReadFrom -= pxQueue->uxItemSize;
if( pxQueue->u.xQueue.pcReadFrom < pxQueue->pcHead ) { pxQueue->u.xQueue.pcReadFrom = ( pxQueue->u.xQueue.pcTail - pxQueue->uxItemSize ); } else { mtCOVERAGE_TEST_MARKER(); }
if( xPosition == queueOVERWRITE ) { if( uxMessagesWaiting > ( UBaseType_t ) 0 ) { --uxMessagesWaiting; } else { mtCOVERAGE_TEST_MARKER(); } } else { mtCOVERAGE_TEST_MARKER(); } }
pxQueue->uxMessagesWaiting = uxMessagesWaiting + ( UBaseType_t ) 1;
return xReturn; }
|
4.3 读队列
xQueueReceive()
函数实际是使用xQueueGenericReceive()
这个函数:
#define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE )
BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeeking ) { BaseType_t xEntryTimeSet = pdFALSE; TimeOut_t xTimeOut; int8_t *pcOriginalReadPosition; Queue_t * const pxQueue = ( Queue_t * ) xQueue;
configASSERT( pxQueue ); configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) { configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); } #endif
for( ;; ) { taskENTER_CRITICAL(); { const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; if( uxMessagesWaiting > ( UBaseType_t ) 0 ) { pcOriginalReadPosition = pxQueue->u.pcReadFrom; prvCopyDataFromQueue( pxQueue, pvBuffer ); if( xJustPeeking == pdFALSE ) { traceQUEUE_RECEIVE( pxQueue ); pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1; #if ( configUSE_MUTEXES == 1 ) { if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) { pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount(); } } #endif if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) { if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) { queueYIELD_IF_USING_PREEMPTION(); } } } else { traceQUEUE_PEEK( pxQueue ); pxQueue->u.pcReadFrom = pcOriginalReadPosition; if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) { if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) { queueYIELD_IF_USING_PREEMPTION(); } } } taskEXIT_CRITICAL(); return pdPASS; } else { if( xTicksToWait == ( TickType_t ) 0 ) { taskEXIT_CRITICAL(); traceQUEUE_RECEIVE_FAILED( pxQueue ); return errQUEUE_EMPTY; } else if( xEntryTimeSet == pdFALSE ) { vTaskSetTimeOutState( &xTimeOut ); xEntryTimeSet = pdTRUE; } } } taskEXIT_CRITICAL();
vTaskSuspendAll(); prvLockQueue( pxQueue ); if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) { if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) { #if ( configUSE_MUTEXES == 1 ) { if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) { taskENTER_CRITICAL(); { vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder ); } taskEXIT_CRITICAL(); } } #endif vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); prvUnlockQueue( pxQueue ); if( xTaskResumeAll() == pdFALSE ) { portYIELD_WITHIN_API(); } } else { prvUnlockQueue( pxQueue ); ( void ) xTaskResumeAll(); } } else { prvUnlockQueue( pxQueue ); ( void ) xTaskResumeAll(); if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) { traceQUEUE_RECEIVE_FAILED( pxQueue ); return errQUEUE_EMPTY; } } } }
|
- 当前任务去读队列时,如果队列中没有数据,则会将自己挂起,添加到队列的等待读的任务链表中,其他任务向队列中写入数据后会去队列的等待读链表中唤醒任务
- 如果队列中有数据,则会去调用
prvCopyDataFromQueue
函数从队列中取出一个元素
static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer ) { if( pxQueue->uxItemSize != ( UBaseType_t ) 0 ) { pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize;
if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail ) { pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; } else { mtCOVERAGE_TEST_MARKER(); } ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( size_t ) pxQueue->uxItemSize ); } }
|
4.4 信号量和互斥量
信号量和互斥量都是特殊的队列,都是通过队列来实现的,相当于队列中只放了一个元素供所有进程共享,信号量用于计数,互斥量只有0 , 1
值,用于进程互斥
参考链接