Skip to content

互斥量

队列的使用FIFO

队列: 可以包含若干个数据, 有若干项, 数据大小固定, 创建的时候指定长度, 采用先进先出

相当于一个传送带

使用的时候有两种:

  • 拷贝: 把数据, 变量复制到队列里面
  • 引用: 把数据, 变量的地址复制到队列里面

image-20231117195425622

image-20231117195442317

任务读写的时候如果不成功就进入阻塞状态, 可以指定阻塞时间, 一旦有数据就会恢复就绪态

多个任务如果读取同一个队列, 都进入阻塞, 有数据的时候优先级比较高的任务进入就绪态, 优先级相同的时候等待时间最长的任务会进入就绪状态

当数据满了的时候也需要一个进入阻塞

实际实现

猜测: 有一个存储数据的部分, 一个链表保存要读取数据阻塞的任务, 一个链表保存写入数据的链表

c
typedef struct QueuePointers
{
    int8_t * 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. 尾部*/
    int8_t * pcReadFrom; /*< Points to the last place that a queued item was read from when the structure is used as a queue. 读指针*/
} QueuePointers_t;

typedef struct QueueDefinition /* The old naming convention is used to prevent breaking kernel aware debuggers. */
{
    int8_t * pcHead;           /*< Points to the beginning of the queue storage area. 指向头部*/
    int8_t * pcWriteTo;        /*< Points to the free next place in the storage area. 读写指针*/

    union
    {
        QueuePointers_t xQueue;     /*< Data required exclusively when this structure is used as a queue. */
        SemaphoreData_t xSemaphore; /*< Data required exclusively when this structure is used as a semaphore. */
    } u;

    List_t xTasksWaitingToSend;             /*< List of tasks that are blocked waiting to post onto this queue.  Stored in priority order. */
    List_t xTasksWaitingToReceive;          /*< List of tasks that are blocked waiting to read from this queue.  Stored in priority order. 两组任务*/

    volatile UBaseType_t uxMessagesWaiting; /*< The number of items currently in the queue. 当前的数据数量*/
    UBaseType_t uxLength;                   /*< The length of the queue defined as the number of items it will hold, not the number of bytes. 保存的最大数量*/
    UBaseType_t uxItemSize;                 /*< The size of each items that the queue will hold. 一个单位的长度*/

    volatile int8_t cRxLock;                /*< 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. */
    volatile int8_t cTxLock;                /*< 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. */

    #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
        uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */
    #endif

    #if ( configUSE_QUEUE_SETS == 1 )
        struct QueueDefinition * pxQueueSetContainer;
    #endif

    #if ( configUSE_TRACE_FACILITY == 1 )
        UBaseType_t uxQueueNumber;
        uint8_t ucQueueType;
    #endif
} xQUEUE;

实际使用

  • 创建
c
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );

队列的长度以及队列一个项的大小(单位是字节), 返回值是一个队列的结构体

c
QueueHandle_t xQueueCreateStatic(UBaseType_t uxQueueLength,
                                UBaseType_t uxItemSize,
                                uint8_t *pucQueueStorageBuffer,
                                StaticQueue_t *pxQueueBuffer
);

静态创建: 队列的长度, 队列一个单位的大小, 队列的数组, 队列的结构体, 返回指向结构体的句柄

  • 复位
c
BaseType_t xQueueReset( QueueHandle_t pxQueue);
  • 删除
c
void vQueueDelete( QueueHandle_t xQueue );

删除动态创建的队列, 释放内存

  • 写队列
c
BaseType_t xQueueSend(QueueHandle_t xQueue,
                        const void *pvItemToQueue,
                        TickType_t xTicksToWait
);

向队列尾部写入数据, 等待的时间是xTicksToWait

c
BaseType_t xQueueSendToBack(QueueHandle_t xQueue,
                            const void *pvItemToQueue,
                            TickType_t xTicksToWait
);
c
BaseType_t xQueueSendToBackFromISR(QueueHandle_t xQueue,
                            const void *pvItemToQueue,
                            BaseType_t *pxHigherPriorityTaskWoken
);

可以在中断中使用

c
BaseType_t xQueueSendToFront(QueueHandle_t xQueue,
                            const void *pvItemToQueue,
                            TickType_t xTicksToWait
);

向头部写入数据

c
BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,
                                    const void *pvItemToQueue,
                                    BaseType_t *pxHigherPriorityTaskWoken
);

头部写入, 不可阻塞, 中断中使用

image-20231117201539734

可以使用portMAX_DELAY一直等待

  • 读取
c
BaseType_t xQueueReceive( QueueHandle_t xQueue,
                            void * const pvBuffer,
                            TickType_t xTicksToWait 
);
BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue,
                                void *pvBuffer,
                                BaseType_t *pxTaskWoken
);
  • 查询
c
/*
* 返回队列中可用数据的个数
*/
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
/*
* 返回队列中可用空间的个数
*/
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );
  • 覆盖
c
/* 覆盖队列
* xQueue: 写哪个队列
* pvItemToQueue: 数据地址
* 返回值: pdTRUE表示成功, pdFALSE表示失败
*/
BaseType_t xQueueOverwrite(
QueueHandle_t xQueue,
const void * pvItemToQueue
);
BaseType_t xQueueOverwriteFromISR(
QueueHandle_t xQueue,
const void * pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken
);

队列长度为1的时候使用这一个函数对队列进行覆盖

  • 窥视
c
/* 偷看队列
* xQueue: 偷看哪个队列
* pvItemToQueue: 数据地址, 用来保存复制出来的数据
* xTicksToWait: 没有数据的话阻塞一会
* 返回值: pdTRUE表示成功, pdFALSE表示失败
*/
BaseType_t xQueuePeek(
QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait
);
BaseType_t xQueuePeekFromISR(
QueueHandle_t xQueue,
void *pvBuffer,
);

读取一个数据, 但是不更改读指针的位置

实现一个锁

c
int InitUARTLock(void)
{
    int val;
    xQueueUARTcHandle = xQueueCreat(1, sizeof(int));
    if(xQueueUARTcHandle==NULL)
    {
        printf("can not creat queue\r\n");
        return -1;
    }
    xQueueSend(xQueueUARTcHandle, &val, portMAX_DELAY);
}
void GetUARTLock(void)
{
    int val;
    xQueueReceive(xQueueUARTcHandle, &val, portMAX_DELAY);
}
void PutUARTLock(void)
{
    int val;
    xQueueSend(xQueueUARTcHandle, &val, portMAX_DELAY);
}

当队列里面有数据的时候表示空闲

