ZYNQ笔记(十三):双核 AMP 通信实验

版本:Vivado2020.2(Vitis)

ZYNQ 裸机双核 AMP 实验:

        CPU0 接收串口的数据,并写入 OCM 中,然后利用软件产生中断触发 CPU1;CPU1 接收到中断后,根据从 OCM 中读出的数据控制呼吸灯的频率,并在控制结束后触发 CPU0 中断,实现了双核 CPU 通信的功能。

目录

一、介绍

(1)双核 AMP 通信

(2)ZYNQ启动流程(裸机)

二、硬件设计

三、软件设计

(1)新建 CPU0 应用工程

(2)分配 CPU0 DDR 空间

(3)新建 CPU1 应用工程

(4)分配 CPU0 DDR 空间

(5)代码部分

(6)下载

四、效果


一、介绍

        Zynq-7000 SoC 集成了 双核 ARM Cortex-A9 MPCore,采用对称多处理(SMP)架构,同时支持非对称多处理(AMP)模式,适用于嵌入式高性能计算和实时控制场景。

(1)双核 AMP 通信

        直接附上正点原子开发指南的内容切片:

          — — — — — — — — — — — — — 分割线 — — — — — — — — — — — — 

(2)ZYNQ启动流程(裸机)

        上面介绍有提到了一些专有名词(如BootROM、FSBL、OCM),这里连带介绍一下 ZYQN(Xilinx Zynq-7000 SoC) 裸机开发时的启动流程,主要分为以下几个阶段:

1. Boot ROM

  • 硬件自动执行:上电或复位后,芯片首先运行片内ROM中的固化代码(Boot ROM)。

  • 启动模式检测:根据硬件配置(如模式引脚设置)确定启动源(如QSPI Flash、SD卡、NAND Flash、JTAG等)。

  • 加载FSBL:从启动设备中读取 FSBL(First Stage Boot Loader)到芯片的 OCM(On-Chip Memory)或 DDR 中(需预先配置DDR,本次例程配置了所以程序基于 DDR 运行)。

2. FSBL(First Stage Boot Loader)

  • 功能

    • 初始化必要外设(如DDR控制器、时钟、PLL)。

    • 从Flash或SD卡中加载用户程序(裸机代码或第二阶段引导程序)。

    • 可配置 PL(Programmable Logic)部分(可选,需加载比特流文件)。

  • 生成方式:通过 Xilinx SDK 或 Vitis 工具链生成(基于用户硬件设计)。

  • 执行位置:通常在 OCM 中运行。

3. 用户程序(裸机代码)

  • 加载与执行:FSBL将用户编写的裸机程序(ELF文件)加载到DDR或OCM中,并跳转到其入口地址。执行程序。

二、硬件设计

        (1)本实验是用软件产生中断(SGI)的方式来控制两个 CPU 访问共享内存的过程:

        首先需要处理器向共享内存中写入一个数据,本实验选择 CPU0 往共享内存中写入数据。当 CPU0 写入成功后就发送一条软中断指令去触发 CPU1 去读取共享内存中的数据。

        当 CPU1 读取到共享内存的数据后需要通过 M_AXI_GP0 接口的 AXI4-Lite 总线将数据发送到 PL 端呼吸灯的 IP 核, 呼吸灯的IP核根据PS发过来的数据对呼吸频率进行相应的配置,然后 CPU1 发送一条软中断指令触发 CPU0 继续往共享内存中写入数据。

        到此便实现了两个 CPU 通过共享内存进行数据交互的功能,同时为了对IP核进行控制以及方便观察数据流的走向,用 UART 接收控制命令以及打印 Debug 信息。

        (2)关于自定义 LED 呼吸灯 IP 核的设计与使用这里不再赘述,跟之前笔记的例程操作步骤一致,详细参考:ZYNQ笔记(六):自定义IP核-LED呼吸灯

        (3)最后整体 bd 设计部分如图所示:设计检查、Generate Output Products、 Create HDL Wrapper、管脚约束、Gnerate Bitstream、Export Hardware(包含比特流文件)、启动Vitis

        注意 LED 呼吸灯的 IP 核要手动添加 LED 管脚并与开发板的 LED 对应管脚绑定。

