1.简介
在使用队列进行任务之间的“沟通交流”时,一个队列只允许任务间传递的消息为同一种数据类型,如果需要在任务间传递不同数据类型的消息时,那么就可以使用队列集。FreeRTOS提供的队列集功能可以对多个队列进行“监听”,只要被监听的队列中有一个队列有有效的消息,那么队列集的读取任务都可以读取到消息,如果读取任务因读取队列集而被阻塞,那么队列集将解除读取任务的阻塞。使用队列集的好处在于,队列集可以是的任务可以读取多个队列中的消息,而无需遍历所有待读取的队列,以确定具体读取哪一个队列。
使用队列集功能,需要在 FreeRTOSConfig.h 文件中将配置项 configUSE_QUEUE_SETS 配
 置为 1,来启用队列集功能。
 
2.相关API函数

1. 函数 xQueueCreateSet()
此函数用于创建队列集,该函数在 queue.c 文件中有定义,函数的原型如下所示:
QueueSetHandle_t xQueueCreateSet(const UBaseType_t uxEventQueueLength);
 

QueueSetHandle_t xQueueCreateSet(const UBaseType_t uxEventQueueLength)
{
 QueueSetHandle_t pxQueue;
 
 /* 创建一个队列作为队列集
 * 队列长度为队列集可容纳的队列数量
 * 队列项目的大小为队列控制块的大小
 * 队列的类型为队列集
 */
 pxQueue = xQueueGenericCreate( uxEventQueueLength,
 ( UBaseType_t ) sizeof( Queue_t * ),
 queueQUEUE_TYPE_SET );
 
 return pxQueue;
}
 
2. 函数 xQueueAddToSet()
此函数用于往队列集中添加队列,要注意的时,队列在被添加到队列集之前,队列中不能
 有有效的消息,该函数在 queue.c 文件中有定义,函数的原型如下所示:
BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore,
 QueueSetHandle_t xQueueSet);
 

 函数 xQueueAddToSet()的具体代码如下所示:
BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore,
QueueSetHandle_t xQueueSet)
{
 BaseType_t xReturn;
 
 /* 进入临界区 */
 taskENTER_CRITICAL();
 {
 if( ( ( Queue_t * ) xQueueOrSemaphore )->pxQueueSetContainer != NULL )
 {
 xReturn = pdFAIL;
 }
 /* 队列中要求没有有效消息 */
 else if( ( ( Queue_t * ) xQueueOrSemaphore )->uxMessagesWaiting !=
 ( UBaseType_t ) 0 )
 {
 xReturn = pdFAIL;
 }
 else
 {
 /* 将队列所在队列集设为队列集 */
 ( ( Queue_t * ) xQueueOrSemaphore )->pxQueueSetContainer =
 xQueueSet;
 xReturn = pdPASS;
 }
 }
 /* 退出临界区 */
 taskEXIT_CRITICAL();
 
 return xReturn;
}
 
3. 函数 xQueueRemoveFromSet()
此函数用于从队列集中移除队列,要注意的时,队列在从队列集移除之前,必须没有有效
 的消息,该函数在 queue.c 文件中有定义,函数的原型如下所示:
BaseType_t xQueueRemoveFromSet(QueueSetMemberHandle_t xQueueOrSemaphore,
 QueueSetHandle_t xQueueSet);
 

 
 函数 xQueueRemoveFromSet()的具体代码如下所示:
BaseType_t xQueueRemoveFromSet(QueueSetMemberHandle_t xQueueOrSemaphore,
 QueueSetHandle_t xQueueSet)
{
 BaseType_t xReturn;
 Queue_t * const pxQueueOrSemaphore = ( Queue_t * ) xQueueOrSemaphore;
 
 /* 队列需在队列集中,才能移除 */
 if( pxQueueOrSemaphore->pxQueueSetContainer != xQueueSet )
 {
 xReturn = pdFAIL;
 }
 /* 队列中没有有效消息时,才能移除 */
 else if( pxQueueOrSemaphore->uxMessagesWaiting != ( UBaseType_t ) 0 )
 {
 xReturn = pdFAIL;
 }
 else
 {
 /* 进入临界区 */
 taskENTER_CRITICAL();
 {
 /* 将队列所在队列集设为空 */
 pxQueueOrSemaphore->pxQueueSetContainer = NULL;
 }
 /* 退出临界区 */
 taskEXIT_CRITICAL();
 xReturn = pdPASS;
 }
 
 return xReturn;
}
 