c
void  Task1Function( void * param){
    while(1){
        Task1Flog = 1;
        Task2Flog = 0;
        Task3Flog = 0;
        GetUARTLock();
        printf("This is task 1\r\n");
        PutUARTLock();
        vTaskDelay(1);	//保证另一个任务可以运行
    }
}

image-20231117205600322

邮箱

image-20231117215121960

队列集

从多个队列里面获取一个数据

比如需要同时获取鼠标, 键盘以及触摸屏的数据的时候可以使用这一个数据类型

  1. 创建一个队列集
  2. 队列集的长度是队列的handle长度
  3. 使用的各个单独的队列都会有一个参数指向这一个队列集, 建立联系
  4. 之后有数据的时候, 在写数据的时候会把数据写入队列里面, 并且把队列的handle写入队列集里面
  5. 读取队列集的时候会返回一个handle, 之后用这一个handle读取队列

函数

c
QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength );

configUSE_QUEUE_SETS宏定义, 参数是监视的所有队列的长度之和

  • 添加/建立联系
c
    BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore,
                               QueueSetHandle_t xQueueSet );

参数是队列和队列集

  • 获取
c
    QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet,
                                                TickType_t const xTicksToWait );

示例

c
xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, &xHandleTask2);
xTaskCreate(Task3Function, "Task3", 100, NULL, 1, &xHandleTask3);
xTask1Queue = xQueueCreate(2, sizeof(int));    
xTask2Queue = xQueueCreate(2, sizeof(int));
xQueueSet = xQueueCreateSet(4);
xQueueAddToSet(xTask1Queue, xQueueSet);
xQueueAddToSet(xTask2Queue, xQueueSet);
c
void  Task2Function( void * param){
    int i = -1;
    while(1){
        Task1Flog = 0;
        Task2Flog = 1;
        Task3Flog = 0;
        xQueueSend(xTask2Queue, &i, portMAX_DELAY);
        i--;
        vTaskDelay(10);

    }
}
void  Task1Function( void * param){
    int i = 0;
    while(1){
        Task1Flog = 1;
        Task2Flog = 0;
        Task3Flog = 0;
        xQueueSend(xTask1Queue, &i, portMAX_DELAY);
        i++;
        vTaskDelay(20);
    }
}

//钩子函数会在空闲任务里面执行
void  Task3Function( void * param){
    QueueSetMemberHandle_t handle;
    int i;
    while(1){
        Task1Flog = 0;
        Task2Flog = 0;
        Task3Flog = 1;
        handle = xQueueSelectFromSet(xQueueSet, portMAX_DELAY);
        xQueueReceive(handle, &i, 0);
        printf("%d\n", i);
    }
        //printf("3");
}

信号量

有一个计数值, 任务完成让信号量加一, 使用的时候使信号量减一

信号: 通知作用

量: 表示资源的数量

量没有限制的时候就是计数型信号量

只有0, 1两种的时候就是二进制信号量

支持的行为: 产生信号的时候give信号量加一, 处理事件的时候take, 信号量减一

访问的时候先take, 访问结束give

image-20231119095715364

image-20231119095745304

函数

  • 创建

image-20231119095759865

c
/* 创建一个二进制信号量,返回它的句柄。
* 此函数内部会分配信号量结构体
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateBinary( void ); //默认获取的时候计数值为0
/* 创建一个二进制信号量,返回它的句柄。
* 此函数无需动态分配内存,所以需要先有一个StaticSemaphore_t结构体,并传入它的指针
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t
*pxSemaphoreBuffer );
c
/* 创建一个计数型信号量,返回它的句柄。
* 此函数内部会分配信号量结构体
* uxMaxCount: 最大计数值
* uxInitialCount: 初始计数值
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t
uxInitialCount);
/* 创建一个计数型信号量,返回它的句柄。
* 此函数无需动态分配内存,所以需要先有一个StaticSemaphore_t结构体,并传入它的指针
* uxMaxCount: 最大计数值
* uxInitialCount: 初始计数值
* pxSemaphoreBuffer: StaticSemaphore_t结构体指针
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount,
StaticSemaphore_t
*pxSemaphoreBuffer );

他的本质还是一个队列, 使用队列结构体个uxMessagegsWaiting变量保存当前的总的资源的数量

  • 删除
c
/*
* xSemaphore: 信号量句柄,你要删除哪个信号量
*/
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
  • 使用

image-20231119100135553

c
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );

返回的是pdTRUE的时候表示成功, 如果超过最大值会返回失败

c
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,
						BaseType_t *pxHigherPriorityTaskWoken
);

第一个参数是要操作的信号量, 第二个参数是是否有高优先级的任务被唤醒, 有的话返回pdTRUE

返回值同上

c
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,
					TickType_t xTicksToWait
);

使用这一个如果信号量为0的时候会阻塞

c
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,
					BaseType_t *pxHigherPriorityTaskWoken
);

信号量句柄, 高优先级任务是否被唤醒

实际使用

使用二进制信号量的时候由于最大只有1, 所以有多次提醒的时候有可能只会接收到一次, 可以使用一个缓冲区, 把所有数据放进去, 最后提取的时候使用一次性把所有的数据提取出来

互斥量

不要在中断里面使用!!!!

相对于信号量有可能会在上锁以后进行任务切换, 切换以后在其他的任务里面对信号量进行解锁(不能实现由谁上锁就是谁来解锁)

互斥量也不能实现这一点, 但是可以解决优先级反转以及递归上锁解锁

**没有解决的时候出现的问题: ** 优先级为1的任务使用一个资源进行上锁, 这时候优先级为2的任务开始运行, 然后又有优先级为3的任务开始运行, 优先级为三的任务也想使用, 进入阻塞, 这时候如果优先级为2的任务一直在运行, 优先级为1的任务就不能进行, 资源不能释放

结果: 优先级为2的任务把优先级为3的任务抢占了

**解决方法: **优先级继承, 在优先级为3进入休眠的时候会提高比较低的优先级任务的优先级, 等他释放的时候返回之前的优先级

**二次上锁: **有一个任务多次获取同一个信号量, 由于自己阻塞了, 所以没有人可以进行释放, 就会进入死锁

**解决方式: **递归锁, 在你持有这一个锁的时候可以递归上锁, 再次获取自己的锁

**注意: **互斥量只实现了优先级继承, 递归锁是另一种特殊的互斥量

使用场景

在有多个任务访问同一个全局变量时候, 如果在任务一获取进行处理但是没有更新的时候任务二对这一个变量进行操作, 会导致操作结果不是预期的的问题

  • 问题原因