三、软件设计

(1)新建 CPU0 应用工程

        新建工程名 “cpu0_uart” ,系统工程名“AMP_2core_system”,通过cpu0实现串口通信,主要是从uart接收控制命令。如图所示,同时处理器选择ps7_cortexa9_0(默认也是cpu0).

(2)分配 CPU0 DDR 空间

        单核应用工程,默认将DDR空间全部分配给cpu0,这里用到了双核所以需要进行分配,就直接将DDR分二部分,一部分给cpu0,另一部分给cpu1(具体怎么大小分配都行),如图所示:

        红框标注的地方左侧为基地址,右侧为存储空间大小(单位字节),存储空间大小将默认的完整空间大小 0x1FF00000 (十进制 535,822,336;535,822,336/1024/1024 = 511MByte),修改为为 0x0FF00000(十进制 267,386,880;267,386,880/1024/1024 = 255MByte)。修改完成后,按下“Ctrl”+ “S”保存。

        另外图中的 ps7_ram_0 和 ps_ram_1 为 OCM 共享内存的基地址和存储空间大小。后面双核通信就是通过这访问这两块共享存储区域实现的,后面软件设计也会用到其的基地址以进行访问(本例程任选一个即可,后面的设计选用的是 ps_ram_0 基地址0x0)。

(3)新建 CPU1 应用工程

        在创建 CPU1 应用工程之前,需要新建基于 CPU1 的板级支持包(BSP)。双击 platform.spr,打开 system_wrapper 设置界面,如下图所示:

        之后依次对硬件平台工程(system_wrapper)右击选择 Clean Project 和 Build Project。

        接下来创建 CPU1 的应用工程,右键系统工程 AMP_2core_system 选择“Add Application Project”,从而在系统工程下添加一个新的应用工程,工程名命名为 cpu1_led,处理器选择 ps7_cortexa9_1, 如图所示:

(4)分配 CPU0 DDR 空间

        同样需要为 cpu2 分配 DDR 空间,两个核的DDR空间不能有重合,负责程序运行时会出现同时访问导致程序异常,所以这里要对起始地址和大小同时进行修改,如图所示:

        起始地址 = CPU0 的 DDR 起始地址 + 其分配大小 = 0x10000 + 0x0FF00000 = 0x10000000 分配大小 ≤  DDR 完整大小 - CPU0 的 DDR 分配大小 = 0x1FF00000(由开发板DDR大小决定)- 0x0FF00000 = 0x10000000 。修改完成后,按下“Ctrl”+“S”保存。

(5)代码部分

        新建 cpu0_uart 应用工程源文件“mian_0.c”,编写代码:

#include "xparameters.h"
#include "xscugic.h"
#include "xil_printf.h"
#include "xil_exception.h"
#include "xil_mmu.h"
#include "stdio.h"//============================宏定义===============================//
#define SHARE_BASE           	0x0		                     //共享OCM首地址(根据lscript.ld设置)
#define CPU1_COPY_ADDR       	0xfffffff0                   //存放CPU1应用起始地址的地址(参考ug585手册)
#define CPU1_START_ADDR      	0x10000000                   //CPU1应用起始地址(给cpu1分配的DDR起始地址)#define INTC_DEVICE_ID	     	XPAR_SCUGIC_SINGLE_DEVICE_ID //GIC中断控制器ID
#define CPU1_ID              	XSCUGIC_SPI_CPU1_MASK        //CPU1 ID
#define SOFT_INTR_ID_CPU0 	 	0                            //软件中断号 0 ,范围0~15
#define SOFT_INTR_ID_CPU1    	1                            //软件中断号 1 ,范围0~15//"SEV"指令唤醒CPU1并跳转至相应的程序
#define sev()                __asm__("sev")               //C语言内嵌汇编写法 send event指令//===========================函数声明==============================//
void start_cpu1();
void cpu0_intr_init(XScuGic *intc_ptr);
void soft_intr_handler(void *CallbackRef);//===========================全局变量==============================//
XScuGic Intc;                    //中断控制器驱动程序实例
int rec_freq_flag = 0;           //接收到呼吸灯频率设置的标志
int speed;                       //频率速度档位(0~7)//=========================CPU0 main函数===========================//
int main()
{//S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0Xil_SetTlbAttributes(SHARE_BASE,0x14de2);    //禁用OCM的Cache属性//S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0Xil_SetTlbAttributes(CPU1_COPY_ADDR,0x14de2);//禁用0xfffffff0的Cache属性//启动CPU1(固化程序时需使用)//start_cpu1();//CPU0中断初始化cpu0_intr_init(&Intc);while(1){if(rec_freq_flag == 0){xil_printf("CPU0: Input 0~7 to change breath LED frequency \r\n");xil_printf("\r\n");//输入0~7指令scanf("%d",&speed);if(speed >= 0 && speed <= 7){xil_printf("CPU0: Input Command %d \r\n",speed);//向共享的地址中写入输入的数据Xil_Out8(SHARE_BASE,speed);//CPU0软件触发CPU1中断XScuGic_SoftwareIntr(&Intc, SOFT_INTR_ID_CPU1, CPU1_ID);rec_freq_flag = 1;}else{xil_printf("CPU0: Command Error, out of range 0~7 \r\n");}}}return 0 ;
}//====================启动CPU1,用于固化程序=======================//
// 如果只是通过 JTAG 模式下载程序,可以不编写这个启动函数,这个函数仅在固化程序的时候起作用。
void start_cpu1()
{//向 CPU1_COPY_ADDR(0Xffffffff0)地址写入 CPU1 的访问内存基地址Xil_Out32(CPU1_COPY_ADDR, CPU1_START_ADDR);dmb();  //等待内存写入完成(同步)sev();  //通过"SEV"指令唤醒CPU1并跳转至相应的程序
}//=========================中断处理函数===========================//
void soft_intr_handler(void *CallbackRef)
{xil_printf("CPU0: Received Soft Interrupt from CPU1 (set finish) \r\n");rec_freq_flag = 0;
}//========================CPU0中断初始化==========================//
void cpu0_intr_init(XScuGic *intc_ptr)
{//初始化中断控制器XScuGic_Config *intc_cfg_ptr;intc_cfg_ptr = XScuGic_LookupConfig(INTC_DEVICE_ID);XScuGic_CfgInitialize(intc_ptr, intc_cfg_ptr,intc_cfg_ptr->CpuBaseAddress);//设置中断异常处理功能Xil_ExceptionInit();Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler, intc_ptr);//使能处理器中断Xil_ExceptionEnable();//关联中断函数XScuGic_Connect(intc_ptr, SOFT_INTR_ID_CPU0,(Xil_ExceptionHandler)soft_intr_handler, (void *)intc_ptr);//使能中断XScuGic_Enable(intc_ptr, SOFT_INTR_ID_CPU0); //CPU0软件中断
}

        新建 cpu1_led 应用工程源文件“mian_1.c”,编写代码:

#include "xparameters.h"
#include "xscugic.h"
#include "xil_printf.h"
#include "xil_exception.h"
#include "xil_mmu.h"
#include "stdio.h"
#include "breath_led_ip.h"//============================宏定义===============================//
#define SHARE_BASE  	     	0x0      		             //共享OCM首地址(根据lscript.ld设置)#define INTC_DEVICE_ID	     	XPAR_SCUGIC_SINGLE_DEVICE_ID //GIC中断控制器ID
#define CPU0_ID              	XSCUGIC_SPI_CPU0_MASK        //CPU0 ID
#define SOFT_INTR_ID_CPU0 		0                            //软件中断号 0 ,范围0~15
#define SOFT_INTR_ID_CPU1 		1                            //软件中断号 1 ,范围0~15#define LED_IP_BASEADDR     	XPAR_BREATH_LED_IP_0_S0_AXI_BASEADDR //LED IP基地址
#define LED_EN					BREATH_LED_IP_S0_AXI_SLV_REG0_OFFSET //宏定义led_en  对应寄存器(slv_reg0)地址偏移量
#define SPEED_EN				BREATH_LED_IP_S0_AXI_SLV_REG1_OFFSET //宏定义speed_en对应寄存器(slv_reg1)地址偏移量
#define SPEED					BREATH_LED_IP_S0_AXI_SLV_REG2_OFFSET //宏定义speed   对应寄存器(slv_reg2)地址偏移量//===========================函数声明==============================//
void cpu1_intr_init(XScuGic *intc_ptr);
void soft_intr_handler(void *CallbackRef);//===========================全局变量==============================//
XScuGic Intc;               //中断控制器驱动程序实例
int soft_intr_flag = 0;     //软件中断的标志
int speed;                  //频率档位(0~7)//=========================CPU1 main函数===========================//
int main()
{//S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0Xil_SetTlbAttributes(SHARE_BASE,0x14de2);    //禁用OCM的Cache属性//CPU1中断初始化cpu1_intr_init(&Intc);//打开LED呼吸灯BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR, LED_EN, 0x1);//使能LED呼吸灯频率设置BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR, SPEED_EN, 0x1);while(1){if(soft_intr_flag){speed = Xil_In8(SHARE_BASE);     //从共享OCM中读出数据xil_printf("CUP1: Received Command is %d \r\n",speed) ;//设置LED呼吸灯频率BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR, SPEED, speed);//CPU1软件触发CPU0中断XScuGic_SoftwareIntr(&Intc,SOFT_INTR_ID_CPU0,CPU0_ID);soft_intr_flag = 0;}}return 0 ;
}//=========================中断处理函数===========================//
void soft_intr_handler(void *CallbackRef)
{xil_printf("CPU1: Received Soft Interrupt from CPU0 (command ready) \r\n") ;soft_intr_flag = 1;
}//=========================CPU1中断初始化=========================//
void cpu1_intr_init(XScuGic *intc_ptr)
{//初始化中断控制器XScuGic_Config *intc_cfg_ptr;intc_cfg_ptr = XScuGic_LookupConfig(INTC_DEVICE_ID);XScuGic_CfgInitialize(intc_ptr, intc_cfg_ptr,intc_cfg_ptr->CpuBaseAddress);//设置中断异常处理功能Xil_ExceptionInit();Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler, intc_ptr);//使能处理器中断Xil_ExceptionEnable();//关联中断函数XScuGic_Connect(intc_ptr, SOFT_INTR_ID_CPU1,(Xil_ExceptionHandler)soft_intr_handler, (void *)intc_ptr);//使能中断XScuGic_Enable(intc_ptr, SOFT_INTR_ID_CPU1); //CPU1软件中断
}

(6)下载

        下载时需要将两个应用工程以及FPGA的比特流文件全部勾选下载。操作如图所示:

        选中两个核的应用工程:

        选中同时下载FPGA波特率文件(这个选项一般是默认勾选了):

四、效果

        1.下载后CPU0打印输入指令的提示信息,同时LED灯已经开始工作:

        2.第一次输入9(正确指令范围0~7),CPU0打印报错信息并重新提示输入指令。

        3.第二次输入1,CPU0打印出接受到的正确指令,接着CPU1打印接收到来自CPU0的中断的提示(表示CPU0已经将指令写入OCM),接着CPU0打印接收到来自CPU1的中断的提示(表示CPU1已经从OCM读取指令并设置LED灯速率),此时LED呼吸灯的速度发生对应变化(比初始的0档位闪烁的快一点),最后CPU0打印输入指令的提示信息(表示可以进行下一次设置操作)

        4.第三次输入7,流程同上,LED灯速率达到最大,闪烁最快。

        注意:

        本次例程用到scanf()(头文件 stidio.h)实现串口输入指令,这种方式可以在vitis串口终端上进行调试。但是如果要用一般的串口助手调试就不行,需要修改串口部分的软件设计,可以加上串口中断功能实现接收串口输入输出。参考笔记:ZYNQ笔记(八):UART 串口中断

        本次例程没有进行程序固化,所以没有使用到CPU0启动CPU1的函数,如果需要固化需要实现该函数,同时也许在硬件设计作配置,例如固化程序到 Flash 需要配置 QSPI Flash ,相关固化可操作参考笔记:ZYNQ笔记(七):程序固化(QSPI Flash)

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

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

