【FreeRTOS】同步互斥与通信 有缺陷的同步示例

news2024/11/18 22:25:32

目录

  • 1 同步互斥与通信
    • 1.1 同步互斥与通信概述
    • 1.2 同步与互斥的概念
    • 1.3 同步的例子:有缺陷
    • 1.4 freertos.c源码
      • 3. 互斥的例子:有缺陷
      • 4. 通信的例子:有缺陷
      • 5. FreeRTOS的解决方案


1 同步互斥与通信

1.1 同步互斥与通信概述

参考《FreeRTOS入门与工程实践(基于DshanMCU-103)》里《第10章 同步互斥与通信》

本章是概述性的内容。可以把多任务系统当做一个团队,里面的每一个任务就相当于团队里的一个人。团队成员之间要协调工作进度(同步)、争用会议室(互斥)、沟通(通信)。多任务系统中所涉及的概念,都可以在现实生活中找到例子。

各类RTOS都会涉及这些概念:任务通知(task notification)、队列(queue)、事件组(event group)、信号量(semaphoe)、互斥量(mutex)等。我们先站在更高角度来讲解这些概念。

1.2 同步与互斥的概念

一句话理解同步与互斥:我等你用完厕所,我再用厕所。

什么叫同步?就是:哎哎哎,我正在用厕所,你等会。 什么叫互斥?就是:哎哎哎,我正在用厕所,你不能进来。

同步与互斥经常放在一起讲,是因为它们之的关系很大,“互斥”操作可以使用“同步”来实现。我“等”你用完厕所,我再用厕所。这不就是用“同步”来实现“互斥”吗?

再举一个例子。在团队活动里,同事A先写完报表,经理B才能拿去向领导汇报。经理B必须等同事A完成报表,AB之间有依赖,B必须放慢脚步,被称为同步。在团队活动中,同事A已经使用会议室了,经理B也想使用,即使经理B是领导,他也得等着,这就叫互斥。经理B跟同事A说:你用完会议室就提醒我。这就是使用"同步"来实现"互斥"。

有时候看代码更容易理解,伪代码如下:

 void  抢厕所(void)
 {
	if (有人在用) 我眯一会;
	用厕所;
	喂,醒醒,有人要用厕所吗;
 }

1.3 同步的例子:有缺陷

程序:在06_create_task_use_params的基础上,修改出12_task_sync_exclusion

创建两个任务:一个用来执行大量的计算任务,另一个永年执行打印函数显示OLED

xTaskCreate(                //加返回值是 判断任务有没有创建成功
        CalcTask,           //计算任务
        "Task1",            //声音任务
        128,                //栈大小
        NULL,       //传入的参数 g_Task1Info
        osPriorityNormal,   //优先级默认
        NULL                //任务句柄 无
        );
xTaskCreate(                //加返回值是 判断任务有没有创建成功
        LcdPrintTask,       //LCD打印任务
        "Task1",            //声音任务
        128,                //栈大小
        &g_Task2Info,       //传入的参数 g_Task1Info
        osPriorityNormal,   //优先级默认
        NULL                //任务句柄 无
        );

编写这两个函数

static struct TaskPrintInfo g_Task1Info = {0, 0, "Task1"};  // (0,0),Task1
static struct TaskPrintInfo g_Task2Info = {0, 3, "Task2"};  // (0,3),Task2
static struct TaskPrintInfo g_Task3Info = {0, 6, "Task3"};  // (0,6),Task3

static int g_LCDCanUse = 1; //默认=1 能使用LCD
uint32_t g_sum = 0;   //定义一个计数值
static volatile int g_calc_end = 0; // 计算结束标志位,使用volatile不要用编译器优化优化

uint64_t g_time = 0;

void CalcTask(void *params)
{
    uint32_t i = 0;     //定义一个计数值

    g_time = system_get_ns();   //获取当前系统时间
    for (i = 0; i < 10000000; i ++)
    {
        g_sum += i;
    }
    g_calc_end = 1; //计算完成标志位   置位
    g_time = system_get_ns() - g_time;  //运行这段任务消耗的时间
    vTaskDelete(NULL);  //计算完成杀死任务
}