没有使用原子操作的变量(按照机器的位数进行使用变量), 比如在16位机器上使用32位的变量, 操作可能会被打断

函数不可重用(使用全局变量, 局部变量)

实际使用

  • 创建
c
/* 创建一个互斥量,返回它的句柄。
* 此函数内部会分配互斥量结构体
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateMutex( void );
/* 创建一个互斥量,返回它的句柄。
* 此函数无需动态分配内存,所以需要先有一个StaticSemaphore_t结构体,并传入它的指针
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer
);

在创建的时候会把值设置为1

  • give/take注意: 互斥量不能在中断中使用
c
/*
* xSemaphore: 信号量句柄,你要删除哪个信号量, 互斥量也是一种信号量
*/
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
/* 释放 */
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
/* 释放(ISR版本) */
BaseType_t xSemaphoreGiveFromISR(
SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken
);
/* 获得 */
BaseType_t xSemaphoreTake(
SemaphoreHandle_t xSemaphore,
    TickType_t xTicksToWait
);
/* 获得(ISR版本) */
xSemaphoreGiveFromISR(
SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken
);

缺陷以及递归锁

没有检测是否是本人在释放

可能会产生监守自盗的情况

c
static void vGiveAndTakeTask( void *pvParameters )
{
	const TickType_t xTicksToWait = pdMS_TO_TICKS( 10UL );	
	BaseType_t xStatus;

	/* 尝试获得互斥量: 上锁 */
	xStatus = xSemaphoreTake(xMutex, 0);
	printf("Task2: at first, take the Mutex %s\r\n", \
		(xStatus == pdTRUE)? "Success" : "Failed");
	
	/* 如果失败则监守自盗: 开锁 */
	if (xStatus != pdTRUE)
	{
		xStatus = xSemaphoreGive(xMutex);
		printf("Task2: give Mutex %s\r\n", \
			(xStatus == pdTRUE)? "Success" : "Failed");
	}

	/* 最后成功获得互斥量 */
	xStatus = xSemaphoreTake(xMutex, portMAX_DELAY);
	printf("Task2: and then, take the Mutex %s\r\n", \
		(xStatus == pdTRUE)? "Success" : "Failed");
	
	/* 无限循环 */
	for( ;; )
	{	
		/* 什么都不做 */
		vTaskDelay(xTicksToWait);
	}
}
  • 解决

使用递归锁, 实现谁持有就有谁有放

image-20231119114023325

c
/* 创建一个递归锁,返回它的句柄。
* 此函数内部会分配互斥量结构体
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void );
/* 释放 */
BaseType_t xSemaphoreGiveRecursive( SemaphoreHandle_t xSemaphore );
/* 获得 */
BaseType_t xSemaphoreTakeRecursive(SemaphoreHandle_t xSemaphore,
	TickType_t xTicksToWait
);

事件组

之前介绍的手段都没有办法实现有多个事件的处理

可以认为是一个整数, 使用它的不同的位表述不同的事件

1表示事件发生, 0表示事件没有发生

事件组用一个整数表示, 高八位是给内核使用的

image-20231119194438693

image-20231119194507468

image-20231119194533066

实现使用的函数

c
typedef struct EventGroupDef_t
{
    EventBits_t uxEventBits;//一个整数, 每一位都是一个事件
    List_t xTasksWaitingForBits; /*< List of tasks waiting for a bit to be set. */

    #if ( configUSE_TRACE_FACILITY == 1 )
        UBaseType_t uxEventGroupNumber;
    #endif

    #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
        uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the event group is statically allocated to ensure no attempt is made to free the memory. */
    #endif
} EventGroup_t;
  • 创建
c
/* 创建一个事件组,返回它的句柄。
* 此函数内部会分配事件组结构体
* 返回值: 返回句柄,非NULL表示成功
*/
EventGroupHandle_t xEventGroupCreate( void );
/* 创建一个事件组,返回它的句柄。
* 此函数无需动态分配内存,所以需要先有一个StaticEventGroup_t结构体,并传入它的指针
* 返回值: 返回句柄,非NULL表示成功
*/
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *
pxEventGroupBuffer );
  • 删除
c
/*
* xEventGroup: 事件组句柄,你要删除哪个事件组
*/
void vEventGroupDelete( EventGroupHandle_t xEventGroup )
  • 设置事件
c
/* 设置事件组中的位
* xEventGroup: 哪个事件组
* uxBitsToSet: 设置哪些位?
* 如果uxBitsToSet的bitX, bitY为1, 那么事件组中的bitX, bitY被设置为1
* 可以用来设置多个位,比如 0x15 就表示设置bit4, bit2, bit0
* 返回值: 返回原来的事件值(没什么意义, 因为很可能已经被其他任务修改了)
*/
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, 					const EventBits_t uxBitsToSet );
/* 设置事件组中的位
* xEventGroup: 哪个事件组
* uxBitsToSet: 设置哪些位?
* 如果uxBitsToSet的bitX, bitY为1, 那么事件组中的bitX, bitY被设置为1
* 可以用来设置多个位,比如 0x15 就表示设置bit4, bit2, bit0
* pxHigherPriorityTaskWoken: 有没有导致更高优先级的任务进入就绪态? pdTRUE-有,
pdFALSE-没有
* 返回值: pdPASS-成功, pdFALSE-失败
*/
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
BaseType_t * pxHigherPriorityTaskWoken );

设置事件组的时候有可能会有多个任务被唤醒, 这会带来很大的不确定性。所以 xEventGroupSetBitsFromISR 函数不是直接去设置事件组,而是给一个FreeRTOS后台任务(daemon task)发送队列数据,由这个任务来设置事件组。

如果后台任务的优先级比当前被中断的任务优先级高, xEventGroupSetBitsFromISR 会设置 *pxHigherPriorityTaskWoken 为pdTRUE。

如果daemon task成功地把队列数据发送给了后台任务,那么 xEventGroupSetBitsFromISR 的返回值 就是pdPASS。

  • 等待事件
c
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
                        const EventBits_t uxBitsToWaitFor,
                        const BaseType_t xClearOnExit,
                        const BaseType_t xWaitForAllBits,
                        TickType_t xTicksToWait );

参数是: 事件组, 等待哪些事件, 返回时候是否会清除, 是否等待所有时间(pdTRUE=都等待与, pdFALSE则是或), 等待的时间

  • 同步点
c
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
                        const EventBits_t uxBitsToSet,
                        const EventBits_t uxBitsToWaitFor,
                        TickType_t xTicksToWait );