相关文章

桌面端开发技术栈选型:开启高效开发之旅

在数字化浪潮中&#xff0c;桌面端应用依然占据重要地位&#xff0c;而选择合适的技术栈是打造优质桌面端应用的关键一步。以下是多种主流桌面端开发技术栈的介绍与对比&#xff0c;希望能为大家提供有价值的参考。 基于 Web 技术的跨平台框架 • Electron&#xff1a; • 特…

C++ 的 输入输出流(I/O Streams)

什么是输入输出流 C 的输入输出操作是通过 流&#xff08;stream&#xff09; 机制实现的。 流——就是数据的流动通道&#xff0c;比如&#xff1a; 输入流&#xff1a;从设备&#xff08;如键盘、文件&#xff09;读取数据 → 程序 输出流&#xff1a;程序将数据写入设备&…

软件测评中心如何保障软件质量与安全性?

软件测评中心是一个专注于保障软件质量、稳定性和安全性的专业机构。在整个软件的生命周期中&#xff0c;它扮演着极其关键的角色。它运用科学的方法和严格的准则&#xff0c;对软件进行全面而细致的检测和评估&#xff0c;确保用户获得可靠的保障。 测评核心工作 软件测评中…

论人际关系发展的阶段

朋友关系的建立和发展是一个渐进的过程&#xff0c;通常需要经历情感积累、信任磨合和价值观融合等阶段。以下是朋友关系发展的详细阶段划分及核心特征&#xff1a; 一、表层接触阶段&#xff08;社交试探期&#xff09; 核心特征&#xff1a;以信息交换为主&#xff0c;关系停…

网络IP冲突的成因与解决方案

网络IP冲突的成因与解决方案 一、IP冲突的常见现象与危害二、IP冲突的常见原因三、6种实用解决方案四、预防IP冲突的4个最佳实践五、总结 前言 肝文不易&#xff0c;点个免费的赞和关注&#xff0c;有错误的地方请指出&#xff0c;看个人主页有惊喜。 作者&#xff1a;神的孩子…

前端节流、防抖函数

节流 什么是节流&#xff1f; 节流就是同一个事件 一秒钟他执行了很多次。但是我不想他执行这么多次&#xff0c;我只想让他执行一次 或者两次。 那该怎么办&#xff1f; why baby why 那我想就是他执行的时候 我就设置一个定时器&#xff0c;如果定时器是空的&#xff0c;等会…

LeetCode第171题_Excel表列序号

LeetCode 第171题&#xff1a;Excel表列序号 题目描述 给你一个字符串 columnTitle&#xff0c;表示 Excel 表格中的列名称。返回 该列名称对应的列序号。 例如&#xff1a; A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 ...难度 简单 题目链…

基于YOLO与PySide6的道路缺陷检测系统(源码)

道路基础设施的健康状况直接影响交通安全与城市运营效率。传统人工巡检方式存在效率低、覆盖范围有限等问题,而基于深度学习的自动化检测技术为道路缺陷识别提供了创新解决方案。本文介绍一个结合YOLO目标检测模型与PySide6图形界面的道路塌陷检测系统,实现了高效、可视化的缺…

04_jQuery

文章目录 一、jQuery介绍1.1、jQuery概述1.2、jQuery特点1.3 为什么要用jQuery 二、引入jQuery2.1、直接引入2.2、CDN引入 三、jQuery语法3.1、基本使用3.2、jQuery事件及常用事件方法3.3、jQuery选择器&#xff08;重点&#xff09;3.3.1、基本选择器3.3.2、层次选择器3.3.3、…

Adruino:传感器及步进电机

一、传感器* 1、温湿度传感器 DHT11它采用专用的数字采集技术和温湿度传感器技术&#xff0c;包括一个电阻式感湿元件和NTC测温元件&#xff0c;并与一个高性能的8位单片机连接。DATA端采用串行接口&#xff08;单线双向&#xff09;与微控制器进行同步和通信。 DHT11的供电电…

