聊聊内存那些事(基于单片机系统)

news2024/11/15 11:40:42

单片机的RAM和ROM

单片机的ROM,叫只读程序存储器,是FLASH存储器构成的,如U盘就是FLASH存储器。所以,FLASH和ROM是同义的。单片机的程序,就是写到FLASH中了。

而RAM是随机读/写存储器,用作数据存储器,是在运行程序时,存放数据的。

内存区

内存主要分为:代码区、常量区、静态区(全局区)、堆区、栈区这几个区域。

l 代码区:存放程序的代码,即CPU执行的机器指令,并且是只读的。

l 常量区:存放常量(程序在运行的期间不能够被改变的量,例如: 25,字符串常量”dongxiaodong”, 数组的名字等)

l 静态区(全局区):静态变量和全局变量的存储区域是一起的,一旦静态区的内存被分配, 静态区的内存直到程序全部结束之后才会被释放

l 堆区:由程序员调用malloc()函数来主动申请的,需使用free()函数来释放内存,若申请了堆区内存,之后忘记释放内存,很容易造成内存泄漏

l 栈区:存放函数内的局部变量,形参和函数返回值。栈区之中的数据的作用范围过了之后,系统就会回收自动管理栈区的内存(分配内存 , 回收内存),不需要开发人员来手动管理。栈区就像是一家客栈,里面有很多房间,客人来了之后自动分配房间,房间里的客人可以变动,是一种动态的数据变动。

STM32F103C8T6中

ROM起始地址为:0x8000000, 大小为:0x10000 (64K)

只读的,存放着代码区和常量区

RAM起始地址为:0x20000000,大小为:0x5000 (20K)

可读可写的,存放着静态区、栈区和堆区

STM32各区详细介绍:

代码区:

l 代码区存放着程序编译后的CPU指令

l 函数名称是一个指针,可以通过查询函数名称所处的内存地址,查询函数存放的区域

//函数声明
void dong();
//主函数定义
int main(void)
{
  //串口初始化
    Uart1_Init(115200);
    //函数调用
  dong();
    //输出test函数地址
    printf("dong() addrs : 0x%p\n",dong);

    while(1);
}
void dong(){
     //输出main函数地址
     printf("mian() addrs : 0x%p\n",main);
}

输出:

可见0x08000c2d和0x08000be9都在ROM里的代码区

常量区

指针可以指向常量也可以指向变量的区域,通过指针(char *p)来测试一下常量与变量去的地址变化。

1void dongxiaodong_fun(){

2char *p=NULL;//定义一个指针变量

3//常量 4 p="2020dongxiaodong";//指针指向一个常量 5 printf("addr1:0x%p\r\n",p);//输出常量地址

6//变量 7char data[]={"dong1234"};

8 p=data;//指针指向一个变量 9 printf("addr2:0x%p\r\n",p);//输出变量地址10 }

输出:

可见常量的地址在ROM里的常量区,局部变量在RAM的栈空间下

静态区

静态区包括静态变量和全局变量,静态变量通过static修饰,一旦初始化则一直占用RAM空间

1int global_a;//全局变量,默认值为0 2staticint global_b;//静态全局变量,默认值为0 3void fun(){

4staticint c;//静态变量,默认值为0 5 printf("static int c add:0x%p , val:%d \r\n",&c,c);

6 c++;

7}

8void dongxiaodong_fun(){

9//输出全局变量10 printf(" int a add:0x%p , val:%d \r\n",&global_a,global_a);

11 printf("static int b add:0x%p , val:%d \r\n",&global_b,global_b);

12//调用函数查看静态变量13for(int i=0;i<3;i++){

14 fun();

15 }

16 }

输出:

其中global_a为全局变量、global_b为全局静态变量、c为局部静态变量,他们如果没有赋初值都会被系统自动赋值为0,静态变量初始化则一直有效,并不会因为多次调用了初始化语句而出现多次初始化的问题。代码中虽然看似初始化了c变量三次,其实实际只有第一次有效。

堆区