当有多个任务协调控控制某一个事件的时候, 使用这一个函数设置一个位等待其他的事件

FreeRTOS代码分析

  • queueQUEUE_TYPE_BASE:表示队列。
  • queueQUEUE_TYPE_SET:表示队列集合。
  • queueQUEUE_TYPE_MUTEX:表示互斥量。
  • queueQUEUE_TYPE_COUNTING_SEMAPHORE:表示计数信号量。
  • queueQUEUE_TYPE_BINARY_SEMAPHORE:表示二进制信号量。
  • queueQUEUE_TYPE_RECURSIVE_MUTEX :表示递归互斥量。

消息队列

image-20240121162745588

系统会为控制块分配对应的内存空间,用于保存消息队列的一些信息如消息的存储位置,头指针pcHead、尾指针pcTail、消息大小uxItemSize以及队列长度uxLength等

实际就是一个环形缓冲区

结构体

c
typedef struct QueueDefinition
{
	int8_t *pcHead;					/*< 队列的起始位置 */
	int8_t *pcTail;					/* 消息存储的结束位置 */
	int8_t *pcWriteTo;				/* 下一个写的位置 */

	union
	{
		int8_t *pcReadFrom;			/* 队列使用, 指向队列最后, 记录下一个读取的位置 */
		UBaseType_t uxRecursiveCallCount;/* 记录锁被使用的数值(递归调用) */
	} u;

	List_t xTasksWaitingToSend;		/*< 消息阻塞队列 */
	List_t xTasksWaitingToReceive;	/*< 消息接受队列 */

	volatile UBaseType_t uxMessagesWaiting;/*< 当前的消息数量 */
	UBaseType_t uxLength;			/*< 可以存放的消息的个数 */
	UBaseType_t uxItemSize;			/*< 单个消息的大小 */

	volatile int8_t cRxLock;		/* 队列上锁的时候记录这时候插入到队列的接接收任务个数,  不上锁的时候为queueUNLOCKED */
	volatile int8_t cTxLock;		/*< 队列上锁的时候记录这时候插入到队列的接发送任务个数,  不上锁的时候为queueUNLOCKED*/

	#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
		uint8_t ucStaticallyAllocated;	/*< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */
	#endif

	#if ( configUSE_QUEUE_SETS == 1 )
		struct QueueDefinition *pxQueueSetContainer;
	#endif

	#if ( configUSE_TRACE_FACILITY == 1 )
		UBaseType_t uxQueueNumber;
		uint8_t ucQueueType;
	#endif

} xQUEUE;

创建

c
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 );

    if( uxItemSize == ( UBaseType_t ) 0 )
    {
        /* 不会有存储信息的位置 */
        xQueueSizeInBytes = ( size_t ) 0;
    }
    else
    {
        /* 计算需要的空间的大小 */
        xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize );
    }

    pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );

    if( pxNewQueue != NULL )
    {
        /* 计算出来消息的大小 */
        pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t );

        prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
    }
    else
    {
        traceQUEUE_CREATE_FAILED( ucQueueType );
    }

    return pxNewQueue;
}
c
static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue )
{
	/* nin消除 */
	( void ) ucQueueType;

	if( uxItemSize == ( UBaseType_t ) 0 )
	{
		/* 没有数据但是不能设置为0, 因为mutex用了 */
		pxNewQueue->pcHead = ( int8_t * ) pxNewQueue;
	}
	else
	{
		/* Set the head to the start of the queue storage area. */
		pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage;
	}

	/* Initialise the queue members as described where the queue type is
	defined. */
	pxNewQueue->uxLength = uxQueueLength;
	pxNewQueue->uxItemSize = uxItemSize;
	( void ) xQueueGenericReset( pxNewQueue, pdTRUE );

	traceQUEUE_CREATE( pxNewQueue );
}
c
BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue )
{
    Queue_t * const pxQueue = ( Queue_t * ) xQueue;

    configASSERT( pxQueue );

    taskENTER_CRITICAL();
    {
        pxQueue->pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize );
        pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U;
        pxQueue->pcWriteTo = pxQueue->pcHead;
        pxQueue->u.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - ( UBaseType_t ) 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
        {
            /* 之前没有初始化. */
            vListInitialise( &( pxQueue->xTasksWaitingToSend ) );
            vListInitialise( &( pxQueue->xTasksWaitingToReceive ) );
        }
    }
    taskEXIT_CRITICAL();

    return pdPASS;
}
c
BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList )
{
    TCB_t *pxUnblockedTCB;
    BaseType_t xReturn;
	pxUnblockedTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList );
	configASSERT( pxUnblockedTCB );
	( void ) uxListRemove( &( pxUnblockedTCB->xEventListItem ) );

	if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
	{
        //可以进行任务切换
		( void ) uxListRemove( &( pxUnblockedTCB->xStateListItem ) );
		prvAddTaskToReadyList( pxUnblockedTCB );
	}
	else
	{
		/*不可以切换 */
		vListInsertEnd( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) );
	}

	if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority )
	{
		/* 解锁一个高优先级的任务 */
		xReturn = pdTRUE;
		xYieldPending = pdTRUE;
	}
	else
	{
		xReturn = pdFALSE;
	}


	return xReturn;
}

image-20240121175154702

删除

c
void vQueueDelete( QueueHandle_t xQueue )
{
Queue_t * const pxQueue = ( Queue_t * ) xQueue;

	configASSERT( pxQueue );
	traceQUEUE_DELETE( pxQueue );

	#if ( configQUEUE_REGISTRY_SIZE > 0 )
	{
        /* 将消息队列从注册表中删除,我们目前没有添加到注册表中,暂时不用理会*/
		vQueueUnregisterQueue( pxQueue );
	}
	#endif

	#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) )
	{
		/* The queue can only have been allocated dynamically - free it
		again. */
		vPortFree( pxQueue );
	}

}

发送消息