4. 函数 xQueueSelectFromSet()
此函数用于在任务中获取队列集中有有效消息的队列,该函数在 queue.c 文件中有定义,函
 数的原型如下所示:
QueueSetMemberHandle_t xQueueSelectFromSet(
 QueueSetHandle_t xQueueSet,
 TickType_t const xTicksToWait);
 

 
 函数 xQueueSelectFromSet()的具体代码如下所示:
QueueSetMemberHandle_t xQueueSelectFromSet(
 QueueSetHandle_t xQueueSet,
 TickType_t const xTicksToWait)
{
 QueueSetMemberHandle_t xReturn = NULL;
 
 /* 读取队列集的消息
 * 读取到的消息,
 * 即为队列集中有空闲消息的队列
 */
 ( void ) xQueueReceive( ( QueueHandle_t ) xQueueSet,
 &xReturn,
 xTicksToWait);
 return xReturn;
}
 
5. 函数 xQueueSelectFromSetFromISR()
此函数用于在中断中获取队列集中有有效消息的队列,该函数在 queue.c 文件中有定义,函
 数的原型如下所示:
QueueSetMemberHandle_t xQueueSelectFromSetFromISR(
 QueueSetHandle_t xQueueSet );
 

QueueSetMemberHandle_t xQueueSelectFromSetFromISR(
 QueueSetHandle_t xQueueSet )
{
 QueueSetMemberHandle_t xReturn = NULL;
 /* 在中断中读取队列集的消息
 * 读取到的消息,
 * 即为队列集中有空闲消息的队列
 */
 ( void ) xQueueReceiveFromISR( ( QueueHandle_t ) xQueueSet,
 &xReturn,
 NULL);
 return xReturn;
}
 
3.相关实验

#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );
/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );
/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );
/******************************************************************************************************/
QueueSetHandle_t queueset_handle;
QueueHandle_t    queue_handle;
QueueHandle_t    semphr_handle;
/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}
void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();                               /* 进入临界区 */
    queueset_handle = xQueueCreateSet( 2 );             /* 创建队列集,可以存放2个队列 */
    if(queueset_handle != NULL)
    {
        printf("队列集创建成功!!\r\n");
    }
    
    queue_handle = xQueueCreate( 1, sizeof(uint8_t) );  /* 创建队列 */ 
    semphr_handle = xSemaphoreCreateBinary();           /* 创建二值信号量 */
    
    xQueueAddToSet( queue_handle,queueset_handle);
    xQueueAddToSet( semphr_handle,queueset_handle);
    
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
                             
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
}
/* 任务一,实现队列发送以及信号量释放 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    BaseType_t err = 0;
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            err = xQueueSend( queue_handle, &key, portMAX_DELAY );
            if(err == pdPASS)
            {
                printf("往队列queue_handle写入数据成功!!\r\n");
            }
        }else if(key == KEY1_PRES)
        {
            err = xSemaphoreGive(semphr_handle);
            if(err == pdPASS)
            {
                printf("释放信号量成功!!\r\n");
            }
        }
        vTaskDelay(10);
    }
}
/* 任务二,获取队列集的消息 */
void task2( void * pvParameters )
{
    QueueSetMemberHandle_t member_handle;
    uint8_t key;
    while(1)
    {
        member_handle = xQueueSelectFromSet( queueset_handle,portMAX_DELAY);
        if(member_handle == queue_handle)
        {
            xQueueReceive( member_handle,&key,portMAX_DELAY);
            printf("获取到的队列数据为:%d\r\n",key);
        }else if(member_handle == semphr_handle)
        {
            xSemaphoreTake( member_handle, portMAX_DELAY );
            printf("获取信号量成功!!\r\n");
        }
    }
}
                


![[NKCTF2024]-PWN:leak解析(中国剩余定理泄露libc地址,汇编覆盖返回地址)](https://img-blog.csdnimg.cn/direct/de3fdfbaf62b403b9833ad27d045f2d8.png)















