---
title: rtos-freertos-07-队列
EntryName : rtos-freertos-07
date: 2020-06-23 09:43:28
categories:
tags:
- ipc
- queue
- freertos
---

章节概述:

介绍 FreeRTOS中的任务间通讯机制:队列的使用(尽管FreeRTOS中没有进程的概念,但为了统一,我们还是以进程间通讯(IPC)的说法)

介绍

队列是主要的任务间通讯方式。可以在任务与任务间、中断和任务间传送信息。

大多数情况下,队列用于具有线程保护的FIFO(先进先出)缓冲区。

特性

FreeRTOS队列具有以下特性:

  • 通过拷贝传递数据

当信息的大小到达一个临界点后,逐字节拷贝整个信息是不实际的,可以定义一个指针队列,只拷贝指向消息的指针来代替整个信息拷贝。

FreeRTOS+UDP IP栈例程正是使用这种方法向FreeRTOS协议栈传递大量网络数据的。

但我们还可以通过定义结构体,并在其中使用指针:

  • 通过定义保存一个结构体变量的队列实现:变长消息(一个成员保存缓存数据的大小;另一个成员指向要入队的缓存)

  • 通过定义保存一个结构体变量的队列实现:单个队列可以接收不同类型信息,并且信息可以来自不同的位置。(一个成员保存信息类型;另一个成员保存信息数据或者指向信息数据的指针)。数据如何解读取决于信息类型。

管理FreeRTOS+UDP IP栈的任务正是使用单个队列接收ARP定时器时间通知、以太网硬件传送来的数据包、从应用层传送来的数据包、网络关闭事件等等。

  • 队列内存区域分配由内核完成,我们不需要关心数据如何出入队。
  • 天生适用于那些内存保护(MPU)场合。一个具有内存区域保护的任务可以向另一个具有内存区域保护的任务传递数据,因为调用队列发送函数会引起RTOS提升微控制器特权级别。只有RTOS(具有所有特权)才可以访问队列存储区域。
  • 在中断函数中使用独立的API。将RTOS任务API和中断服务例程API分来实现意味着可以避免执行时的上下文调用检查开销,还意味着在大多数情况下,与其它RTOS产品相比,用户创建中断服务例程会更简单。

基本用法

队列的基本用法:

  • 定义一个队列句柄变量,用于保存创建的队列:xQueueHandle xQueue1;
  • 使用API函数xQueueCreate()创建一个队列。
  • 如果希望使用先进先出队列,使用API函数xQueueSend()向队列投递队列项。如果希望使用后进先出队列,使用API函数xQueueSendToFront()向队列投递队列项。
  • 使用API函数xQueueReceive()从队列读取队列项。

如果在中断服务程序中,切记使用它们的带中断保护版本。

基本操作

创建队列

QueueHandle_t xQueueCreate (UBaseType_t uxQueueLength, UBaseType_t uxItemSize);

描述:创建新队列。为新队列分配指定的存储空间并返回队列句柄。

参数解析:

  • usQueueLength:队列项数目

  • uxItemSize:每个队列项大小,单位是字节。队列项通过拷贝入队而不是通过引用入队,因此需要队列项的大小。每个队列项的大小必须相同。

返回值:成功创建队列返回队列句柄,否则返回0。

例子:

struct AMessage
{
portCHAR ucMessageID;
portCHAR ucData[ 20 ];
};
void vATask( void*pvParameters )
{
xQueueHandle xQueue1, xQueue2;
// 创建一个队列,队列能包含10个unsigned long类型的值。
xQueue1 = xQueueCreate( 10, sizeof( unsigned portLONG ));
if( xQueue1 ==0 )
{
// 队列创建失败,不可以使用
}
// 创建一个队列,队列能包含10个 Amessage结构体指针类型的值。
// 这样可以通过传递指针变量来包含大量数据。
xQueue2 =xQueueCreate( 10, sizeof( struct AMessage * ) );
if( xQueue2 ==0 )
{
// 队列创建失败,不可以使用
}
// ... 任务的其它代码.
}

注意:创建完成句柄以后,此后对队列的操作函数都需要使用QueueHandle_t xQueue,篇幅有限,会对此参数进行省略。