void LcdPrintTask(void *params)
{
	int len;
	while (1)
	{
		LCD_PrintString(0, 0, "Waiting");
		
		// vTaskDelay(3000);
		
		while (g_calc_end == 0);    //等待
		
		/* 打印信息 */
		if (g_LCDCanUse)
		{
			g_LCDCanUse = 0;
			
			LCD_ClearLine(0, 0);
			len = LCD_PrintString(0, 0, "Sum: ");
			LCD_PrintHex(len, 0, g_sum, 1);
			
			LCD_ClearLine(0, 2);
			len = LCD_PrintString(0, 2, "Time(ms): ");
			LCD_PrintSignedVal(len, 2, g_time/1000000); //打印消耗了多长时间
			
			g_LCDCanUse = 1;
		}
		vTaskDelete(NULL);  //任务自杀
	}
}

这里遇到了bug!!!

在这里插入图片描述

  • 程序卡死在while循环里了,但是这个变量已经是1了,为什么程序会卡死在上面两行汇编语句呢???

  • 原因是编译器优化了我们的变量

对这个变量,执行while (g_calc_end == 0); 这条语句的时候,它会读取内存,把变量的值放到CPU某个寄存器里,以后一直就判断那个寄存器,但是这个寄存器得到的是原始的值,并没有每次都去读取内存

  • 这个变量是在其他任务里被修改的,那我们使用这个变量的时候,每次都需要读内存!
  • 那怎么办呢??我们加上一个volatile就可以了

烧录代码运行
在这里插入图片描述

计算10000000个数需要2.5S,真的是这样的吗???

我们现在有两个任务

在这里插入图片描述
他们是怎么调度的呢?
两个任务优先级相同,A任务运行1ms,B任务运行1ms,A任务运行1ms,B任务运行1ms

在这里插入图片描述

任务B执行的程序是死等,这也耗费了一半的时间,在任务B的开始就等待3S左右

vTaskDelay(3000);   //开始的时候我先等待3000Tick

加上这句等待3S之后,就变成了1278ms

在这里插入图片描述

确实如此

这个死循环可以用其他方法来代替

  • 等任务A计算完成之后,用任务A唤醒任务B
  • 使用同步的时候,我们需要考虑如何提高处理器的性能!
  • 让等待的任务阻塞,不参与CPU的调度!

这节课学习了同步的例子,有缺陷的例子

学习视频:【FreeRTOS入门与工程实践 --由浅入深带你学习FreeRTOS(FreeRTOS教程 基于STM32,以实际项目为导向)】 【精准空降到 13:38】 https://www.bilibili.com/video/BV1Jw411i7Fz/?p=25&share_source=copy_web&vd_source=8af85e60c2df9af1f0fd23935753a933&t=818

1.4 freertos.c源码

/* USER CODE BEGIN Header */
#include "driver_led.h"
#include "driver_lcd.h"
#include "driver_mpu6050.h"
#include "driver_timer.h"
#include "driver_ds18b20.h"
#include "driver_dht11.h"
#include "driver_active_buzzer.h"
#include "driver_passive_buzzer.h"
#include "driver_color_led.h"
#include "driver_ir_receiver.h"
#include "driver_ir_sender.h"
#include "driver_light_sensor.h"
#include "driver_ir_obstacle.h"
#include "driver_ultrasonic_sr04.h"
#include "driver_spiflash_w25q64.h"
#include "driver_rotary_encoder.h"
#include "driver_motor.h"
#include "driver_key.h"
#include "driver_uart.h"
#include "music.h"

/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */

BaseType_t ret; // long
static TaskHandle_t xSoundTaskHandle;           // void *  在全局变量里记录句柄

static StackType_t g_pucStackOfLightTask[128];  // 变量前缀的意思是 全局变量g 指针p uint8_t类型uc的StackOfLightTask 光任务的栈
StaticTask_t g_TCBofLightTask;                  // 光任务的TCB
static TaskHandle_t xLightTaskHandle;           // void *  在全局变量里记录句柄

static StackType_t g_pucStackOfColorTask[128];  // 变量前缀的意思是 全局变量g 指针p uint8_t类型uc的StackOfLightTask 色任务的栈
StaticTask_t g_TCBofColorTask;                  // 色任务的TCB
static TaskHandle_t xColorTaskHandle;           // void *  在全局变量里记录句柄

/* USER CODE END Variables */
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */

struct TaskPrintInfo{
    uint8_t x;      //定义坐标x
    uint8_t y;      //定义坐标y
    char name[16];  //定义要打印输出的内容,最多显示16个字符
};