堆区是调用malloc函数来申请的内存空间,这部分空间使用完后要调用free()函数来释放申请的空间。Void * malloc(size_t);函数的参数是需要分配的空间字节大小,返回是一个void*类型的指针,该指针指向分配空间的首地址,void*类型指针可以转换为任意的其它类型指针。

l 堆是向上增长,即首地址递增的方向增长

l 通过malloc()申请的空间必须通过free()进行释放,如果申请的内存未释放则可能造成内存泄露

l malloc()内存申请失败将返回NULL

l malloc分配的内存空间在逻辑上是连续的,而在物理上可以不连续。

l 释放只能释放一次,如果释放两次及两次以上会出现错误(但是释放空指针例外,释放空指针其实也等于什么都没有做,所以,释放多少次都是可以的),free()释放空间后可以将指针指向“NULL”确保指针不会成为野指针。

STM32C8T6:

标准库中定义了默认堆的大小为0x200=512字节,其可以认为程序同一时间的malloc分配大小不可大于512字节数据。

堆空间默认不常驻RAM空间,但当代码出现malloc关键字后,堆空间将分配设置的整体大小(512字节)占用RAM空间。

void dongxiaodong_fun(){

//申请

printf("-----malloc-----\r\n");

char *p1=malloc(100);

if(p1==NULL) printf("p1 malloc fail \r\n");

char *p2=malloc(1024);

if(p2==NULL) printf("p2 malloc fail \r\n");

//赋值

memcpy(p1,"dongxiaodong123456",strlen("dongxiaodong123456"));

printf("p1 addr:%p ,val:%s \r\n",p1,p1);

printf("p2 addr:%p\r\n",p2);

//释放

printf("-----free-----\r\n");

free(p1);

free(p2);

printf("p1 addr:%p ,val:%s \r\n",p1,p1);

p1=NULL;

printf("p1 addr:%p \r\n",p1);

}

输出:

可见堆空间分配内存失败则会返回NULL,并且地址指向0x00,释放时只是通过free(),仅是把指向的内容变成了空值,但地址还是存在的,所以标准的做法是赋上“NULL”值。内存释放后(使用free函数之后指针变量p本身保存的地址并没有改变),需要将p的值赋值为NULL(拴住野指针)。

分配空间不能达到所规定的最大值:

void dongxiaodong_fun(){

char *d=malloc(512);

//char *d=malloc(500); //可行if(d==NULL) printf("512 malloc fail\r\n");

}

输出:

查看解释:

如果用malloc(n)来分配堆内存,那么分配的内存比n大,为什么呢?

0.malloc分配的内存不一定连续,所以需要header指针来链接各部分

1.实际分配的堆内存是Header + n结构。返回给用户的是n部分的首地址 所以他还有一部分内存是用来存header的,所以比原始的大

2.由于内存对齐值8,内存对其机制,实际分配的堆内存大于等于sizeof(Header) + n

realloc() --- 来源于“菜鸟”

C 库函数 void *realloc(void *ptr, size_t size) 尝试重新调整之前调用 malloccalloc 所分配的 ptr 所指向的内存块的大小。

下面是 realloc() 函数的声明。

void*realloc(void*ptr,size_t size)

参数

  • ptr -- 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针。

  • size -- 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针。

返回值

该函数返回一个指针 ,指向重新分配大小的内存。如果请求失败,则返回 NULL。

示例:

1 #include <stdio.h>

2 #include <stdlib.h>

3 #include <string.h>

4 5int main()

6{

7char *str;

8 9/* 最初的内存分配 */10 str = (char *) malloc(15);

11 strcpy(str, "runoob");

12 printf("String = %s, Address = %p\n", str, str);

1314/* 重新分配内存 */15 str = (char *) realloc(str, 25);

16 strcat(str, ".com");

17 printf("String = %s, Address = %p\n", str, str);

1819 free(str);

2021return(0);

22 }

输出:

String= runoob,Address=0x7fa2f8c02b10String= runoob.com,Address=0x7fa2f8c02b10

栈区

栈区由编译器自动分配和是释放,存放函数中定义的参数值、返回值和局部变量,在程序运行过程实时分配和释放,栈区由操作系统自动管理,无须手动管理。栈区是先进后出原则。

l 栈是向下增长,即首地址递减的方向增长