高中数学联赛模拟试题精选第18套几何题

在 △ A B C \triangle ABC △ABC 中, A B < A C AB< AC AB<AC, 点 K K K, L L L, M M M 分别是边 B C BC BC, C A C A CA, A B AB AB 的中点. △ A B C \triangle ABC △ABC 的内切圆圆心为 I I I, 且与边 B C BC BC 相切于点 D D D. 直线 l l l 经过线段…

ubantu18.04(Hadoop3.1.3)之Spark安装和编程实践

说明&#xff1a;本文图片较多&#xff0c;耐心等待加载。&#xff08;建议用电脑&#xff09; 注意所有打开的文件都要记得保存。 第一步&#xff1a;准备工作 本文是在之前Hadoop搭建完集群环境后继续进行的&#xff0c;因此需要读者完成我之前教程的所有操作。 以下所有操…

DCDC芯片,boost升压电路设计,MT3608 芯片深度解析:从架构到设计的全维度技术手册

一、硬件架构解析:电流模式升压 converter 的核心设计 (一)电路拓扑与核心组件 MT3608 采用恒定频率峰值电流模式升压(Boost)转换器架构,核心由以下模块构成: 集成功率 MOSFET 内置 80mΩ 导通电阻的 N 沟道 MOSFET,漏极(Drain)对应引脚 SW,源极(Source)内部接…

Java 日志:掌握本地与网络日志技术

日志记录是软件开发中不可或缺的一部分&#xff0c;它为开发者提供了洞察应用程序行为、诊断问题和监控性能的手段。在 Java 生态系统中&#xff0c;日志框架如 Java Util Logging (JUL)、Log4j 和 Simple Logging Facade for Java (SLF4J) 提供了丰富的功能。然而&#xff0c;…

上位机知识篇---时钟分频

文章目录 前言 前言 本文简单介绍了一下时钟分频。时钟分频&#xff08;Clock Division&#xff09;是数字电路设计中常见的技术&#xff0c;用于将高频时钟信号转换为较低频率的时钟信号&#xff0c;以满足不同模块的时序需求。它在处理器、FPGA、SoC&#xff08;片上系统&am…

推荐几个免费提取音视频文案的工具(SRT格式、通义千问、飞书妙记、VideoCaptioner、AsrTools)

文章目录 1. 前言2. SRT格式2.1 SRT 格式的特点2.2 SRT 文件的组成2.3 SRT 文件示例 3. 通义千问3.1 官网3.2 上传音视频文件3.3 导出文案 4. 飞书妙记4.1 官网4.2 上传音视频文件4.3 导出文案4.4 缺点 5. VideoCaptioner5.1 GitHub地址5.2 下载5.2.1 通过GitHub下载5.2.2 通过…

Linux深度探索:进程管理与系统架构

1.冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系。 截至目前&#xff0c;我们所认识的计算机&#xff0c;都是由⼀个个的硬件组件组成。 输入设备&#xff1a;键盘&#xff0c;鼠标…

观察者模式 (Observer Pattern)

观察者模式(Observer Pattern)是一种行为型设计模式。它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象的状态发生变化时,会自动通知所有观察者对象,使它们能够自动更新自己的状态。 一、基础 1. 意图 核心目的:定义对象间的一种一对…

Network.framework 的引入,不是为了取代 URLSession

Network.framework 的引入&#xff0c;不是为了取代 URLSession 如果你感觉 Network.framework 的引入, 可能是为了取代 URLSession, 那你就大错特错了&#xff01;这里需要非常准确地区分一下&#xff1a; &#x1f535; Network.framework 不是为了取代 URLSession。 &…

Redis 数据分片三大方案深度解析与 Java 实战

Redis 数据分片是将数据分散存储在多个 Redis 实例上的技术&#xff0c;以解决单个 Redis 实例在存储容量、性能和可用性上的限制。常见的 Redis 数据分片方案包括客户端分片、代理分片和Redis Cluster&#xff08;集群分片&#xff09;&#xff0c;以下为你详细介绍&#xff1…