static struct TaskPrintInfo g_Task1Info = {0, 0, "Task1"};  // (0,0),Task1
static struct TaskPrintInfo g_Task2Info = {0, 3, "Task2"};  // (0,3),Task2
static struct TaskPrintInfo g_Task3Info = {0, 6, "Task3"};  // (0,6),Task3

static int g_LCDCanUse = 1; //默认=1 能使用LCD
uint32_t g_sum = 0;   //定义一个计数值
static volatile int g_calc_end = 0; // 计算结束标志位,使用volatile不要用编译器优化优化

uint64_t g_time = 0;

void CalcTask(void *params)
{
    uint32_t i = 0;     //定义一个计数值

    g_time = system_get_ns();   //获取当前系统时间
    for (i = 0; i < 10000000; i ++)
    {
        g_sum += i;
    }
    g_calc_end = 1; //计算完成标志位   置位
    g_time = system_get_ns() - g_time;  //运行这段任务消耗的时间
    vTaskDelete(NULL);  //计算完成杀死任务
}

void LcdPrintTask(void *params)
{
	int len;
	while (1)
	{
		LCD_PrintString(0, 0, "Waiting");
		
		vTaskDelay(3000);   //开始的时候我先等待3000Tick
		
		while (g_calc_end == 0);    //等待
		
		/* 打印信息 */
		if (g_LCDCanUse)
		{
			g_LCDCanUse = 0;
			
			LCD_ClearLine(0, 0);
			len = LCD_PrintString(0, 0, "Sum: ");
			LCD_PrintHex(len, 0, g_sum, 1);
			
			LCD_ClearLine(0, 2);
			len = LCD_PrintString(0, 2, "Time(ms): ");
			LCD_PrintSignedVal(len, 2, g_time/1000000); //打印消耗了多长时间
			
			g_LCDCanUse = 1;
		}
		vTaskDelete(NULL);  //任务自杀
	}
}
/* USER CODE END FunctionPrototypes */

void StartDefaultTask(void *argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */
    LCD_Init();
    LCD_Clear();

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* creation of defaultTask */
  
//  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  
  
//  /* 创建任务:声 */
//  // 先创建一个动态分配内存的任务
//  ret = xTaskCreate(                //加返回值是 判断任务有没有创建成功
//            PlayMusic,          //孤勇者的函数
//            "SoundTask",        //声音任务
//            128,                //栈大小
//            NULL,               //无传入的参数
//            osPriorityNormal,   //优先级默认
//            & xSoundTaskHandle  //任务句柄
//            );

//  
//  /* 创建任务:光 */ 
//  // 创建一个静态分配内存的任务
//  xLightTaskHandle = xTaskCreateStatic(
//            Led_Test,           //LED测试函数,PC13以500ms间隔亮灭一次
//            "LightTask",        //光任务
//            128,                //栈大小,这里提供了栈的大小(长度)
//            NULL,               //无传入的参数
//            osPriorityNormal,   //优先级默认
//            g_pucStackOfLightTask,  // 静态分配的栈,一个buffer,这里只提供了首地址,长度就是栈的大小,最开始栈的类型不对,栈的类型uint32_t
//            &g_TCBofLightTask       // 取址TCB
//  );
//  
//  /* 创建任务:色 */ 
//  xColorTaskHandle = xTaskCreateStatic(
//            ColorLED_Test,           //LED测试函数,PC13以500ms间隔亮灭一次
//            "ColorTask",        //光任务
//            128,                //栈大小,这里提供了栈的大小(长度)
//            NULL,               //无传入的参数
//            osPriorityNormal,   //优先级默认
//            g_pucStackOfColorTask,  // 静态分配的栈,一个buffer,这里只提供了首地址,长度就是栈的大小
//            &g_TCBofColorTask       // 取址TCB
//  );


    xTaskCreate(                //加返回值是 判断任务有没有创建成功
            CalcTask,           //计算任务
            "Task1",            //声音任务
            128,                //栈大小
            NULL,       //传入的参数 g_Task1Info
            osPriorityNormal,   //优先级默认
            NULL                //任务句柄 无
            );
    xTaskCreate(                //加返回值是 判断任务有没有创建成功
            LcdPrintTask,       //LCD打印任务
            "Task1",            //声音任务
            128,                //栈大小
            &g_Task2Info,       //传入的参数 g_Task1Info
            osPriorityNormal,   //优先级默认
            NULL                //任务句柄 无
            );
  
  /* USER CODE END RTOS_THREADS */

  /* USER CODE BEGIN RTOS_EVENTS */
  /* add events, ... */
  /* USER CODE END RTOS_EVENTS */
}
/* USER CODE BEGIN Header_StartDefaultTask */