l 编译器不会给未初始化的局部变量赋初始值0,所以未初始化的局部变量通常是一个混乱值,所以定义局部变量时赋初值是最稳妥的。

STM32C8T6:

标准库中定义了默认栈的大小为0x400=1024字节,其可以认为程序同一时间的局部变量不可大于1024字节数据。

栈空间的字节数是常驻空间,一经初始化将分配设置的整体大小(1024字节)占用RAM空间。

1//主函数定义 2int main(void)

3{

4//串口初始化 5 Uart1_Init(115200);

6 printf("start SYS 1\r\n");

7char data1[1024]={0}; //1024字节 8 printf("start SYS 2\r\n");

9char data2[100]={0}; //100字节10 printf("start SYS 3\r\n");

11char data3[100]={0}; //100字节,10字节可以正常运行12 printf("start SYS 4\r\n");

13while(1);

14 }

实测发现栈空间的大小到1024+100+10字节都是可以正常运行的,这个难道是STM32做了栈空间的预留吗?1024并不是做了完全的强制限制。

地址测试

void dongxiaodong_fun(){

int a=100;

int b;

printf("a addr:0x%p val:%d\r\n",&a,a);

printf("b addr:0x%p val:%d\r\n",&b,b);

}

输出:

可见b的地址小于a的地址,其是向首地址递减的方向增长(向下增长),b的值没有赋初值,其值是混乱的,建议赋初值使用。

注意:

const修饰的数据

l const修饰的是变量名,之所以叫const常量,意思是不可以更改,权限为只读,但是它的本质是变量,只不过是不可修改的变量

l const修饰局部变量则存放在栈区,如果修饰全局变量就存放在静态区(全局区)

数据存储(大小端模式)

数据在内存中存放,分为大端模式和小端模式

大端模式:低位字节存在高地址上,高位字节存在低地址上。

小端模式:低位字节存在低地址上,高位字节存在高地址上。

网络字节序:TCP/IP各层协议将字节序列定义为大端模式,因此在TCP/IP协议中使用的大端模式通常称为网络字节序。

void dongxiaodong_fun(){

int data=0x12345678;

char *p=(char*)&data;

printf("p+0:0x%p-->0x%02X\r\n",p,*(p+0));

printf("p+1:0x%p-->0x%02X\r\n",p,*(p+1));

printf("p+2:0x%p-->0x%02X\r\n",p,*(p+2));

printf("p+3:0x%p-->0x%02X\r\n",p,*(p+3));

}

输出:

可见其值的高位存储在地址的低位上,所以STM32的变量存储是小端模式

动态内存申请的碎片化问题

标准的内存动态分配是动态链表进行管理。由于malloc返回的是一个指针再加上单片机没有MMU,使得分配的指针就像一个个钉子一样在内存中了,直到被释放。这就会导致内存管理非常困难,从而导致内存碎片化。

这是一个理想的极端例子

单片机的堆空间分配有1KB的空间,其为1024字节,为了说明和计算方便我们忽略掉链表占用的空间,只计算实际存储空间大小。

第一步:申请64块内存空间,每块16字节,那么就会分配完1K字节的空间。

char *p[64]={NULL};

for(int i=0; i<64; i++){

ptr[i] = malloc(16);

}

第二步:释放掉偶数块内存空间

for(int i=0; i<64; i+=2){

free(ptr[i] );

ptr[i]=NULL;

}

第三步:

我们释放掉的空间达到了堆的一半大小,512字节了,但都是不连续的。32块16字节的非连续空间,所以要分配出大于16字节的内存块是分配不出来的。有512字节的空间但只能分配小于16字节的连续空间,在某些场合原本单片机RAM的堆空间资源就很紧张,再加上这种不充分的使用使得程序稳定性大打折扣。

STM32C8T6真实案例:

内存碎片化可以通过如下列子进行验证:

1void dongxiaodong_fun(){

2char *p[8]={NULL};

3//512字节的堆空间,似乎只能分配8*50=400字节 4for(int i=0;i<8;i++){

5 p[i]=malloc(50);

6if(p[i]==NULL) printf("p[%d] malloc fail\r\n",i);

7 }

