ARM Cortex-M7 中 MPU 与 Cache

news/2024/11/16 12:02:05/

项目中采用了ARM cortex-M7的架构进行芯片设计,在随后的开发中遇到了关于Cache配置等问题,花费了一段时间查阅资料与参考STM32H743开发板代码,现在记录总结一下。

1、关于Cache

1.1 Cache是什么

        Cache是位于core与内存之间的一个高速缓存区域,在cortexM7内可以进行设置有16kb、32kb等在硬件集成的时候进行选择。

1.2 Cache的主要功能

        coretexM7中Cache分为ICache以及DCache,分别对应指令缓存以及数据缓存,core为处理器,内存则是保存要处理的数据或者需要执行的代码,内存根据位置可以分为片内以及片外,当core执行程序时需要访问相应内存代码段、数据段等,那么其中就涉及到访问速度,查阅资料显示一般core访问内存需要的时间大概需要60+ns,但是core执行速度很快,这样就会出现core访问内存时出现无事可做的情况,那么为了解决这种问题,就出现了Cache,Cache作为一个core与内存之间的一个高速缓存器,core访问Cache需要的时间在1-15ns左右(不太确定,但是相比于core直接访问内存)能够显著减少core内存访问时间,提高core执行效率。

1.3 Cache的工作模式

        关于Cache的工作模式个人只是大概了解,网上内容也挺多,这里大概按照我的思路大概介绍一下。以ICache举例,当开启了Cache功能后,core要执行的代码的位置会进行先对比Cache内缓存的地址,当地址在Cache缓存的范围内会直接访问Cache,这称为命中,若不在Cache内,称为未命中,Cache会有按照不太清楚的规则对Cache缓存内的指令进行替换(cache内存储也有一定规则,个人感觉不需要了解太深,所以也没有仔细看)。Cache内缓存的指令段都是成段存在的,不是简单的单条指令,所以个人认为除了在执行指令时存在函数跳转或者产生中断等情况频繁出现的话,基本都能够很大提升执行效率。

        当进行Cache配置时由于不同的配置信息会使得Cache的工作模式不尽相同,其中Cache工作模式是通过MPU进行设置的,Cache的工作模式包含4中,分别如下(其中都是MPU寄存器配置,与Cache寄存器无关)

(1)non-Cache

        不进行Cache

(2)Write-Back,write and read allocate

        读:read allocate(开启了Cache就有此种配置,MPU寄存器内无该项配置)-当未命中时,对Cache缓存的数据进行更新;命中时若内存中数据改变了那么不会更新Cache缓存的数据。

        写:write allocate-未命中时,core同时写入内存与Cache内(更新Cache内缓存数据),当下次访问时能够提高速度;当Cache缓存中有core要改变内存数据的时候,仅改变Cache内的数据,不改变内存中的数据

        此种配置能够最大限度提升core的执行效率,但是会造成数据不一致的情况。

(3)Write-Though,no write allocate

        读:read allocate(开启了Cache就有此种配置,MPU寄存器内无该项配置)-当未命中时,对Cache缓存的数据进行更新,命中时若内存中数据改变了那么不会更新Cache缓存的数据。

        写:no write allocate-未命中时不会更新Cache内缓存数据,当Cache缓存中有core要改变内存数据的时候,改变Cache内的数据,同时改变内存中的数据

        不会出现写操作时Cache与内存数据不一致的情况,同时效率没有第四种高。

(4)Write-Back,no write allocate

         读:read allocate(开启了Cache就有此种配置,MPU寄存器内无该项配置)-当未命中时,对Cache缓存的数据进行更新,命中时若内存中数据改变了那么不会更新Cache缓存的数据。

        写:no write allocate-未命中时不会更新Cache内缓存数据,当Cache缓存中有core要改变内存数据的时候,改变Cache内的数据,不改变内存中的数据

        会出现写操作时Cache与内存数据不一致的情况,但是效率没有第二种高。