删除队列

void vQueueDelete( QueueHandle_t xQueue );

描述:删除队列并释放所有分配给队列的内存。

复位队列

BaseType_t xQueueReset( QueueHandle_t xQueue );

描述:将队列复位到初始状态。

获取信息

获取队列入队信息数目

UBaseType_t uxQueueMessagesWaiting( QueueHandle_t xQueue );

// 具有中断保护的版本:
UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue )。

描述:获取队列中存储的信息数目。

获取队列的空闲数目

UBaseType_t uxQueueSpacesAvailable( QueueHandle_t xQueue );

描述:获取队列的空闲数目。

查询队列是否为空

BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue );

描述:查询队列是否为空。这个函数仅用于ISR。

返回值:队列非空返回pdFALSE,其它值表示队列为空。

查询队列是否满

描述:查询队列是否满,仅用于ISR。

BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue )

返回值:没有满返回pdFALSE;其它值表示队列满。

数据传输

向队列投递一个值

BaseType_t xQueueSend(QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait ); // 中断保护的版本
BaseType_t xQueueSendFromISR(
QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken);

描述:向队列尾部投递一个队列项。

参数解析:

  • pvItemToQueue:指针,指向要入队的项目。要保存到队列中的项目字节数在队列创建时就已确定。因此要从指针pvItemToQueue指向的区域拷贝到队列存储区域的字节数,也已确定。
  • xTicksToWait:如果队列满,任务等待队列空闲的最大时间。如果队列满并且xTicksToWait被设置成0,函数立刻返回。时间单位为系统节拍时钟周期,因此宏portTICK_PERIOD_MS可以用来辅助计算真实延时值。如果INCLUDE_vTaskSuspend设置成1,并且指定延时为portMAX_DELAY将引起任务无限阻塞(没有超时)。
  • pxHigherPriorityTaskWoken:如果入队导致一个任务解锁,并且解锁的任务优先级高于当前运行的任务,则该函数将*pxHigherPriorityTaskWoken设置成pdTRUE。如果xQueueSendFromISR()设置这个值为pdTRUE,则中断退出前需要一次上下文切换。从FreeRTOS V7.3.0起, pxHigherPriorityTaskWoken 作为可选参数,并可以设置为NULL。

返回值:队列项入队成功返回pdTRUE,否则返回errQUEUE_FULL。

例子:

struct AMessage
{
portCHAR ucMessageID;
portCHAR ucData[ 20 ];
}xMessage;
unsigned portLONG ulVar = 10UL;
void vATask( void *pvParameters )
{
xQueueHandle xQueue1, xQueue2;
struct AMessage *pxMessage;
/*创建一个队列,队列能包含10个unsigned long类型的值。*/
xQueue1 = xQueueCreate( 10, sizeof( unsigned portLONG ) );
/* 创建一个队列,队列能包含10个 Amessage结构体指针类型的值。
这样可以通过传递指针变量来包含大量数据。*/
xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
// ...
if( xQueue1 != 0 )
{
/*1个unsigned long型数据入队.如果需要等待队列空间变的有效,
会最多等待10个系统节拍周期*/
if( xQueueSend( xQueue1, ( void * ) &ulVar, ( portTickType ) 10 ) !=pdPASS )
{
/*消息入队失败*/
}
}
if( xQueue2 != 0 )
{
/* 发送一个指向结构体Amessage的对象,如果队列满也不等待 */
pxMessage = & xMessage;
xQueueSend( xQueue2, ( void * ) &pxMessage, ( portTickType ) 0 );
}
//... 任务其余代码.
} // xQueueSendFromISR 例子
void vBufferISR( void )
{
portCHARcIn;
portBASE_TYPE xHigherPriorityTaskWoken; /* 初始化,没有唤醒任务*/
xHigherPriorityTaskWoken = pdFALSE; /* 直到缓冲区为空 */
do
{
/* 从缓冲区获得一个字节数据 */
cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS ); /* 投递这个数据 */
xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
}while( portINPUT_BYTE( BUFFER_COUNT ) ); /* 这里缓冲区已空,如果需要进行一个上下文切换*/
/*根据不同移植平台,这个函数也不同*/
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