8//输出其中一个数的地址 9 printf("%p\r\n",p[2]);

10 printf("%p\r\n",p[3]);

11//释放偶数下标空间12for(int i=0;i<8;i+=2){

13free(p[i]);

14 p[i]=NULL;

15 }

16//分配失败,内存碎片化17char *d1=malloc(100); //可行18if(d1==NULL) printf("d1 100 malloc fail\r\n");

1920//释放一个奇数位空间21free(p[3]);

22//分配成功,分配的空间在p[2]和p[3]的空间上,和多了10个字节的额外空间23char *d2=malloc(160);

24if(d2==NULL) printf("d2 100 malloc fail\r\n");

25 printf("%p\r\n",d2);

26 }

输出:

这个例子大体上还是体现出了内存碎片化的问题所在,因为总共有8个空间快,申请后释放奇数块理论上有50*4=200字节,但分配100字节却行不通,重要原因在于释放的偶数块每块大小为50,并且其地址是不连续的。当释放其中一个奇数块后,内存就可以达到需要分配的连续块大小了,所以分配的空间使用了p[2]、 p[3]、p[4]的空间。

存在几个问题:

Malloc分配的空间总共可以有512,但分1包也只能是500左右的有效空间,分8包是400左右的有效空间,利用率为什么这么低?

碎片化测试时,p[2]、p[3]、p[4]的大小应该是3*50=150,结果最大可以是160左右。

查看解释:

如果用malloc(n)来分配堆内存,那么分配的内存比n大,为什么呢?

0.malloc分配的内存不一定连续,所以需要header指针来链接各部分

1.实际分配的堆内存是Header + n结构。返回给用户的是n部分的首地址 所以他还有一部分内存是用来存header的,所以比原始的大

2.由于内存对齐值8,内存对其机制,实际分配的堆内存大于等于sizeof(Header) + n

内存碎片化的主要解决方法:

将间隔的小内存移动到一起并排,释放连续空间

现在普遍采用的段页式内存分配方式就是将进程的内存区域分为不同的段,然后将每一段由多个固定大小的页组成。通过页表机制,使段内的页可以不必连续处于同一内存区域,从而减少了外部碎片,然而同一页内仍然可能存在少量的内部碎片,只是一页的内存空间本就较小,从而使可能存在的内部碎片也较少。

正点原子的mymalloc() 函数

问题1:Malloc函数标准库有为什么又出现这个?

问题2:内存碎片化处理?

总结:

l 可以进行多种RAM的内存管理,比如外部的SRAM,方便管理多个RAM空间

l 可以查看到内存的使用率

l 没有进行内存碎片化处理

STM32查看FLASH空间和RAM空间使用量

打开显示:

编译后输出:

Program Size: Code=38356 RO-data=6676 RW-data=400 ZI-data=47544

Code:代码占用的空间

RO-data:其中RO表示Read Only ,只读常亮的大小

RW-data:其中RW表示Read Write,可读可写的变量大小,初始化已经付了初值

ZI-data:其中ZI表示Zero Initialize,可读可写的变量大小,没有赋初值而被系统赋值为0的字节数

RAM的大小:

RAM=【RW-data】+【ZI-data】

ROM的大小:

ROM =【Code】+【RO-data】+【RW-data】,ROM的大小即为程序所下载到ROM Flash中的大小。为什么Rom中还要有RW,因为掉电后RAM中所有的数据都丢失了,每次上电RAM中的数据是被程序赋值的,每次这些固定的值就是存储在ROM中的,为什么不包含ZI段呢,是因为ZI数据都是0,没必要包含,只要查询运行之前将ZI数据所在的区域一律清零即可。包含进去反而浪费存储空间。

程序运行:

烧录到ROM中的image文件与实际运行时的ARM程序之间并不是完全一样的。MCU执行过程是先将RW从ROM中搬到RAM中,因为RW是变量,变量不能存在ROM中。然后将ZI所在的RAM区域全部清零,因为ZI区域并不在Image中,所以需要程序根据编译器给出的ZI地址及大小来将相应的RAM区域清零。ZI中也是变量,同理:变量不能存储在ROM中,在程序运行的最初阶段,RO中的指令完成了这两项工作后C程序才能正常访问变量。否则只能运行不含变量的代码。

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

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