1.4 Cache的隐患分析与解决思路

        因为Cache本质上是一个能够让core进行高速访问的缓存,当外设或者core对某地址的数据进行修改时,若Cache内也同样缓存了这段地址的数据,那么就会出现两者不一致,以及两者数据如何同步的问题。

        所以本质上,当外设修改或者读取了某段内存的数据的时候,判断Cache内是否有相应地址缓存,进行更新,即可解决数据不一致的问题,但是很大可能也会造成效率下降,个人认为需要在关键的不那么频繁使用的数据访问或者修改段(eg:core一直进行高速计算,实时更新一个参数,外设只有偶尔用到这个参数的时候进行写回,保证外设获取的数据为实时数据)添加更新代码应该能够最大限度保留Cache的效率。

1.5 Cache的寄存器配置

        关于Cache的启动与禁用在cm7_core.h内已经封装完成,可以直接调用(调用应先配置MPU 寄存器,见下一节):

        void SCB_DisableICache();                //禁用ICache;

        void SCB_EnableICache();                //使能ICache;

        void SCB_DisableDcache();                //禁用DCache;

        void SCB_EnableDCache();                //使能DCache;

2、关于MPU

2.1 MPU介绍及功能

        内存保护单元(Memory Protection Uint),顾名思义表示对内存(0x00000000-0xFFFFFFFF)进行保护的寄存器设置,参考资料表示在cortexM7以前的产品中(M3.M4)中关于此类设置基本不用,但是在cortexM7中如果要开启Cache就需要对内存进行设置,此时必须用到MPU的一些设置。

        其位于core与内存之间,按我的理解就是如果我按照全部内存不可访问这种模式设置了MPU,那么设置以后core所有的访问内存的操作都会被阻止(可能会触发硬件中断?不确定需要试验进行验证),那么MPU其实就是起的一个core与内存之间访问的权限的相关设置,那么Cache与MPU以及core有什么样的关系呢?无论怎么样Cache工作肯定与core有交互,而cache需要通过core对内存中代码或者数据进行缓冲存储等,所以若是MPU设置不可访问、不可Cache、不可缓冲的话那么肯定会影响到相应Cache的执行类型。

        MPU具体类型见下表

        

  三种内存类型Strongly ordered、Device、Normal,不同的内存类型能够开启的Cache模式也不同,当MPU内存类型设置为Strongly Ordered时就算开启了Cache也是没有起到作用的。

2.2 与MPU相关的寄存器

 

        需要用到的分别为CTRL、RNR、RBAR、RASR寄存器

        (1)MPU->CTRL

        使能与禁止MPU功能。

        (2)MPU->RNR

        MPU将存储分为0-15个区域,区域可以自行设置,不同区域可以重叠覆盖,数值越大,优先级越高,比如说以0为编号,设置0x00000000-0x00080000区域为不可访问,但是又以2为编号,设置0x00040000-0x00080000为读写访问,那么0x00040000-0x00080000为读写访问,0x00000000-0x00040000为不可访问。

        (3)MPU->RBAR

        设置MPU->RNR之后再次配置MPU->RBAR,此寄存器位配置MPU->RNR对应编号的区域的基地址((1)中的0x00000000、0x00040000)。

        (4)MPU->RASR

        

按照不同的配置TEX、C、B的设置可以得到不同MPU类型

 

 

 

 

根据上述信息,查阅stm32 hal库代码得到其怎么配置MPU的代码