c
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;

    for( ;; )
    {
        taskENTER_CRITICAL();
        {
			//队列没有满, 或者可以覆盖
            if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
            {
                traceQUEUE_SEND( pxQueue );
                //复制数据
                xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );

                /* 有任务在等数据 */
                if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
                {
                    if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
                    {
                        /* 恢复的任务优先级比较高 */
                        queueYIELD_IF_USING_PREEMPTION();
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
                else if( xYieldRequired != pdFALSE )
                {
                    /* 这个是任务有多个互斥锁, 并且返回的顺序不对 */
                    queueYIELD_IF_USING_PREEMPTION();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }

                taskEXIT_CRITICAL();
                return pdPASS;
            }
            else
            {
                //队列是满的
                if( xTicksToWait == ( TickType_t ) 0 )
                {
                    /* 不设置退出的时间,直接退出 */
                    taskEXIT_CRITICAL();
                    traceQUEUE_SEND_FAILED( pxQueue );
                    return errQUEUE_FULL;
                }
                else if( xEntryTimeSet == pdFALSE )
                {
                    /* 初始化超时的结构体 */
                    vTaskInternalSetTimeOutState( &xTimeOut );
                    xEntryTimeSet = pdTRUE;
                }
                else
                {
                    /* 设置过时间了 */
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        }
        taskEXIT_CRITICAL();

        /* Interrupts and other tasks can send to and receive from the queue
		now the critical section has been exited. */

        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();
                }
                //这时候可以是Delay结束或者是被其他任务唤醒
            }
            else
            {
                /* 有空闲的消息空间. 重新回到最上面进行尝试*/
                prvUnlockQueue( pxQueue );
                ( void ) xTaskResumeAll();
            }
        }
        else
        {
            /* The timeout has expired. */
            prvUnlockQueue( pxQueue );
            ( void ) xTaskResumeAll();

            traceQUEUE_SEND_FAILED( pxQueue );
            return errQUEUE_FULL;
        }
    }
}

接收消息