相关文章

SpringBoot笔记(一)入门使用

一、为什么用SpringBootSpringBoot优点创建独立Spring应用内嵌web服务器自动starter依赖&#xff0c;简化构建配置自动配置Spring以及第三方功能提供生产级别的监控、健康检查及外部化配置无代码生成、无需编写XMLSpringBoot缺点人称版本帝&#xff0c;迭代快&#xff0c;需要时…

电路基础(1)电路模型和电路定律

电路中的电压、电流之间具有两种约束&#xff0c;一种是由电路元件决定的元件约束&#xff1b;另一种是元件间连接而引入的几何约束&#xff08;就是拓扑约束&#xff09;&#xff0c;后者由基尔霍夫定律来表达。基尔霍夫定律是集总参数电路的基本定律。 1.电路和电路模型电源又…

电路模型和电路定律(2)——“电路分析”

各位CSDN的uu们你们好呀&#xff0c;好久没有更新电路分析的文章啦&#xff0c;今天来小小复习一波&#xff0c;之前那篇博客&#xff0c;小雅兰更新了电路的历史以及电压电流的参考方向&#xff0c;这篇博客小雅兰继续&#xff01;&#xff01;&#xff01; 电阻元件 电压源和…

FFMPEG 安装教程windowslinux(CentOS版)

ps: 从笔记中迁移至blog 版本概述 Windows 基于win10 Linux 基于CentOS 7.6 一.Windows安装笔记 1.下载安装 https://ffmpeg.org/download.html 2 解压缩&#xff0c;拷贝到需要目录&#xff0c;重命名 3 追加环境变量 echo %PATH%setx /m PATH "%PATH%;F:\dev_tools\…

用C/C++制作一个简单的俄罗斯方块小游戏

用C/C制作一个简单的俄罗斯方块小游戏 用C/C制作一个简单的俄罗斯方块小游戏 0 准备1 游戏界面设计 1.1 界面布局1.2 用 EasyX 显示界面1.3 音乐播放 2 方块设计 2.1 方块显示2.2 随机生成一个方块2.3 方块记录 3 方块移动和旋转 3.1 方块的移动3.2 方块的旋转3.3 方块的碰撞和…

基于 WebSocket、Spring Boot 教你实现“QQ聊天功能”的底层简易demo

目录 前言 一、分析 1.1、qq聊天功能分析 1.2、WebSocket介绍 1.2.1、什么是消息推送呢&#xff1f; 1.2.2、原理解析 1.2.3、报文格式 二、简易demo 2.1、后端实现 2.1.1、引入依赖 2.1.2、继承TextWebSocketHandler 2.1.3、实现 WebSocketConfigurer 接口 2.2、…

LeetCode096不同的二叉搜索树(相关话题:卡特兰数)

目录 题目描述 解题思路 代码实现 进出栈序列理解卡特兰数分析策略 相关知识 参考文章 题目描述 给你一个整数 n &#xff0c;求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种&#xff1f;返回满足题意的二叉搜索树的种数。 示例 1&#xff1a; …

《程序员面试金典(第6版)》面试题 02.07. 链表相交

题目描述 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c;函数返回结果…

socket本地多进程通信基本使用方法和示例

目录 前言&#xff1a; socket是什么 socket基本原理框图 socket基本函数 1 socket() 函数 2 bind()函数 3 connect()函数 4 listen() 函数 5 accept() 函数 6 read() write() send() recv()函数 7 close()函数 8 字节序转换&#xff08;hton&#xff09; 示例代码 …

使用 Pulumi 打造自己的多云管理平台

前言在公有云技术与产品飞速发展的时代&#xff0c;业务对于其自身的可用性提出了越来越高的要求&#xff0c;当跨区域容灾已经无法满足业务需求的情况下&#xff0c;我们通常会考虑多云部署我们的业务平台&#xff0c;以规避更大规模的风险。但在多云平台部署的架构下&#xf…

埃安自研版图扩至夸克电驱,动力研发团队已超1000人