typedef struct
{uint8_t                Enable;                /*!< Specifies the status of the region.This parameter can be a value of @ref CORTEX_MPU_Region_Enable                 */uint8_t                Number;                /*!< Specifies the number of the region to protect.This parameter can be a value of @ref CORTEX_MPU_Region_Number                 */uint32_t               BaseAddress;           /*!< Specifies the base address of the region to protect.                           */uint8_t                Size;                  /*!< Specifies the size of the region to protect.This parameter can be a value of @ref CORTEX_MPU_Region_Size                   */uint8_t                SubRegionDisable;      /*!< Specifies the number of the subregion protection to disable.This parameter must be a number between Min_Data = 0x00 and Max_Data = 0xFF    */uint8_t                TypeExtField;          /*!< Specifies the TEX field level.This parameter can be a value of @ref CORTEX_MPU_TEX_Levels                    */uint8_t                AccessPermission;      /*!< Specifies the region access permission type.This parameter can be a value of @ref CORTEX_MPU_Region_Permission_Attributes  */uint8_t                DisableExec;           /*!< Specifies the instruction access status.This parameter can be a value of @ref CORTEX_MPU_Instruction_Access            */uint8_t                IsShareable;           /*!< Specifies the shareability status of the protected region.This parameter can be a value of @ref CORTEX_MPU_Access_Shareable              */uint8_t                IsCacheable;           /*!< Specifies the cacheable status of the region protected.This parameter can be a value of @ref CORTEX_MPU_Access_Cacheable              */uint8_t                IsBufferable;          /*!< Specifies the bufferable status of the protected region.This parameter can be a value of @ref CORTEX_MPU_Access_Bufferable             */
}MPU_Region_InitTypeDef;#if (__MPU_PRESENT == 1)
/** @defgroup CORTEX_MPU_HFNMI_PRIVDEF_Control MPU HFNMI and PRIVILEGED Access control* @{*/
#define  MPU_HFNMI_PRIVDEF_NONE      ((uint32_t)0x00000000)
#define  MPU_HARDFAULT_NMI           ((uint32_t)0x00000002)
#define  MPU_PRIVILEGED_DEFAULT      ((uint32_t)0x00000004)
#define  MPU_HFNMI_PRIVDEF           ((uint32_t)0x00000006)
/*** @}*//** @defgroup CORTEX_MPU_Region_Enable CORTEX MPU Region Enable* @{*/
#define  MPU_REGION_ENABLE     ((uint8_t)0x01)
#define  MPU_REGION_DISABLE    ((uint8_t)0x00)
/*** @}*//** @defgroup CORTEX_MPU_Instruction_Access CORTEX MPU Instruction Access* @{*/
#define  MPU_INSTRUCTION_ACCESS_ENABLE      ((uint8_t)0x00)
#define  MPU_INSTRUCTION_ACCESS_DISABLE     ((uint8_t)0x01)
/*** @}*//** @defgroup CORTEX_MPU_Access_Shareable CORTEX MPU Instruction Access Shareable* @{*/
#define  MPU_ACCESS_SHAREABLE        ((uint8_t)0x01)
#define  MPU_ACCESS_NOT_SHAREABLE    ((uint8_t)0x00)
/*** @}*//** @defgroup CORTEX_MPU_Access_Cacheable CORTEX MPU Instruction Access Cacheable* @{*/
#define  MPU_ACCESS_CACHEABLE         ((uint8_t)0x01)
#define  MPU_ACCESS_NOT_CACHEABLE     ((uint8_t)0x00)
/*** @}*//** @defgroup CORTEX_MPU_Access_Bufferable CORTEX MPU Instruction Access Bufferable* @{*/
#define  MPU_ACCESS_BUFFERABLE         ((uint8_t)0x01)
#define  MPU_ACCESS_NOT_BUFFERABLE     ((uint8_t)0x00)
/*** @}*//** @defgroup CORTEX_MPU_TEX_Levels MPU TEX Levels* @{*/
#define  MPU_TEX_LEVEL0    ((uint8_t)0x00)
#define  MPU_TEX_LEVEL1    ((uint8_t)0x01)
#define  MPU_TEX_LEVEL2    ((uint8_t)0x02)
/*** @}*//** @defgroup CORTEX_MPU_Region_Size CORTEX MPU Region Size* @{*/
#define   MPU_REGION_SIZE_32B      ((uint8_t)0x04)
#define   MPU_REGION_SIZE_64B      ((uint8_t)0x05)
#define   MPU_REGION_SIZE_128B     ((uint8_t)0x06)
#define   MPU_REGION_SIZE_256B     ((uint8_t)0x07)
#define   MPU_REGION_SIZE_512B     ((uint8_t)0x08)
#define   MPU_REGION_SIZE_1KB      ((uint8_t)0x09)
#define   MPU_REGION_SIZE_2KB      ((uint8_t)0x0A)
#define   MPU_REGION_SIZE_4KB      ((uint8_t)0x0B)
#define   MPU_REGION_SIZE_8KB      ((uint8_t)0x0C)
#define   MPU_REGION_SIZE_16KB     ((uint8_t)0x0D)
#define   MPU_REGION_SIZE_32KB     ((uint8_t)0x0E)
#define   MPU_REGION_SIZE_64KB     ((uint8_t)0x0F)
#define   MPU_REGION_SIZE_128KB    ((uint8_t)0x10)
#define   MPU_REGION_SIZE_256KB    ((uint8_t)0x11)
#define   MPU_REGION_SIZE_512KB    ((uint8_t)0x12)
#define   MPU_REGION_SIZE_1MB      ((uint8_t)0x13)
#define   MPU_REGION_SIZE_2MB      ((uint8_t)0x14)
#define   MPU_REGION_SIZE_4MB      ((uint8_t)0x15)
#define   MPU_REGION_SIZE_8MB      ((uint8_t)0x16)
#define   MPU_REGION_SIZE_16MB     ((uint8_t)0x17)
#define   MPU_REGION_SIZE_32MB     ((uint8_t)0x18)
#define   MPU_REGION_SIZE_64MB     ((uint8_t)0x19)
#define   MPU_REGION_SIZE_128MB    ((uint8_t)0x1A)
#define   MPU_REGION_SIZE_256MB    ((uint8_t)0x1B)
#define   MPU_REGION_SIZE_512MB    ((uint8_t)0x1C)
#define   MPU_REGION_SIZE_1GB      ((uint8_t)0x1D)
#define   MPU_REGION_SIZE_2GB      ((uint8_t)0x1E)
#define   MPU_REGION_SIZE_4GB      ((uint8_t)0x1F)
/*** @}*//** @defgroup CORTEX_MPU_Region_Permission_Attributes CORTEX MPU Region Permission Attributes* @{*/
#define  MPU_REGION_NO_ACCESS      ((uint8_t)0x00)
#define  MPU_REGION_PRIV_RW        ((uint8_t)0x01)
#define  MPU_REGION_PRIV_RW_URO    ((uint8_t)0x02)
#define  MPU_REGION_FULL_ACCESS    ((uint8_t)0x03)
#define  MPU_REGION_PRIV_RO        ((uint8_t)0x05)
#define  MPU_REGION_PRIV_RO_URO    ((uint8_t)0x06)
/*** @}*//** @defgroup CORTEX_MPU_Region_Number CORTEX MPU Region Number* @{*/
#define  MPU_REGION_NUMBER0    ((uint8_t)0x00)
#define  MPU_REGION_NUMBER1    ((uint8_t)0x01)
#define  MPU_REGION_NUMBER2    ((uint8_t)0x02)
#define  MPU_REGION_NUMBER3    ((uint8_t)0x03)
#define  MPU_REGION_NUMBER4    ((uint8_t)0x04)
#define  MPU_REGION_NUMBER5    ((uint8_t)0x05)
#define  MPU_REGION_NUMBER6    ((uint8_t)0x06)
#define  MPU_REGION_NUMBER7    ((uint8_t)0x07)
#define  MPU_REGION_NUMBER8    ((uint8_t)0x08)
#define  MPU_REGION_NUMBER9    ((uint8_t)0x09)
#define  MPU_REGION_NUMBER10   ((uint8_t)0x0A)
#define  MPU_REGION_NUMBER11   ((uint8_t)0x0B)
#define  MPU_REGION_NUMBER12   ((uint8_t)0x0C)
#define  MPU_REGION_NUMBER13   ((uint8_t)0x0D)
#define  MPU_REGION_NUMBER14   ((uint8_t)0x0E)
#define  MPU_REGION_NUMBER15   ((uint8_t)0x0F)/*** @}*/
#endif /* __MPU_PRESENT */void MPU_Config( void )
{MPU_Region_InitTypeDef MPU_InitStruct;/* 禁止 MPU */HAL_MPU_Disable();/* 配置0x80000000内存段的MPU属性为Write back, Read allocate,Write allocate */MPU_InitStruct.Enable           = MPU_REGION_ENABLE;MPU_InitStruct.BaseAddress      = 0x80000000;MPU_InitStruct.Size             = MPU_REGION_SIZE_2MB;MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;MPU_InitStruct.Number           = MPU_REGION_NUMBER0;MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;MPU_InitStruct.SubRegionDisable = 0x00;MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;HAL_MPU_ConfigRegion(&MPU_InitStruct);/*使能 MPU */HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

故可以在main函数内调用MPU_config()进行MPU配置,其函数内容可以借鉴修改。

{MPU_Config();SCB_EnableICache();SCB_EnableDCache();SCB_CleanDCache();}__STATIC_INLINE void SCB_EnableICache (void)
{#if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U)__DSB();__ISB();SCB->ICIALLU = 0UL;                     /* invalidate I-Cache */__DSB();__ISB();SCB->CCR |=  (uint32_t)SCB_CCR_IC_Msk;  /* enable I-Cache */__DSB();__ISB();#endif
}/**\brief   Disable I-Cache\details Turns off I-Cache*/
__STATIC_INLINE void SCB_DisableICache (void)
{#if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U)__DSB();__ISB();SCB->CCR &= ~(uint32_t)SCB_CCR_IC_Msk;  /* disable I-Cache */SCB->ICIALLU = 0UL;                     /* invalidate I-Cache */__DSB();__ISB();#endif
}/**\brief   Enable D-Cache\details Turns on D-Cache*/
__STATIC_INLINE void SCB_EnableDCache (void)
{#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)uint32_t ccsidr;uint32_t sets;uint32_t ways;SCB->CSSELR = 0U; /*(0U << 1U) | 0U;*/  /* Level 1 data cache */__DSB();ccsidr = SCB->CCSIDR;/* invalidate D-Cache */sets = (uint32_t)(CCSIDR_SETS(ccsidr));do {ways = (uint32_t)(CCSIDR_WAYS(ccsidr));do {SCB->DCISW = (((sets << SCB_DCISW_SET_Pos) & SCB_DCISW_SET_Msk) |((ways << SCB_DCISW_WAY_Pos) & SCB_DCISW_WAY_Msk)  );#if defined ( __CC_ARM )__schedule_barrier();#endif} while (ways-- != 0U);} while(sets-- != 0U);__DSB();SCB->CCR |=  (uint32_t)SCB_CCR_DC_Msk;  /* enable D-Cache */__DSB();__ISB();#endif
}/**\brief   Disable D-Cache\details Turns off D-Cache*/
__STATIC_INLINE void SCB_DisableDCache (void)
{#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)register uint32_t ccsidr;register uint32_t sets;register uint32_t ways;SCB->CSSELR = 0U; /*(0U << 1U) | 0U;*/  /* Level 1 data cache */__DSB();SCB->CCR &= ~(uint32_t)SCB_CCR_DC_Msk;  /* disable D-Cache */__DSB();ccsidr = SCB->CCSIDR;/* clean & invalidate D-Cache */sets = (uint32_t)(CCSIDR_SETS(ccsidr));do {ways = (uint32_t)(CCSIDR_WAYS(ccsidr));do {SCB->DCCISW = (((sets << SCB_DCCISW_SET_Pos) & SCB_DCCISW_SET_Msk) |((ways << SCB_DCCISW_WAY_Pos) & SCB_DCCISW_WAY_Msk)  );#if defined ( __CC_ARM )__schedule_barrier();#endif} while (ways-- != 0U);} while(sets-- != 0U);__DSB();__ISB();#endif
}/**\brief   Clean D-Cache\details Cleans D-Cache*/
__STATIC_INLINE void SCB_CleanDCache (void)
{#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)uint32_t ccsidr;uint32_t sets;uint32_t ways;SCB->CSSELR = 0U; /*(0U << 1U) | 0U;*/  /* Level 1 data cache */__DSB();ccsidr = SCB->CCSIDR;/* clean D-Cache */sets = (uint32_t)(CCSIDR_SETS(ccsidr));do {ways = (uint32_t)(CCSIDR_WAYS(ccsidr));do {SCB->DCCSW = (((sets << SCB_DCCSW_SET_Pos) & SCB_DCCSW_SET_Msk) |((ways << SCB_DCCSW_WAY_Pos) & SCB_DCCSW_WAY_Msk)  );#if defined ( __CC_ARM )__schedule_barrier();#endif} while (ways-- != 0U);} while(sets-- != 0U);__DSB();__ISB();#endif
}

其中SCB_EnableICache()以及后面2个函数都已经定义好了,这里复制处出来供大家学习参考,内还有其他对ICache或者DCache的操作,感兴趣可以到core_cm7.h头文件去了解一下。

3 总结

        (1)在cortexM7中若需要使用Cache则必须先配置MPU

        (2)按照不同的MPU配置能够使用的Cache属性也不同

        (3)Cache一共有4种模式

        (4)Cache作为一个高速缓存,肯定存在缓存与实际内存不一致的情况,只要能够保证在关键数据或者代码变更时(非core操作)更新ICache或者DCache虽然会降低性能但能够保证数据一致。


http://www.ppmy.cn/news/146795.html

相关文章

Cortex‐M3-总线接口

总线框图 Cortex‐M3 内部有若干个总线接口&#xff0c;以使 CM3 能同时取址和访内&#xff08;访问内存&#xff09;&#xff0c;它们是: 指令存储区总线&#xff08;两条): 负责对代码存储区的访问&#xff0c;分别是 I‐Code 总线和 D‐Code 总线。前者用 于取指&#xff0…

MPU MCU CPU GPU之间的关系

CPU&#xff08;Central Processing Unit&#xff0c;中央处理器)是计算机系统的主要处理器&#xff0c;它负责执行指令、处理数据和控制计算机系统的操作。CPU通常被用于通用计算和控制任务&#xff0c;如桌面电脑、服务器和智能手机等。 MPU&#xff08;Microprocessor Unit…

CPU、MPU、MCU、SoC、MCM介绍

写在前面&#xff1a; 本文章旨在总结备份、方便以后查询&#xff0c;由于是个人总结&#xff0c;如有不对&#xff0c;欢迎指正&#xff1b;另外&#xff0c;内容大部分来自网络、书籍、和各类手册&#xff0c;如若侵权请告知&#xff0c;马上删帖致歉。 目录 一、CPU二、MP…

CPU、MPU、MCU、SOC的概念和区别

概念 1、cpu cpu(central processing unit)是一台计算机的运算核心和控制核心&#xff0c;CPU由运算器、控制器和寄存器以及实现他们联系的数据、控制总线构成&#xff0c;cpu典型的三级流水线操作是取值、译码、执行&#xff0c;差不多所有CPU的执行原理可以分为四个阶段&am…

CPU、MPU、MCU、SOC的理解

1、CPU&#xff08;Central Processing Unit&#xff09; 中央处理器&#xff08;central processing unit&#xff0c;简称CPU&#xff09;作为计算机系统的运算和控制核心&#xff0c;是信息处理、程序运行的最终执行单元。CPU由运算器、控制器和寄存器及实现它们之间联系的数…

【日常】怀念儿童时的多啦A梦

怀念儿童时期的多啦A梦 儿童时期的乐趣HTML多啦A梦效果图&#xff1a;总结对自己孩子的期望 儿童时期的乐趣 时光飞逝&#xff0c;如白驹过隙版飞逝而过&#xff0c;一眨眼已经到了奔三的年纪。俗话说三十而立&#xff0c;是我拖了三十群里的后腿&#xff0c;在这里说一声抱歉…

ARM:MPU MMU SMMU

1、MPU &#xff08;Memory Protection Unit&#xff09;&#xff1b; 2、MMU&#xff08;Memory Management Unit&#xff09;。 MMU是比MPU提供了功能更强大的内存保护机制&#xff0c;MPU只提供了内存区域保护&#xff0c;而MMU是在此基础上提供了虚拟地址映射技术&#x…

glGenBuffers与glBindBuffer理解

1.glGenBuffers 官方解释&#xff1a;generate buffer object names unsigned int VBO; glGenBuffers(1, &VBO); void glGenBuffers(GLsizei n,GLuint * buffers); 第一个参数是要生成的缓冲对象的数量&#xff0c;第二个是要输入用来存储缓冲对象名称的数组&#xff0c…