/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN StartDefaultTask */
  /* Infinite loop */
  LCD_Init();
  LCD_Clear();
  
  for(;;)
  {
    //Led_Test();
    //LCD_Test();
	//MPU6050_Test(); 
	//DS18B20_Test();
	//DHT11_Test();
	//ActiveBuzzer_Test();
	//PassiveBuzzer_Test();
	//ColorLED_Test();
	IRReceiver_Test();  //影
	//IRSender_Test();
	//LightSensor_Test();
	//IRObstacle_Test();
	//SR04_Test();
	//W25Q64_Test();
	//RotaryEncoder_Test();
	//Motor_Test();
	//Key_Test();
	//UART_Test();
  }
  /* USER CODE END StartDefaultTask */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

在这里插入图片描述

3. 互斥的例子:有缺陷

讲解这个程序"06_create_task_use_params"的互斥缺陷。

4. 通信的例子:有缺陷

5. FreeRTOS的解决方案

  • 正确性

  • 效率:等待者要进入阻塞状态

  • 多种解决方案

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1896620.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

滚动表格(vue版本)【已验证可正常运行】

演示图 注&#xff1a;以下代码来自于GPT4o&#xff1a;国内官方直连GPT4o 代码 <template><div><div class"alarmList-child" ref"alarmList" mouseenter.stop"autoRoll(1)" mouseleave.stop"autoRoll()"><div…

Debezium报错处理系列之第111篇:Can‘t compare binlog filenames with different base names

Debezium报错处理系列之第111篇:Cant compare binlog filenames with different base names 一、完整报错二、错误原因三、解决方法Debezium从入门到精通系列之:研究Debezium技术遇到的各种错误解决方法汇总: Debezium从入门到精通系列之:百篇系列文章汇总之研究Debezium技…

【笔记】redis和session的关系

把这句注释掉之后变成了空指针 新用户/老用户的id都登不进页面

k8s-第四节-Service

Service Service 通过 label 关联对应的 PodServcie 生命周期不跟 Pod 绑定&#xff0c;不会因为 Pod 重创改变 IP提供了负载均衡功能&#xff0c;自动转发流量到不同 Pod可对集群外部提供访问端口集群内部可通过服务名字访问 创建 Service kubectl apply -f service.yamlkub…

Maven 分模块设计与开发 继承

介绍 在 Maven 中进行分模块设计&#xff08;multi-module project&#xff09;&#xff0c;可以帮助将一个大型项目分解为更小、更易管理的模块。这种设计方式有助于提高项目的可维护性、复用性和团队协作效率。 继承关系 目录结构 引入父Maven 父坐标 在子项目中引入父亲…

时钟系统框图(时钟树)解析

时钟系统框图&#xff08;时钟树&#xff09;解析 文章目录 时钟系统框图&#xff08;时钟树&#xff09;解析1、时钟树2、 4个时钟源&#xff1a;$HSI、HSE、LSI、LSE$3、PLL锁相环倍频输出4、系统时钟的来源5、Enable CSS&#xff08;时钟监视系统&#xff09;6、几个重要的时…

pytorch-时间序列

目录 1. 时间序列2. word embedding2.1 one hot2.2 word2vec2.3 GloVe 1. 时间序列 具有时间相关性的序列叫做时间序列&#xff0c;比如&#xff1a;语音、文本句子 2. word embedding 2.1 one hot 针对句子来说&#xff0c;可以用[seq_len, vector_len] 有多少个单词vecto…

Finding and exploting an unused API endpoint

Using 0$ account buy a piece of lether priced at $133 1、尝试访问api接口 大概率可能访问不到,但是可以尝试访问下 /api/swagger/v1 /openapi.json 2、页面功能点寻找 api send to Repeter 3、Find Supported HTTP请求 POST方法测试 通过测试得知支持GET方法和PATC…

无服务器【Serverless】架构的深度剖析:组件介绍、优缺点与适用场景

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《未来已来&#xff1a;云原生之旅》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、云计算的发展趋势 2、无服务器计算简介 二、无服务…