埃安的三电自研版图正在扩大。3月3日&#xff0c;广汽集团旗下埃安发布了一项名为“夸克电驱”的技术产品&#xff0c;相比主流电驱体积减少一倍&#xff0c;同时电机功率密度比主流电驱增加了一倍。此前&#xff0c;比亚迪刚刚发布易四方动力系统&#xff0c;特斯拉也在投资者…

HTML常见标签

文章目录一、HTML基础标签注释标签标题标签段落标签换行标签格式化标签图片、音频、视频标签超链接标签列表标签表格标签布局标签表单标签表单标签概述form标签属性表单项标签综合案例一、HTML基础标签 基础标签就是和文字相关的标签 标签描述<h1> ~ <h6>定义标题…

【项目管理】晋升为领导后,如何开展工作?

兵随将转&#xff0c;作为管理者&#xff0c;你可以不知道下属的短处&#xff0c;却不能不知道下属的长处。晋升为领导后&#xff0c;如何开展工作呢&#xff1f; 金九银十&#xff0c;此期间换工作的人不在少数。有几位朋友最近都换了公司&#xff0c;职位得到晋升&#xff0c…

前端——1.相关概念

这篇文章主要介绍前端入门的相关概念 1.网页 1.1什么是网页&#xff1f; 网站&#xff1a;是指在因特网上根据一定的规则&#xff0c;使用HTML等制作的用于展示特定内容相关的网页集合 网页&#xff1a;是网站中的一“页”&#xff0c;通常是HTML格式的文件&#xff0c;它要…

JAVA后端部署项目三步走

1. JAVA部署项目三步走 1.1 查看 运行的端口 lsof -i:8804 &#xff08;8804 为端口&#xff09; 发现端口25111被监听 1.2 杀死进程,终止程序 pid 为进程号 kill -9 pid 1.3 后台运行jar包 nohup java -jar -Xms128M -Xmx256M -XX:MetaspaceSize128M -XX:MaxM…

C++笔记之lambda表达式

引言 Lambda表达式是从C 11版本引入的特性&#xff0c;利用它可以很方便的定义匿名函数对象&#xff0c;通常作为回调函数来使用。大家会经常拿它和函数指针&#xff0c;函数符放在一起比较&#xff0c;很多场合下&#xff0c;它们三者都可以替换着用。 语法 [ captures ] (…

javaScript基础面试题 ---宏任务微任务

宏任务微任务一、为什么JS是单线程语言&#xff1f;二、JS是单线程&#xff0c;怎样执行异步代码&#xff1f;1、JS是单线程语言 2、JS代码执行流程&#xff0c;同步执行完&#xff0c;再进行事件循环&#xff08;微任务、宏任务&#xff09; 3、清空所有的微任务&#xff0c;再…

机器学习100天(四十):040 线性支持向量机-公式推导

《机器学习100天》完整目录:目录 机器学习 100 天,今天讲的是:线性支持向量机-公式推导! 首先来看这样一个问题,在二维平面上需要找到一条直线划分正类和负类。 我们找到了 A、B、C 三条直线。这三条直线都能正确分类所有训练样本。但是,哪条直线最好呢?直观上来看,我…

代码随想录算法训练营第六天|242.有效的字母异位词 、349. 两个数组的交集 、 202. 快乐数、1. 两数之和

当我们遇到了要快速判断一个元素是否出现集合里的时候&#xff0c;就要考虑哈希法。哈希法是牺牲了空间换取了时间&#xff0c;要使用额外的数组&#xff0c;set或者是map来存放数据&#xff0c;才能实现快速的查找。当我们要使用集合来解决哈希问题的时候&#xff0c;优先使用…

【SpringCloud】SpringCloud教程之Nacos实战(1)

目录Nacos是什么&#xff1f;一.Nacos下载二.安装Nacos三.Nacos原理四.Nacos快速入门五.Nacos服务多级存储模式六.Nacos根据集群设置负载均衡1.根据同集群优先访问2.根据权重配置负载均衡七.Nacos的环境隔离八.Nacos和Eureka的区别前提&#xff1a;以订单服务和用户服务为例&am…