c
BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
{
BaseType_t xEntryTimeSet = pdFALSE;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;


	/* This function relaxes the coding standard somewhat to allow return
	statements within the function itself.  This is done in the interest
	of execution time efficiency. */

	for( ;; )
	{
		taskENTER_CRITICAL();
		{
			const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;

			/* Is there data in the queue now?  To be running the calling task
			must be the highest priority task wanting to access the queue. */
			if( uxMessagesWaiting > ( UBaseType_t ) 0 )
			{
				/* 获取信息*/
				prvCopyDataFromQueue( pxQueue, pvBuffer );
				traceQUEUE_RECEIVE( pxQueue );
				pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1;

				/* 看看有没有等待发送的信息的任务 */
				if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
				{
					if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
					{
						queueYIELD_IF_USING_PREEMPTION();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}

				taskEXIT_CRITICAL();
				return pdPASS;
			}
			else
			{
				if( xTicksToWait == ( TickType_t ) 0 )
				{
					/* 不想等待 */
					taskEXIT_CRITICAL();
					traceQUEUE_RECEIVE_FAILED( pxQueue );
					return errQUEUE_EMPTY;
				}
				else if( xEntryTimeSet == pdFALSE )
				{
					/* The queue was empty and a block time was specified so
					configure the timeout structure. */
					vTaskInternalSetTimeOutState( &xTimeOut );
					xEntryTimeSet = pdTRUE;
				}
				else
				{
					/* Entry time was already set. */
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
		taskEXIT_CRITICAL();

		/* Interrupts and other tasks can send to and receive from the queue
		now the critical section has been exited. */

		vTaskSuspendAll();
		prvLockQueue( pxQueue );

		/* Update the timeout state to see if it has expired yet. */
		if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
		{
			/* The timeout has not expired.  If the queue is still empty place
			the task on the list of tasks waiting to receive from the queue. */
			if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
			{
				traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );
				vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
				prvUnlockQueue( pxQueue );
				if( xTaskResumeAll() == pdFALSE )
				{
					portYIELD_WITHIN_API();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				/* The queue contains data again.  Loop back to try and read the
				data. */
				prvUnlockQueue( pxQueue );
				( void ) xTaskResumeAll();
			}
		}
		else
		{
			/* Timed out.  If there is no data in the queue exit, otherwise loop
			back and attempt to read the data. */
			prvUnlockQueue( pxQueue );
			( void ) xTaskResumeAll();

			if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
			{
				traceQUEUE_RECEIVE_FAILED( pxQueue );
				return errQUEUE_EMPTY;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
	}
}
c
BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
{
BaseType_t xEntryTimeSet = pdFALSE;
TimeOut_t xTimeOut;
int8_t *pcOriginalReadPosition;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;

	for( ;; )
	{
		taskENTER_CRITICAL();
		{
			const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;

			/* Is there data in the queue now?  To be running the calling task
			must be the highest priority task wanting to access the queue. */
			if( uxMessagesWaiting > ( UBaseType_t ) 0 )
			{
				/* 记录数据的位置 */
				pcOriginalReadPosition = pxQueue->u.pcReadFrom;

				prvCopyDataFromQueue( pxQueue, pvBuffer );
				traceQUEUE_PEEK( pxQueue );

				/* 不改变数据的位置 */
				pxQueue->u.pcReadFrom = pcOriginalReadPosition;

				/* 有任务在等数据 */
				if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
				{
					if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
					{
						/* 需要切换 */
						queueYIELD_IF_USING_PREEMPTION();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}

				taskEXIT_CRITICAL();
				return pdPASS;
			}
			else
			{
				if( xTicksToWait == ( TickType_t ) 0 )
				{
					/* 不等待 */
					taskEXIT_CRITICAL();
					traceQUEUE_PEEK_FAILED( pxQueue );
					return errQUEUE_EMPTY;
				}
				else if( xEntryTimeSet == pdFALSE )
				{
					/*   */
					vTaskInternalSetTimeOutState( &xTimeOut );
					xEntryTimeSet = pdTRUE;
				}
				else
				{
					/* Entry time was already set. */
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
		taskEXIT_CRITICAL();

		/* Interrupts and other tasks can send to and receive from the queue
		now the critical section has been exited. */

		vTaskSuspendAll();
		prvLockQueue( pxQueue );

		/* Update the timeout state to see if it has expired yet. */
		if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
		{
			/* 还有时间 */
			if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
			{
				traceBLOCKING_ON_QUEUE_PEEK( pxQueue );
				vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
				prvUnlockQueue( pxQueue );
				if( xTaskResumeAll() == pdFALSE )
				{
					portYIELD_WITHIN_API();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				/* 有位置了, 再来一次 */
				prvUnlockQueue( pxQueue );
				( void ) xTaskResumeAll();
			}
		}
		else
		{
			/* 超时了 */
			prvUnlockQueue( pxQueue );
			( void ) xTaskResumeAll();

			if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
			{
				traceQUEUE_PEEK_FAILED( pxQueue );
				return errQUEUE_EMPTY;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
	}
}

信号量

结构体

c
typedef struct QueueDefinition
{
	int8_t *pcHead;					
	int8_t *pcTail;					
	int8_t *pcWriteTo;			

	union						
	{
		int8_t *pcReadFrom;		
		UBaseType_t uxRecursiveCallCount;
	} u;

	List_t xTasksWaitingToSend;		
	List_t xTasksWaitingToReceive;	

	volatile UBaseType_t uxMessagesWaiting; //记录可以使用的设备的个数
	UBaseType_t uxLength;			//记录最大的可以使用的个数, 二值信号量的时候为0
	UBaseType_t uxItemSize;			//0

	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;

二值信号量

计数信号量

创建

c
QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount )
{
    QueueHandle_t xHandle;

    configASSERT( uxMaxCount != 0 );
    configASSERT( uxInitialCount <= uxMaxCount );

    xHandle = xQueueGenericCreate( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE );

    if( xHandle != NULL )
    {
        ( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount;//设置一个初始值

        traceCREATE_COUNTING_SEMAPHORE();
    }
    else
    {
        traceCREATE_COUNTING_SEMAPHORE_FAILED();
    }

    return xHandle;
}

释放

c
#define xSemaphoreGive( xSemaphore )		xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )

实际上是一个不发发送数据的, 不阻塞的信息

c
if( pxQueue->uxItemSize == ( UBaseType_t ) 0 )
{
    #if ( configUSE_MUTEXES == 1 )
    {
        if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
        {
            /* The mutex is no longer being held. 优先级翻转*/
            xReturn = xTaskPriorityDisinherit( ( void * ) pxQueue->pxMutexHolder );
            pxQueue->pxMutexHolder = NULL;
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    #endif /* configUSE_MUTEXES */
}
pxQueue->uxMessagesWaiting = uxMessagesWaiting + ( UBaseType_t ) 1;

获取

c
BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, TickType_t xTicksToWait )
{
BaseType_t xEntryTimeSet = pdFALSE;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;

#if( configUSE_MUTEXES == 1 )
	BaseType_t xInheritanceOccurred = pdFALSE;
#endif

	/* This function relaxes the coding standard somewhat to allow return
	statements within the function itself.  This is done in the interest
	of execution time efficiency. */

	for( ;; )
	{
		taskENTER_CRITICAL();
		{
			/* 获取可以使用的数量 */
			const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting;

			/* 有可以使用的设备 */
			if( uxSemaphoreCount > ( UBaseType_t ) 0 )
			{
				traceQUEUE_RECEIVE( pxQueue );

				/* 更新数量 */
				pxQueue->uxMessagesWaiting = uxSemaphoreCount - ( UBaseType_t ) 1;

				#if ( configUSE_MUTEXES == 1 )
				{
                    //检测尾变量是不是NULL, 判断是不是MUTEX(有两种)
					if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
					{
                          //这是一个锁
						/* 当前的任务有的锁数量加一, 返回当前的任务, 并记录, Muxtex只有一个使用权 */
						pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount(); 
					}
					else
					{
					}
				}
				#endif /* configUSE_MUTEXES */

				/* 有在等待的任务 */
				if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
				{
					if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
					{
                          //释放的优先级比较高, 切换
						queueYIELD_IF_USING_PREEMPTION();
					}
					else
					{
					}
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}

				taskEXIT_CRITICAL();
				return pdPASS;
			}
			else
			{
                //没有可以使用的设备
				if( xTicksToWait == ( TickType_t ) 0 )
				{
					/* 不等待 */
					#if( configUSE_MUTEXES == 1 )
					{
						configASSERT( xInheritanceOccurred == pdFALSE );
					}
					#endif /* configUSE_MUTEXES */

					/* The semaphore count was 0 and no block time is specified
					(or the block time has expired) so exit now. */
					taskEXIT_CRITICAL();
					return errQUEUE_EMPTY;
				}
				else if( xEntryTimeSet == pdFALSE )
				{
                    //第一次进来, 初始化一下等待的结构体
					/* The semaphore count was 0 and a block time was specified
					so configure the timeout structure ready to block. */
					vTaskInternalSetTimeOutState( &xTimeOut );
					xEntryTimeSet = pdTRUE;
				}
				else
				{
				}
			}
		}
		taskEXIT_CRITICAL();

		/* Interrupts and other tasks can give to and take from the semaphore
		now the critical section has been exited. */

		vTaskSuspendAll();
		prvLockQueue( pxQueue );

		/* Update the timeout state to see if it has expired yet. */
		if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
		{
			//还有等待的时间
			if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
			{
				traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );

				#if ( configUSE_MUTEXES == 1 )
				{
					if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
					{
						taskENTER_CRITICAL();
						{
                            //是一个锁, 进行优先级继承, 记录有没有继承
							xInheritanceOccurred = xTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );
						}
						taskEXIT_CRITICAL();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				#endif

				vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
				prvUnlockQueue( pxQueue );
				if( xTaskResumeAll() == pdFALSE )
				{
					portYIELD_WITHIN_API();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				/* There was no timeout and the semaphore count was not 0, so
				attempt to take the semaphore again. */
				prvUnlockQueue( pxQueue );
				( void ) xTaskResumeAll();
			}
		}
		else
		{
			/* Timed out. */
			prvUnlockQueue( pxQueue );
			( void ) xTaskResumeAll();

			/* If the semaphore count is 0 exit now as the timeout has
			expired.  Otherwise return to attempt to take the semaphore that is
			known to be available.  As semaphores are implemented by queues the
			queue being empty is equivalent to the semaphore count being 0. */
			if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
			{
				#if ( configUSE_MUTEXES == 1 )
				{
					/* xInheritanceOccurred could only have be set if
					pxQueue->uxQueueType == queueQUEUE_IS_MUTEX so no need to
					test the mutex type again to check it is actually a mutex. */
					if( xInheritanceOccurred != pdFALSE )
					{
                        //继承过优先级, 但是现在被降级了
						taskENTER_CRITICAL();
						{
							UBaseType_t uxHighestWaitingPriority;

							/* 记录一下现在等待的任务的最高优先级 */
							uxHighestWaitingPriority = prvGetDisinheritPriorityAfterTimeout( pxQueue );
							vTaskPriorityDisinheritAfterTimeout( ( void * ) pxQueue->pxMutexHolder, uxHighestWaitingPriority );
						}
						taskEXIT_CRITICAL();
					}
				}
				#endif /* configUSE_MUTEXES */

				traceQUEUE_RECEIVE_FAILED( pxQueue );
				return errQUEUE_EMPTY;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
	}
}

优先级继承

c
BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder )
{
    TCB_t * const pxMutexHolderTCB = ( TCB_t * ) pxMutexHolder;
    BaseType_t xReturn = pdFALSE;

    /* 检测这个锁有没有被中断释放 */
    if( pxMutexHolder != NULL )
    {
        /* 需要继承优先级 */
        if( pxMutexHolderTCB->uxPriority < pxCurrentTCB->uxPriority )
        {
            /* 进行调整 */
            if( ( listGET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
            {
                listSET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }

            /* 调整他的列表 */
            if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxMutexHolderTCB->uxPriority ] ), &( pxMutexHolderTCB->xStateListItem ) ) != pdFALSE )
            {
                //在拥有者在Ready队列里面
                if( uxListRemove( &( pxMutexHolderTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
                {
                    taskRESET_READY_PRIORITY( pxMutexHolderTCB->uxPriority );
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }

                /* 继承优先级 */
                pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority;
                prvAddTaskToReadyList( pxMutexHolderTCB );
            }
            else
            {
                //不在Ready的列表里面, 不需要改列表
                /* Just inherit the priority. */
                pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority;
            }

            traceTASK_PRIORITY_INHERIT( pxMutexHolderTCB, pxCurrentTCB->uxPriority );

            /* Inheritance occurred. */
            xReturn = pdTRUE;
        }
        else
        {
            if( pxMutexHolderTCB->uxBasePriority < pxCurrentTCB->uxPriority )
            {
                //基础优先级比较低, 但是现在实际的优先级不低, 不需要继承
                /* The base priority of the mutex holder is lower than the
					priority of the task attempting to take the mutex, but the
					current priority of the mutex holder is not lower than the
					priority of the task attempting to take the mutex.
					Therefore the mutex holder must have already inherited a
					priority, but inheritance would have occurred if that had
					not been the case. */
                xReturn = pdTRUE;
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    return xReturn;
}
c
//返回没有继承的时候的优先级
BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder )
{
    TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;
    BaseType_t xReturn = pdFALSE;

    if( pxMutexHolder != NULL )
    {
        ( pxTCB->uxMutexesHeld )--;

        /* 检测之前有没有继承过优先级 */
        if( pxTCB->uxPriority != pxTCB->uxBasePriority )
        {
            /* 当前没有需要保持优先级的锁了 */
            if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 )
            {
                /* A task can only have an inherited priority if it holds
					the mutex.  If the mutex is held by a task then it cannot be
					given from an interrupt, and if a mutex is given by the
					holding task then it must be the running state task.  Remove
					the holding task from the ready list. */
                if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
                {
                    taskRESET_READY_PRIORITY( pxTCB->uxPriority );
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }

                /* Disinherit the priority before adding the task into the
					new	ready list. */
                traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority );
                pxTCB->uxPriority = pxTCB->uxBasePriority;

                /* Reset the event list item value.  It cannot be in use for
					any other purpose if this task is running, and it must be
					running to give back the mutex. */
                listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
                prvAddTaskToReadyList( pxTCB );

                /* Return true to indicate that a context switch is required.
					This is only actually required in the corner case whereby
					multiple mutexes were held and the mutexes were given back
					in an order different to that in which they were taken.
					If a context switch did not occur when the first mutex was
					returned, even if a task was waiting on it, then a context
					switch should occur when the last mutex is returned whether
					a task is waiting on it or not. */
                xReturn = pdTRUE;
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    return xReturn;
}
c
//一个锁超时没有获得资源,需要获取拥有者现在的等待者里面最高优先级
static UBaseType_t prvGetDisinheritPriorityAfterTimeout( const Queue_t * const pxQueue )
{
    UBaseType_t uxHighestPriorityOfWaitingTasks;

    /* If a task waiting for a mutex causes the mutex holder to inherit a
		priority, but the waiting task times out, then the holder should
		disinherit the priority - but only down to the highest priority of any
		other tasks that are waiting for the same mutex.  For this purpose,
		return the priority of the highest priority task that is waiting for the
		mutex. */
    if( listCURRENT_LIST_LENGTH( &( pxQueue->xTasksWaitingToReceive ) ) > 0 )
    {
        uxHighestPriorityOfWaitingTasks = configMAX_PRIORITIES - listGET_ITEM_VALUE_OF_HEAD_ENTRY( &( pxQueue->xTasksWaitingToReceive ) );
    }
    else
    {
        uxHighestPriorityOfWaitingTasks = tskIDLE_PRIORITY;
    }

    return uxHighestPriorityOfWaitingTasks;
}
c
//一个拥有者在申请者超时以后的处理
void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, UBaseType_t uxHighestPriorityWaitingTask )
{
    TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;
    UBaseType_t uxPriorityUsedOnEntry, uxPriorityToUse;
    const UBaseType_t uxOnlyOneMutexHeld = ( UBaseType_t ) 1;

    if( pxMutexHolder != NULL )
    {

        /* 检测需要改为新的继承等级吗? */
        if( pxTCB->uxBasePriority < uxHighestPriorityWaitingTask )
        {
            uxPriorityToUse = uxHighestPriorityWaitingTask;
        }
        else
        {
            uxPriorityToUse = pxTCB->uxBasePriority;
        }

        /* Does the priority need to change? */
        if( pxTCB->uxPriority != uxPriorityToUse )
        {
            /* 进行了优先级的切换 */
            if( pxTCB->uxMutexesHeld == uxOnlyOneMutexHeld )
            {
                //这时候这一个任务只有一个锁, 不会影响别的锁
                /*  检测这个超时的任务不是要更改的任务 */
                configASSERT( pxTCB != pxCurrentTCB );
				
                uxPriorityUsedOnEntry = pxTCB->uxPriority;
                pxTCB->uxPriority = uxPriorityToUse;

                /* Only reset the event list item value if the value is not
					being used for anything else. */
                if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
                {
                    listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriorityToUse ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
                }

                /* 更换所在的列表 */
                if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE )
                {
                    if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
                    {
                        taskRESET_READY_PRIORITY( pxTCB->uxPriority );
                    }
                    prvAddTaskToReadyList( pxTCB );
                }
            }
        }
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }
}

互斥量

c
typedef struct QueueDefinition /* The old naming convention is used to prevent breaking kernel aware debuggers. */
{
    ...
    union
    {
        QueuePointers_t xQueue;     
        SemaphoreData_t xSemaphore; /*< 记录递归调用的次数 */
    } u;

...

    volatile UBaseType_t uxMessagesWaiting; /*1: 可以使用 0: 被占用了*/
    UBaseType_t uxLength;                   /* 1*/
    UBaseType_t uxItemSize;                 /* 0*/

    ...
} xQUEUE;
c
#define pxMutexHolder					pcTail
#define uxQueueType						pcHead
#define queueQUEUE_IS_MUTEX				NULL

pcTail与pcHead用于指向消息存储区域的, pxMutexHolder就被用于指向持有互斥量的任务控制块,现在初始化的时候,就初始化为NULL,表示没有任务持有互斥量。uxQueueType表示队列的类型,设置为queueQUEUE_IS_MUTEX(NULL),表示的是用作互斥量

c
static void prvInitialiseMutex( Queue_t *pxNewQueue )
{
    if( pxNewQueue != NULL )
    {
        /* The queue create function will set all the queue structure members
			correctly for a generic queue, but this function is creating a
			mutex.  Overwrite those members that need to be set differently -
			in particular the information required for priority inheritance. */
        pxNewQueue->pxMutexHolder = NULL;
        pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;

        /* In case this is a recursive mutex. */
        pxNewQueue->u.uxRecursiveCallCount = 0;

        traceCREATE_MUTEX( pxNewQueue );

        /* Start with the semaphore in the expected state. */
        ( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK );
    }
    else
    {
        traceCREATE_MUTEX_FAILED();
    }
}

递归互斥量

c
BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait )
{
    BaseType_t xReturn;
    Queue_t * const pxMutex = ( Queue_t * ) xMutex;
	//当前任务多次调用
    if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() )
    {
        ( pxMutex->u.uxRecursiveCallCount )++;
        xReturn = pdPASS;
    }
    else
    {
        xReturn = xQueueSemaphoreTake( pxMutex, xTicksToWait );

        /* pdPASS will only be returned if the mutex was successfully
			obtained.  The calling task may have entered the Blocked state
			before reaching here. */
        if( xReturn != pdFAIL )
        {
            ( pxMutex->u.uxRecursiveCallCount )++;
        }
    }

    return xReturn;
}

事件组

c
typedef struct xEventGroupDefinition
{
	EventBits_t uxEventBits;
	List_t xTasksWaitingForBits;		/* 记录等待的任务 */

	#if( configUSE_TRACE_FACILITY == 1 )
		UBaseType_t uxEventGroupNumber;	//用于可视化追踪
	#endif

	#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
		uint8_t ucStaticallyAllocated; //记录是不是静态分配的
	#endif
} EventGroup_t;

设置

image-20240122195201868

c
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )
{
ListItem_t *pxListItem, *pxNext;
ListItem_t const *pxListEnd;
List_t *pxList;
EventBits_t uxBitsToClear = 0, uxBitsWaitedFor, uxControlBits;
EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
BaseType_t xMatchFound = pdFALSE;


	pxList = &( pxEventBits->xTasksWaitingForBits );
	pxListEnd = listGET_END_MARKER( pxList ); 
	vTaskSuspendAll();
	{
		traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet );

		pxListItem = listGET_HEAD_ENTRY( pxList );

		/* Set the bits. */
		pxEventBits->uxEventBits |= uxBitsToSet;

		/* See if the new bit value should unblock any tasks. */
		while( pxListItem != pxListEnd )
		{
			pxNext = listGET_NEXT( pxListItem );
			uxBitsWaitedFor = listGET_LIST_ITEM_VALUE( pxListItem );
			xMatchFound = pdFALSE;

			/* Split the bits waited for from the control bits. */
			uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES;
			uxBitsWaitedFor &= ~eventEVENT_BITS_CONTROL_BYTES;

			if( ( uxControlBits & eventWAIT_FOR_ALL_BITS ) == ( EventBits_t ) 0 )
			{
				/* 有一个bit匹配就可以了 */
				if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) != ( EventBits_t ) 0 )
				{
					xMatchFound = pdTRUE;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) == uxBitsWaitedFor )
			{
				/* 所有的bit都需要匹配. */
				xMatchFound = pdTRUE;
			}

			if( xMatchFound != pdFALSE )
			{
				/* 需要切换 */
				if( ( uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT ) != ( EventBits_t ) 0 )
				{
                    //需要进行bit的清除
					uxBitsToClear |= uxBitsWaitedFor;
				}
				//从列表里面移除, 同时设置标志位
				vTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET );
			}

			/* 获取下一个(这个时候已经改变表的位置了) */
			pxListItem = pxNext;
		}

		/* 如果需要清除的话就进行清除 */
		pxEventBits->uxEventBits &= ~uxBitsToClear;
	}
	( void ) xTaskResumeAll();

	return pxEventBits->uxEventBits;
}
c
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait )
{
EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
EventBits_t uxReturn, uxControlBits = 0;
BaseType_t xWaitConditionMet, xAlreadyYielded;
BaseType_t xTimeoutOccurred = pdFALSE;

	vTaskSuspendAll();
	{
		const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits;

		/* 检测是不是匹配得上 */
		xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits );

		if( xWaitConditionMet != pdFALSE )
		{
			/* 已经满足了 */
			uxReturn = uxCurrentEventBits;
			xTicksToWait = ( TickType_t ) 0;

			/* 清空对应的标志位o. */
			if( xClearOnExit != pdFALSE )
			{
				pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
			}
		}
		else if( xTicksToWait == ( TickType_t ) 0 )
		{
			/* 不等待, 直接退出 */
			uxReturn = uxCurrentEventBits;
			xTimeoutOccurred = pdTRUE;
		}
		else
		{
			/* 记录一下条件 */
			if( xClearOnExit != pdFALSE )
			{
				uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT;
			}

			if( xWaitForAllBits != pdFALSE )
			{
				uxControlBits |= eventWAIT_FOR_ALL_BITS;
			}

			/* 把这个加入到Delay和事件的等待队列里面, 同时使用EventList的参数保存一下当前状态 */
			vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait );
			
			uxReturn = 0;

			traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor );
		}
	}
	xAlreadyYielded = xTaskResumeAll();

	if( xTicksToWait != ( TickType_t ) 0 )
	{
		if( xAlreadyYielded == pdFALSE )
		{
			portYIELD_WITHIN_API();
		}
		/* 恢复之前用来记录的vEventList的值, 返回的是记录的事件的值 */
        uxReturn = uxTaskResetEventItemValue();
		if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
		{
            //超时了
			taskENTER_CRITICAL();
			{
				uxReturn = pxEventBits->uxEventBits;

				/* 最后检测一下是不是达标了 */
				if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE )
				{
					if( xClearOnExit != pdFALSE )
					{
						pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
					}
				}
				xTimeoutOccurred = pdTRUE;
			}
			taskEXIT_CRITICAL();
		}

		/* 记录所有的标志位 */
		uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
	}

	return uxReturn;
}