使用AES加密数据传输的iOS客户端实现方案

在现代应用开发中&#xff0c;确保数据传输的安全性是至关重要的。本文将介绍如何在iOS客户端中使用AES加密数据传输&#xff0c;并与服务器端保持加密解密的一致性。本文不会包含服务器端代码&#xff0c;但会解释其实现原理。 加密与解密的基本原理 AES&#xff08;Advance…

Bellman equation的不同形式及变化

总忘记贝尔曼方程的推导过程&#xff0c;自己推一遍吧 matrix-vector form就省略了 对于matrix-vector form形式的状态价值贝尔曼方程求解&#xff0c;若已知MDP的动态&#xff08;转移矩阵P和奖励函数R&#xff09;&#xff0c;则计算复杂度的贡献主要来自矩阵求逆&#xff…

Solo 开发者周刊 (第12期):连接独立开发者,共享开源智慧

这里会整合 Solo 社区每周推广内容、产品模块或活动投稿&#xff0c;每周五发布。在这期周刊中&#xff0c;我们将深入探讨开源软件产品的开发旅程&#xff0c;分享来自一线独立开发者的经验和见解。本杂志开源&#xff0c;欢迎投稿。 产品推荐 1、Soju————一个现代的书签…

在线图片转文字的软件,分享3种强大的软件!

在信息爆炸的时代&#xff0c;图片作为信息的重要载体之一&#xff0c;其内容往往蕴含着巨大的价值。然而&#xff0c;面对海量的图片信息&#xff0c;如何高效、准确地将其转化为文字&#xff0c;成为了许多人的迫切需求。今天&#xff0c;就为大家盘点几款功能强大的在线图片…

马斯克宣布xAI将在8月份推出Grok-2大模型 预计年底推出Grok-3

在今年内&#xff0c;由特斯拉创始人马斯克创立的人工智能初创公司xAI将推出两款重要产品Grok-2和Grok-3。马斯克在社交平台上透露了这一消息&#xff0c;其中Grok-2预计在今年8月份面世&#xff0c;而Grok-3则计划于年底前亮相。 除此之外&#xff0c;马斯克还表示&#xff0c…

SQLyog脚本无限试用重置脚本

文章目录 引言脚本(win)必要操作、说明 引言 SQLyog 需要po jie&#xff0c;但是网上的没看到很好使的&#xff0c;直接下的官方。能处理14天试用也是很ok的。 脚本(win) echo offREM SQLyog注册表key&#xff0c;可能跟你的不一样&#xff0c;如果不一样&#xff0c;请替换…

【TB作品】体重监控系统,ATMEGA16单片机,Proteus仿真

机电荷2018级课程设计题目及要求 题1:电子称重器设计 功能要求: 1)开机显示时间(小时、分)、时分可修改; 2)用滑动变阻器模拟称重传感器(测量范围0- 200g),数码管显示当前重量值,当重量值高于高 值时,红灯长亮; 3)当重量值低于低值时,黄灯长亮; 4)当重量值在正常值时,绿灯亮; 5…

【堆 优先队列】23. 合并 K 个升序链表

本文涉及知识点 堆 优先队列 LeetCode23. 合并 K 个升序链表 给你一个链表数组&#xff0c;每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表。 示例 1&#xff1a; 输入&#xff1a;lists [[1,4,5],[1,3,4],[2,6]] 输出&#…

使用任意电脑通过内网穿透生成的公网地址远程SSH连接本地Windows电脑

文章目录 前言1. Windows安装SSH服务2. Windows本地连接测试3. Windows安装Cpolar工具4. 配置SSH公网地址5. 远程SSH 连接测试6. 固定SSH公网地址7. 固定SSH地址测试 前言 在当今的数字化转型时代&#xff0c;远程连接和管理计算机已成为日常工作中不可或缺的一部分。对于Wind…

JAVA--JSON转换工具类

JSON转换工具类 import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackso…

浅析MySQL-索引篇01

什么是索引&#xff1f; 索引是帮助存储引擎快速获取数据的一种数据结构&#xff0c;类似于数据的目录。 索引的分类 按数据结构分类&#xff1a; MySQL 常见索引有 BTree 索引、HASH 索引、Full-Text 索引。 Innodb是MySQL5.5之后的默认存储引擎&#xff0c;BTree索引类型也…