向队尾投递一个值

BaseType_t xQueueSendToBack(QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait ); // 中断保护的版本
BaseType_t xQueueSendToBackFromISR (QueueHandle_t xQueue,
const void *pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken );

描述:向队列尾投递一个队列项。

参数解析:xQueueSendxQueueSendFromISR

返回值:xQueueSend

向队首投递一个值

BaseType_t xQueueSendToFront(QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait); // 有中断保护
BaseType_t xQueueSendToFrontFromISR (QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken);

描述:向队列首部投递一个队列项。

参数解析:

  • pvItemToQueue:指针,指向要入队的项目。要保存到队列中的项目字节数在队列创建时就已确定。因此要从指针pvItemToQueue指向的区域拷贝到队列存储区域的字节数,也已确定。
  • xTicksToWait:如果队列满,任务等待队列空闲的最大时间。如果队列满并且xTicksToWait被设置成0,函数立刻返回。时间单位为系统节拍时钟周期,因此宏portTICK_PERIOD_MS可以用来辅助计算真实延时值。如果INCLUDE_vTaskSuspend设置成1,并且指定延时为portMAX_DELAY将引起任务无限阻塞(没有超时)。
  • pxHigherPriorityTaskWoken:如果入队导致一个任务解锁,并且解锁的任务优先级高于当前运行的任务,则该函数将*pxHigherPriorityTaskWoken设置成pdTRUE。如果xQueueSendFromISR()设置这个值为pdTRUE,则中断退出前需要一次上下文切换。从FreeRTOS V7.3.0起,pxHigherPriorityTaskWoken称为一个可选参数,并可以设置为NULL。

返回值:队列项入队成功返回pdTRUE,否则返回errQUEUE_FULL。

从队列中读取一个值

BaseType_t xQueueReceive(QueueHandle_t xQueue,
void *pvBuffer,
TickType_t xTicksToWait); // 带有中断保护的版本
BaseType_t xQueueReceiveFromISR (QueueHandle_t xQueue,
void *pvBuffer,
BaseType_t *pxHigherPriorityTaskWoken);

描述:从队列中读取一个队列项并把该队列项从队列中删除。

参数解析:

  • pvBuffer:指向一个缓冲区,用于拷贝接收到的列表项。必须提供足够大的缓冲区以便容纳队列项。
  • xTicksToWait:要接收的项目队列为空时,允许任务最大阻塞时间。如果设置该参数为0,则表示即队列为空也立即返回。阻塞时间的单位是系统节拍周期,宏portTICK_RATE_MS可辅助计算真实阻塞时间。如果INCLUDE_vTaskSuspend设置成1,并且阻塞时间设置成portMAX_DELAY,将会引起任务无限阻塞(不会有超时)。
  • pxHigherPriorityTaskWoken:如果入队导致一个任务解锁,并且解锁的任务优先级高于当前运行的任务,则该函数将*pxHigherPriorityTaskWoken设置成pdTRUE。如果xQueueSendFromISR()设置这个值为pdTRUE,则中断退出前需要一次上下文切换。从FreeRTOS V7.3.0起,pxHigherPriorityTaskWoken称为一个可选参数,并可以设置为NULL。

返回值:成功接收到列表项返回pdTRUE,否则返回pdFALSE。

例子:

struct AMessage
{
portCHAR ucMessageID;
portCHAR ucData[ 20 ];
} xMessage; xQueueHandle xQueue; // 创建一个队列并投递一个值
void vATask( void *pvParameters )
{
struct AMessage *pxMessage; // 创建一个队列,队列能包含10个 Amessage结构体指针类型的值。
// 这样可以通过传递指针变量来包含大量数据。
xQueue =xQueueCreate( 10, sizeof( struct AMessage * ) );
if( xQueue == 0)
{
// 创建队列失败
}
// ...
// 向队列发送一个指向结构体对象Amessage的指针,如果队列满不等待
pxMessage = & xMessage;
xQueueSend(xQueue, ( void * ) &pxMessage, ( portTickType ) 0 );
// ... 其它代码
} // 该任务从队列中接收一个队列项
voidvADifferentTask( void *pvParameters )
{
struct AMessage *pxRxedMessage; if( xQueue != 0)
{
// 从创建的队列中接收一个消息,如果消息无效,最多阻塞10个系统节拍周期
if(xQueueReceive( xQueue, &( pxRxedMessage ), ( portTickType ) 10 ) )
{
// 现在pcRxedMessage 指向由vATask任务投递进来的结构体Amessage变量
}
}
// ... 其它代码
} // xQueueReceiveFromISR 例程 /* 该函数创建一个队列并投递一些值 */
voidvAFunction( void *pvParameters )
{
portCHAR cValueToPost;
const portTickType xBlockTime = (portTickType )0xff; /*创建一个队列,可以容纳10个portCHAR型变量 */
xQueue = xQueueCreate( 10, sizeof( portCHAR ) );
if( xQueue == 0 )
{
/* 队列创建失败 */
}
/*…... */
/* 投递一些字符,在ISR中使用。如果队列满,任务将会阻塞xBlockTime 个系统节拍周期 */
cValueToPost = 'a';
xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
cValueToPost = 'b';
xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
/*... 继续投递字符 ... 当队列满时,这个任务会阻塞*/
cValueToPost = 'c';
xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
} /* ISR:输出从队列接收到的所有字符 */
voidvISR_Routine( void )
{
portBASE_TYPE xTaskWokenByReceive = pdFALSE;
portCHAR cRxedChar; while( xQueueReceiveFromISR( xQueue, ( void *) &cRxedChar, &xTaskWokenByReceive) )
{
/* 接收到一个字符串,输出.*/
vOutputCharacter( cRxedChar );
/* 如果从队列移除一个字符串后唤醒了向此队列投递字符的任务,那么参数xTaskWokenByReceive将会设置成pdTRUE,这个循环无论重复多少次,仅会
有一个任务被唤醒。*/
}
/*这里缓冲区已空,如果需要进行一个上下文切换根据不同移植平台,这个函数也不同 */
portYIELD_FROM_ISR(xTaskWokenByReceive);
}

读取但不移除队列项

BaseType_t xQueuePeek(QueueHandle_t xQueue,
void *pvBuffer,
TickType_t xTicksToWait); // 带有中断保护的版本xQueuePeekFromIS()来完成相同功能。
BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue, void *pvBuffer,);

描述:从队列中读取一个队列项,但不会把该队列项从队列中移除。

参数解析:同xQueueReceive()。

返回值:成功接收到列表项返回pdTRUE,否则返回pdFALSE。

向队尾覆盖投递队列项

BaseType_t xQueueOverwrite(QueueHandle_t xQueue,
const void * pvItemToQueue); //带有中断保护的版本
BaseType_t xQueueOverwriteFromISR (QueueHandle_t xQueue,
const void * pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken);

描述:向队列尾投递一个队列项,如果队列已满,则覆盖之前的队列项。

一般用于只有一个队列项的队列中,如果队列的队列项超过1个,使用这个宏会触发一个断言(已经正确定义configASSERT()的情况下)。

参数解析:

  • pvItemToQueue:指针,指向要入队的项目。要保存到队列中的项目字节数在队列创建时就已确定。因此要从指针pvItemToQueue指向的区域拷贝到队列存储区域的字节数,也已确定。
  • pxHigherPriorityTaskWoken:如果入队导致一个任务解锁,并且解锁的任务优先级高于当前运行的任务,则该函数将*pxHigherPriorityTaskWoken设置成pdTRUE。如果xQueueSendFromISR()设置这个值为pdTRUE,则中断退出前需要一次上下文切换。从FreeRTOS V7.3.0起,pxHigherPriorityTaskWoken称为一个可选参数,并可以设置为NULL。

例子:

void vFunction( void *pvParameters )
{
QueueHandle_t xQueue;
unsigned long ulVarToSend, ulValReceived; /*创建队列,保存一个unsignedlong值。如果一个队列的队列项超过1个,强烈建议不要使用xQueueOverwrite(),如果使用xQueueOverwrite()会触发一个断言(已经正确定义configASSERT()的情况下)。*/
xQueue = xQueueCreate( 1, sizeof( unsigned long ) ); /*使用 xQueueOverwrite().向队列写入10*/
ulVarToSend = 10;
xQueueOverwrite( xQueue, &ulVarToSend ); /*从队列读取值,但是不把这个值从队列中删除。*/
ulValReceived = 0;
xQueuePeek( xQueue, &ulValReceived, 0 ); if( ulValReceived != 10 )
{
/* 处理错误*/
} /*到这里队列仍是满的。使用xQueueOverwrite()覆写队列,写入值100 */
ulVarToSend = 100;
xQueueOverwrite( xQueue, &ulVarToSend ); /* 从队列中读取值*/
xQueueReceive( xQueue, &ulValReceived, 0 ); if( ulValReceived != 100 )
{
/*处理错误 */
} /* ... */
}

调试

仅当你想使用可视化调试内核时,才进行队列和信号量注册。

宏configQUEUE_REGISTRY_SIZE定义了可以注册的队列和信号量的最大数量。

队列注册有两个目的,这两个目的都是为了调试RTOS内核:

  • 它允许队列具有一个相关的文本名字,在GUI调试中可以容易的标识队列;
  • 结合调试器用于定位每一个已经注册的队列和信号量时所需的信息。

队列注册

void vQueueAddToRegistry(QueueHandle_t xQueue, char *pcQueueName,);

描述:为队列分配名字并进行注册。

例子:

void vAFunction( void )
{
xQueueHandle xQueue; /*创建一个队列,可以容纳10个char类型数值 */
xQueue = xQueueCreate( 10, sizeof( portCHAR ) ); /* 我们想可视化调试,所以注册它*/
vQueueAddToRegistry( xQueue, "AMeaningfulName" );
}

解除注册

void vQueueUnregisterQueue(QueueHandle_t xQueue);

描述:从队列注册表中移除指定的队列。

Freertos学习:07-队列的更多相关文章

  1. FreeRTOS学习笔记——任务间使用队列同步数据

    1.前言 在嵌入式操作系统中队列是任务间数据交换的常用手段,队列是生产者消费者模型的重要组成部分.FreeRTOS的队列简单易用,下面结合一个具体例子说明FreeRTOS中的队列如何使用. 2.参考代 ...

  2. 【FreeRTOS学习04】小白都能懂的 Queue Management 消息队列使用详解

    消息队列作为任务间同步扮演着必不可少的角色: 相关文章 [FreeRTOS实战汇总]小白博主的RTOS学习实战快速进阶之路(持续更新) 文章目录 相关文章 1 前言 2 xQUEUE 3 相关概念 3 ...

  3. Java虚拟机JVM学习07 类的卸载机制

    Java虚拟机JVM学习07 类的卸载机制 类的生命周期 当Sample类被加载.连接和初始化后,它的生命周期就开始了. 当代表Sample类的Class对象不再被引用,即不可触及时,Class对象就 ...

  4. ThinkPhp学习07

    原文:ThinkPhp学习07 简单CRUD操作 public function show() { $m=M('User'); // $arr=$m->find(2); //查找id=2的数据, ...

  5. 【FreeRTOS学习05】深度解剖FreeRTOSConfig.h实现对系统的自定义剪裁

    ROM/RAM太小,因此要对系统进行剪裁: 相关文章 [FreeRTOS实战汇总]小白博主的RTOS学习实战快速进阶之路(持续更新) 文章目录 相关文章 1 系统的剪裁 2 FreeRTOSConfi ...

  6. 深度学习-07(图像分类、常用数据集、利用CNN实现图像分类、图像分类优化)

    文章目录 深度学习-07(PaddlePaddle图像分类) 图像分类概述 概述 什么是图像分类 图像分类粒度 图像分类发展历程 图像分类问题的挑战 常用数据集介绍 MNIST数据集 CIFAR10数 ...

  7. FreeRTOS学习笔记5:队列

    为通信而准备 1.任务存储存储缓冲机制:先进先出 FIFO 后进先出 LIFO (ucOS消息队列采用的是引用传递,传递的都是指针.采用引用的话,内容必须一致保持可见性,即消息内容必须有效.1.不能传 ...

  8. FREERTOS学习笔记

    2012-02-25 21:43:40 为提升自己对实时操作系统(RTOS)的认识,我学习了freeRTOS. 理解了OS任务的状态.优先级的概念.信号量的概念.互斥的概念.队列.内存管理.这都是和R ...

  9. 【FreeRTOS学习02】源码结构/数据类型/命名规则总结

    个人不是很喜欢FreeRTOS的编程风格,但是没办法,白嫖人家的东西,只能忍了,这里先简单总结一下: 相关文章 [FreeRTOS实战汇总]小白博主的RTOS学习实战快速进阶之路(持续更新) 文章目录 ...

  10. 【FreeRTOS学习01】CubeIDE快速整合FreeRTOS创建第一个任务

    整个专栏主要是博主结合自身对FreeRTOS的实战学习以及源码分析,基于STM32F767 Nucleo-144平台,在CubeIDE下进行开发,结合官方的HAL库,将硬件环节的问题减少到最小,将精力 ...

随机推荐

  1. C语言程序设计-笔记7-指针

    C语言程序设计-笔记7-指针 例8-1  利用指针模拟密码开锁游戏. #include<stdio.h> int main(void) { int x=5342;          //变 ...

  2. 还在用Jenkins?快来试试这款比Jenkins简而轻的自动部署软件!

    大家好,我是 Java陈序员. 在工作中,你是否遇到过团队中没有专业的运维,开发还要做运维的活,需要自己手动构建.部署项目? 不同的项目还有不同的部署命令,需要使用 SSH 工具连接远程服务器和使用 ...

  3. 11.17~11.18暨noip2023游寄

    11.17 我们DZ不负众望又干了点nt事,但是为了按时间顺序记叙,所以说放到最后再讲 上午 平常的起床+吃饭,然后就发手机啥的,坐大巴去德州东再坐会高铁去秦皇岛,这些简单记一下就行了 重点来了 先拜 ...

  4. LLM基础能力实现-书生浦语大模型实战营学习笔记2&大语言模型4

    大语言模型-4.LLM基础能力实现 书生浦语大模型实战营学习笔记-2.LLM基础能力实现 本文包括第二期实战营的第2课内容.本来是想在笔记中给官方教程做做补充的,没想到官方教程的质量还是相当高的,跟着 ...

  5. rails 上传文件

    控制器文件 app/controllers/api/v1/order_controller.rb def create # 从本地读取 log_dir = File.expand_path(File. ...

  6. GOLANG-配置nginx反向代理端口 配置域名

    目录 配置/etc/nginx/nginx.conf文件 新建/etc/nginx/conf.d/doc.haimait.conf文件 重启nginx服务 解析自己的域名到服务器的公网ip 配置/et ...

  7. SQL函数详解SUM\COUNT\AVG......

    朋友们,个人公众号:SQL数据库运维 移动端的学习分享,各种数据库基础知识,一起进步,共同学习,期待你的加入. 函数的类型 1.聚合函数:对一组值执行计算,并返回单个值,也被称为组函数.聚合函数经常与 ...

  8. kettle使用4-使用Pan.bat执行转换、Kitchen.bat执行作业

    一.直接在spoon中执行作业 使用bat文件执行速度比执行在spoon.bat中执行慢很多,如果少数几个任务,可以直接在spoon中执行. 1.新建作业 2.在通用中,新建START 任务执行的时间 ...

  9. 一次glide内存泄漏排查分析

    glide是一款非常优秀的图片加载框架,目前很多项目在使用.提供了非常方法,在此,笔者就不一一列举了,可以到官网查找. 目前项目在做内存排查,因为是车机项目,之前开发的时候没有注意内存方面的问题(车机 ...

  10. 2023年Clion插件推荐

    目录 搜素位置 插件 background-image plus 背景图片插件 Rainbow Brackets 彩虹括号 Xcode-Dark Theme 界面主题 Grep Console 日志颜 ...