RISC-V学习笔记

news2025/1/7 13:33:41

1.RISC ISA=1个基本整数指令集+多个可选的扩展指令集,如RV32I表示支持32位整数指令集。I表示基本指令集,M表示整数乘法与除法指令集,A表示存储器原子指令集,F表示单精度浮点指令集,D表示双精度浮点指令集等,C表示压缩指令集,G(通用)表示指令集模块组合“IMAFD,Zicsr,Zifencei”(Zicsr指令集扩展用于增强对控制和状态寄存器(CSR)的操作,Zifencei指令集扩展用于优化指令缓存(I-Cache)的操作)。ISA的命名格式为RV[###][abc…xyz],其中RV位RISC-V的缩写,[###]为32、64、128等,表示处理器字宽,[abc…xyz]标识该处理器支持的指令集模块集合。

2.RISC-V的Unprivileged Specification定义了32个通用寄存器(X0-X31)以及一个PC,PC寄存器是无法被直接访问的。RV32寄存器位宽为32位,RV64寄存器位宽为64位,依此类推。寄存器宽度与指令的宽度无关,RISC-V大部分指令都是32位的。

3.RISC-V定义了三个特权级,分别是User(U)、Supervisor(S)和Machine(M),M是最高级别,所有的实现都需要支持,还提供了一个可选的Debug级别。不同特权级别下分别对应各自的一套Registers(CSR),用于控制和获取相应Level下的处理器工作状态,高级别的特权级别下可以访问低级别的CSR,反之不行。

4.RSIC-V支持物理内存保护和虚拟内存,物理内存保护(PMP)允许M模式指定U模式可以访问的内存地址,即可以指定不同内存区域的读写权限;虚拟内存需要支持S特权级,用于实现高级的操作系统特性。

5.硬件线程(Hardware Thread,Hart)是指由处理器硬件直接支持的线程执行单元。硬件线程是多线程处理的基础,用于提高计算资源的利用率和整体系统性能。RSIV-V中Hart的概念可以简单理解为一个CPU,Hart在执行算术逻辑运算时所操作的数据必须直接来自于寄存器,Hart可以执行在寄存器和内存之间的数据读写操作,读写操作使用字节为基本单位进行寻址。

6.参与和运行的机器根据其角色可以分成以下三类:构建系统(build):生成编译器可执行程序的计算机;主机系统(host):运行编译器可执行程序,编译链接应用程序的计算机系统;目标系统(target)系统:运行应用程序的计算机系统。根据build/host/target的不同组合我们可以得到如下的编译方式分类:本地编译(native):build==host==target;交叉编译(cross):build==host!=target。

7.平时使用的gcc命令其实是一个符号链接,比如在ubuntu22.04中gcc指向/usr/bin/gcc-11,而/usr/bin/gcc-11又指向/usr/bin/x86_64-linux-gnu-gcc-11,/usr/bin/x86_64-linux-gnu-gcc-11才是真正的编译程序,这个名称含义如下:x86_64指明目标架构,表示该编译器生成适用于64位x86架构;linux指明目标操作系统,表示该编译器生成适用于 Linux 操作系统的代码;gnu指明目标 ABI(应用二进制接口),表示该编译器生成符合 GNU 操作系统和库的代码;gcc是GNU编译器集合(GNU Compiler Collection)的缩写,是一个支持多种编程语言的编译器系统;11是 GCC 的版本号,表示这是 GCC 的第11版。

8..s(小写)文件是纯汇编文件,不再需要预处理器处理,可直接由汇编器处理。.S(大写)文件是汇编文件,需要经过预处理器处理后再由汇编器处理,允许使用宏定义和条件编译指令。.s适用于简单汇编代码,.S适用于需要预处理的复杂汇编代码。

9.RISC-V的汇编语法格式:[label:] [operation] [comment]

  • label(标号):GNU汇编中,任何以冒号结尾的标识符都被认为是一个标号;
  • operation可以有以下多种类型:instruction(指令):直接对应二进制机器指令的字符串;pseudo-instruction(伪指令):为了提高编写代码的效率,可以用一条伪指令指示汇编器产生多条实际的指令;directive(指示/伪操作):通过类似指令的形式(以“.”开头),通知汇编器如何控制代码的产生等,不对应具体的指令;macro:采用.macro/.endm自定义的宏。
  • comment(注释):“#”开始到当前行结束。

10.在RISC-V架构中,中断和异常都是处理器执行流的非正常转移,但它们有不同的触发方式和处理特点。中断是由外部事件(如硬件定时器或外部设备)触发的异步信号,通常在指令之间发生,并会暂停当前指令的执行,转而执行中断服务程序。中断处理完毕后,返回的指令通常是继续执行被中断前的下一条指令。异常是由指令本身引发的同步事件,如非法指令或访问错误等。异常在指令执行时立即发生,并触发异常处理程序处理错误。异常处理完毕后,返回的指令通常是重试引发异常的那条指令,或者根据具体情况跳过该指令。

11.QEMU有两种运行模式:用户模式和系统模式。在用户模式下,QEMU只模拟单个进程,使得为一种架构编译的程序可以在另一种架构上运行,它适用于在不同架构的Linux二进制文件上运行,而无需完整的系统支持。在系统模式下,QEMU模拟整个系统,包括CPU、内存和外围设备,使得可以运行完整的操作系统或模拟硬件平台,此模式常用于操作系统开发和在不同架构上进行测试。通常情况下,系统中的QEMU命令是不同QEMU可执行文件的软链接。QEMU提供多个二进制文件,每个文件针对不同的架构或操作模式(如qemu-system-x86_64、qemu-system-arm等)。

12.RSIC-V的x0寄存器是一个zero寄存器,无法写入数据,读出的数据永远为0。

13.RSIC-V指令编码如下图所示,具体参考官方非特权指令手册。指令长度为32位,指令按照32位对齐,opcode、funct3和funct7共同决定最终的指令类型,指令在内存中按照小端排序。共有R、I、S、B、U、J六类指令。

常见指令与伪指令如下图所示:

RISC-V中的伪指令,如:neg rd,rs(等价于sub rd,x0,rs)、mv rd,rs(等价于addi rd,rs,0)、nop(等价于addi x0,x0,0)等都是通过等价的指令来实现的。

14.QEMU的-s和-S选项常用于调试虚拟机。-s选项等效于-gdb tcp::1234,启动一个gdb服务器并将其绑定到TCP端口1234,允许使用gdb连接到虚拟机进行调试。-S选项则会在启动QEMU后暂停虚拟机的CPU执行,确保虚拟机在启动时不立即运行,而是等待进一步的调试操作。通常,这两个选项会一起使用,启动虚拟机后立即暂停,然后通过gdb进行连接调试,这样可以在虚拟机运行前设置断点,便于调试操作系统或嵌入式系统。

15.常见RISC-V指令使用方法示例:

对于上图中的addi指令,它是属于I-type的,但由于立即数只有12位,所以可表示的数值范围为[-2048,2047),并且在参与计算前该指令中的立即数还会被符号扩展。RISC-V并没有提供subi指令,因为可以用addi指令代替。对于addi指令,要实现赋值一个大数(32位),可以通过lui和addi命令共同实现,lui是一个U-type命令该指令可以构造一个32位的立即数并存放到寄存器中,这个32位的数的高20位为imm,低12位为0,例如lui x5,0x12345等价于x5=0x12345<<12。下图是这两个指令结合使用的例子:

但由于addi指令会让立即数进行符号扩展,所以可能出错,如下图:

正确如下:

RISC-V提供了伪指令li来给寄存器赋值,如:li x5,0x12345678。这个li指令会根据后面立即数的具体大小,将其转换为addi指令或addi和lui指令的组合,如下图:

auipc指令常用来构造相对于当前pc值的地址,并赋值给寄存器,如下图,auipc指令为U-type:

la伪指令可以将label对应的地址加载到寄存器中,编译器会根据实际情况利用auipc和其他指令自动生成la指令,如:la x5,foo(即将foo对应的地址加载到x5寄存器中)。上述内容总结如下:

对于逻辑运算指令,RISC-V提供了逻辑与and(and rd,rs1,rs2)、逻辑或or(or rd,rs1,rs2)、异或xor(xor rd,rs1,rs2),RISC-V并没有提供取反的指令,而是通过伪指令not来实现:not rd,rs(实际是通过xori rd,rs,-1来实现的)。如下图:

移位指令分为逻辑移位和算术移位,逻辑移位又分为逻辑左移和逻辑右移,如下图所示:

对于内存读写指令,在load从内存读取数据到寄存器时,由于寄存器是32位的,所以lb、lh指令存在符号扩展和零扩展的区别,load和store类型的指令中的imm都是12位的,范围为[-2048,2047],但是有相应的伪指令可以实现全局的load和store。如下图:

常规的条件跳转指令如下图所示,立即数imm只有12位,所以跳转是有范围限制的,但实际使用时,直接写标号即可:

以上图中的指令衍生出的伪指令如下图所示:

无条件跳转指令JAL如下图所示,该指令基于PC进行相对跳转,一般用在函数调用时,会保存返回地址,方便函数返回:

基于寄存器的无条件跳转指令JALR如下图所示:

对于更远的,超过+/-1MB的地址,可以将jalr与auipc指令结合使用实现跳转。伪指令j和jr可以在不需要返回时使用,这两个伪指令实际是利用jal和jalr指令和x0寄存器实现的,对于需要返回时可以使用伪指令jal和jalr。

16.RISC-V中寄存器的默认使用规则如下图所示:

注意上图中没有x3和x4寄存器,x3寄存器通常用于存储全局数据的基地址,它是一个全局指针,用于在程序中访问全局变量或数据结构,特别是在使用位置无关代码(PIC)时,x3寄存器可以提高对全局数据的访问效率,x3又被称作gp;x4寄存器通常在多线程编程中用于指向每个线程的线程本地存储(Thread-Local Storage, TLS)。这允许每个线程快速访问与自身相关的数据,而无需查找复杂的结构或全局变量。x4通常用于存储当前硬件线程(hart)的 ID,这需要由软件赋值,x4寄存器又被称作tp。一般在函数调用过程中使用下图中的伪指令:

17.C语言内嵌汇编形式如下:

18.QEMU-virt的内存布局如下图所示:

左侧为起始物理地址,右侧为空间大小。启动 QEMU 可以发现,virt 平台在reset时,会将PC置为 0x1000。0x1000处的内存块名为VIRT_MROM,这部分相当于bootloader。内核加载的地址就是VIRT_DRAM对应的0x80000000。

19.RISC-V系统上电后处于Machine特权级,Machine特权级对应的CSRs寄存器如下图所示,如寄存器mhartid上电初始化后会存储Hart的ID号(多个hart的ID必须是唯一的,且必须有一个hart的ID值为0)。

Zicsr扩展指令集中的指令可用来读取CSR寄存器,如下图中的CSRRW指令可用来读取CSR寄存器的值并写入新值:

伪指令csrw csr,rs(实际为csrrw x0,csr,rs)可以向CSR寄存器中写入新值。下图中的csrrs指令可以读取CSR寄存器的值,并设置相应位为1,伪指令csrr rd,csr(实际为csrrs rd,csr,x0)可用来读取CSR寄存器的值。

20..ld文件是链接脚本文件,用于指导链接器如何将编译后的代码和数据链接成最终的可执行文件,可以在gcc编译时用-T选项指定要使用的链接脚本文件(如gcc -T os.ld)。链接脚本文件常见的语法有:

  • ENTRY(symbol) 用来告诉链接器在生成最终的可执行文件时,程序的入口点应该是指定的符号(symbol)的位置,如ENTRY( _start );
  • OUTPUT_ARCH(bfdarch)命令指定输出文件所适用的计算机体系架构,如OUTPUT_ARCH( "riscv" )(这里并没有说明具体是32位架构还是64位架构,也没有指定目标RISC-V架构具体实现了哪些模块,需要在使用gcc使通过-mabi和-march选项进一步说明);
  • MEMORY 命令描述目标中内存块的位置和大小。可以使用它来描述链接器可以使用哪些内存区域,以及链接器必须避免使用哪些内存区域,可以把段放到特定的内存区域里。链接器将会基于内存区域设置段地址,如果区域趋于饱和将会产生警告信息,链接器不会为了把段更好的放入内存区域而打乱段的顺序。其语法如下图所示:

    name 是链接器脚本中用于引用内存区域的名称。区域名称在链接器脚本之外没有任何意义。区域名称存储在单独的名称空间中,不会与符号名、文件名或段冲突。每个内存区域在 MEMORY 命令中必须有一个不同的名称。attr 字符是一个可选的属性列表,用于指定是否对链接器脚本中未显式映射的输入段使用特定的内存区域。
  • SECTIONS 命令来描述输出文件的内存布局。如下图所示,符号.表示当前位置计数器,如果未通过其他方式指定输出段的地址,地址就会被设置为位置计数器的当前值,在‘SECTIONS ’命令的开头,位置计数器的值为‘ 0 ’。

  • PROVIDE命令用来定义符号(这就是一个如上图中提到的符号赋值命令),每个符号包括一个名字和一个对应的地址值,在代码中访问这些符号等同于访问一个地址,如:PROVIDE(_text_start=.)表示将当前所在地址值赋值给_text_start。

21.mscratch(属于CSRs)是一个关键的临时寄存器,用于在机器模式下处理中断和异常时保存关键状态信息。它帮助确保在处理完中断或异常之后,系统能够正确地恢复并继续执行原来的程序。

22.RISC-V中与Trap(包括异常和中断)相关的寄存器(下图为Machine模式下,三个模式分别有自己的CSRs寄存器,具体可参考RISC-V特权指令集手册)如下图所示:

mtvec寄存器用来存储异常处理函数入口地址,Direct模式表明只有一个异常处理函数,Vectored模式表示异常处理函数以向量表的形式给出:

mcause记录了中断或者异常的具体类型,WLRL表示只有符合要求的值才能被写入:

mtvaul寄存器可以提供更多的信息:

mstatus寄存器记录了相关状态信息,如是否开中断等(这里的中断控制位控制的是全局中断,一旦关闭,后续对mie寄存器的设置将无效),mstatus寄存器上电默认值为全0:

当发生异常或中断时,处理器会将当前的PC值或下一条指令的PC值保存到mepc(Machine Exception Program Counter)寄存器中。mret指令会从mepc寄存器中恢复保存的PC值,并将其加载到PC中,从而使程序跳转回异常发生前的位置继续执行。mret指令如下图所示:

23.Trap发生时,Hart(硬件)自动执行如下图所示的状态装换(下图对应的是异常,保存的是当前指令的PC值):

异常和中断会分为上部分和下部分,上部分就是上图所示的硬件自动执行的部分,在这个过程中会关中断(使用mret命令后才会恢复到之前的中断打开/关闭状态),下半部分就是执行设置的处理函数,这个过程中,主要是:保存当前控制流的上下文信息、调用C语言的trap handler、从trap handler返回(mepc的值可能需要调整)、恢复上下文信息、执行mret指令返回到trap之前的状态。要注意中断和异常返回地址的区别,中断返回后执行下一条指令,而异常返回后会重新执行之前的那条指令。

24.RISC-V将中断分为三类:软中断、定时器中断和外部中断,每类中断又分为U/S/M三种模式,可以通过mie寄存器进一步设置各类中断的打开/关闭状态,如下图:

RISC-V共定义了53个外部中断源,其中UART中断源编号为10。

25.PLIC(Platform-Level Interrupt Controller)是 RISC-V 架构中的一个平台级中断控制器,负责处理和管理系统中的外部中断。PLIC 的设计使得它可以在多核系统中协调多个硬件外部中断,并将这些中断分配给适当的核心进行处理。PLIC相关寄存器如下图:

26.CLINT(Core-Local Interruptor)是 RISC-V 架构中用于管理核心本地中断的组件,它主要负责定时器中断和软件中断,这些中断用于核心之间的通信和定时调度。CLINT相关寄存器如下图所示:

27.可睡眠锁(如互斥量、读写锁)允许线程在锁被占用时进入休眠状态,直到锁变为可用,适用于持锁时间较长的场景,它们的开销较大但适合复杂或长时间的操作。不可睡眠锁(如自旋锁)则不会使线程休眠,而是通过忙等待方式尝试获取锁,适用于持锁时间非常短的场景,它们避免了线程上下文切换的开销,但在锁被长时间占用时可能会浪费CPU资源。

28.ecall是RISC-V指令集中的系统调用指令,用于触发操作系统服务。在执行ecall时,处理器根据寄存器a7中的值(系统调用号)来识别请求的服务类型,而其他寄存器(如a0到a6)传递系统调用的参数。执行ecall后,控制权转交给操作系统,操作系统根据系统调用号和参数执行相应操作。系统调用处理完成后,控制权返回到用户程序。如下图所示:

29.在 RISC-V 架构中,U模式(用户模式)下的程序无法直接访问某些系统控制寄存器(CSR)。这些寄存器通常包括机器模式(M模式)和监督模式(S模式)专用的寄存器,用户模式下的程序在访问这些寄存器时会引发异常。

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

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

相关文章

strapi中使用Documentation插件

Swagger UI 自动生成并展示了 API 的文档&#xff0c;这些文档是根据 OpenAPI Specification (OAS) 格式编写的。它提供了对 API 端点、请求方法&#xff08;GET, POST, PUT, DELETE 等&#xff09;、参数、响应格式等详细信息的描述 安装 npm run strapi install documentat…

AI来帮忙:蛋白纯化不用慌

在当今生物学研究的前沿领域&#xff0c;从探索疾病的发病机制&#xff0c;到新型药物的研发&#xff0c;再到生物工程产品的制造&#xff0c;高纯度、高活性的蛋白质都是不可或缺的基石。 科研人员在蛋白纯化的征程中&#xff0c;时常被诸多难题困扰。一方面&#xff0c;生物…

SpringCloud系列教程:微服务的未来(六)docker教程快速入门、常用命令

对于开发人员和运维工程师而言&#xff0c;掌握 Docker 的基本概念和常用命令是必不可少的。本篇文章将带你快速入门 Docker&#xff0c;并介绍一些最常用的命令&#xff0c;帮助你更高效地进行开发、测试和部署。 目录 前言 快速入门 docker安装 配置镜像加速 部署Mysql …

基于单片机中药存放环境监测系统的实现

基于单片机中药存放环境监测系统的实现 项目开发背景 随着现代中药的广泛应用&#xff0c;中药材的存储环境对其质量有着至关重要的影响。温湿度、烟雾、火灾等环境因素&#xff0c;若不加以控制&#xff0c;将会导致中药材失效或变质。因此&#xff0c;设计一个基于单片机的…

casaos安装最新版homeassistant-arm

进入cosOS界面点自定义安装 Docker镜像:homeassistant/armv7-homeassistant Tag:2024.12.2 标题&#xff1a;Home Assistant 图片路径&#xff1a;https://cdn.jsdelivr.net/gh/IceWhaleTech/CasaOS-AppStoremain/Apps/HomeAssistant/icon.png Web UI&#xff1a;http&…

Fabric环境部署-安装Go

安装go语言环境 国内镜像&#xff1a;Go下载 - Go语言中文网 - Golang中文社区 1.选择版本下载后解压&#xff1a;注意go1.11.linux-amd64.tar.gz换成你下的 sudo tar zxvf go1.21.linux-amd64.tar.gz -C /usr/local 2.. 创建Go目录 mkdir $HOME/go 3. 用vi打开~./bashrc&…

慧集通(DataLinkX)iPaaS集成平台-主数据映射管理(多系统间基础档案的映射)

主数据管理 主数据管理主要是解决不同业务系统之间历史数据不统一的问题&#xff0c;在该功能下主要分为三个模块分别为数据对象、应用系统、数据映射&#xff1b; 其中数据对象指的是我们的不同的对象&#xff0c;如&#xff1a;部门、人员、职级、科目、供应商等等&#xff…

Hoverfly 任意文件读取漏洞(CVE-2024-45388)

漏洞简介 Hoverfly 是一个为开发人员和测试人员提供的轻量级服务虚拟化/API模拟/API模拟工具。其 /api/v2/simulation​ 的 POST 处理程序允许用户从用户指定的文件内容中创建新的模拟视图。然而&#xff0c;这一功能可能被攻击者利用来读取 Hoverfly 服务器上的任意文件。尽管…

基于单片机的公交车报站系统设计

引言&#xff1a;单片机应用实践是电类相关专业一门必修的专业技术基础课&#xff0c;其教学目的就是为了使学生能深入了解模拟电路、数字电路、EDA 技术、传感器、单片机原理及其相关接口的综合应用技术&#xff0c;为此我们选了一个典型的实践题目- 公交车报站系统设计&#…

基于Java的超级玛丽游戏的设计与实现【源码+文档+部署讲解】

目 录 1、绪论 1.1背景以及现状 1.2 Java语言的特点 1.3 系统运行环境及开发软件&#xff1a; 1.4 可行性的分析 1.4.1 技术可行性 1.4.2 经济可行性 1.4.3 操作可行性 2、 需求分析 2.1 用户需求分析 2.2功能需求分析 2.3界面设计需求分析…

关于数组的一些应用--------数组作函数的返回值(斐波那契数列数列的实现)

数组在作为函数的返回值&#xff0c;一个很经典的例子就是获取斐波那契数列的前N项 代码思路&#xff1a; 设计思路 输入&#xff1a; 输入一个整数 n&#xff0c;表示要生成斐波那契数列的长度。 输出&#xff1a; 输出一个长度为 n 的整数数组&#xff0c;其中每个元素为斐…

灰度图的Stride和RGB的Stride有什么区别呢?

灰度图&#xff08;Grayscale&#xff09;和RGB图像的步长&#xff08;Stride&#xff09;计算确实有所不同&#xff0c;主要是因为它们每个像素占用的字节数不同。以下是两者的区别及对齐要求&#xff1a; 灰度图&#xff08;Grayscale&#xff09; 每个像素占用的字节数&…

使用WebSocket 获取实时数据

回车发送数据&#xff0c;模拟服务器发送数据 效果图&#xff1a; 源码&#xff1a; <template><div><h1>WebSocket 实时数据</h1><input type"text" v-model"ipt" keyup.enter"sendMessage(ipt)"><div v-if…

Python:交互式物质三态知识讲解小工具

学着物理写着Python 以下是一个使用Python的Tkinter库实现的简单示例程序&#xff0c;通过图形界面展示并讲解固态、液态、气态的一些特点&#xff0c;代码中有详细的注释来帮助你理解各部分功能&#xff1a; 完整代码 import tkinter as tk from tkinter import ttk import …

基于64QAM的载波同步和定时同步性能仿真,包括Costas环和gardner环

目录 1.算法仿真效果 2.算法涉及理论知识概要 3.MATLAB核心程序 4.完整算法代码文件获得 1.算法仿真效果 matlab2022a仿真结果如下&#xff08;完整代码运行后无水印&#xff09;&#xff1a; 仿真操作步骤可参考程序配套的操作视频。 2.算法涉及理论知识概要 载波同步是…

Arduino 小白的 DIY 空气质量检测仪(5)- OLED显示模块、按钮模块

最终章 这一章把剩下的OLED显示模块、按钮模块分享一下&#xff0c;当前这个离线无存储的版本&#xff0c;基本告一段落。 如果后续能进化成&#x1f236;存储、联网版本&#xff0c;就再开一个小系列分享一下。 逐个分析 display.h #include <Arduino.h> #include &l…

基于机器视觉和Dijkstra算法的平面建筑群地图路线规划matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 1.程序功能描述 基于机器视觉和Dijkstra算法的平面建筑群地图路线规划matlab仿真&#xff0c;输入一张平面建筑群的地图&#xff0c;然后通过机器视觉识别地图里面的障碍物&#xff0c;然后通…

计算机的错误计算(二百零一)

摘要 用两个大模型计算 &#xff0c;结果保留 10位有效数字。实验表明&#xff0c;两个大模型的输出均只有1位正确数字&#xff1b;并它们几乎相同&#xff1a;仅最后1位数字不同。 例1. 计算 , 结果保留 10位有效数字。 下面是与一个数学解题器的对话。 以上为与一个数学解…

【Motion Builder】配置c++插件开发环境

目录 准备环境构建官方案例另行构建经验分享附录 准备环境 安装Motion Builder 2024并破解安装Qt 5.15.2 截止至2024年12月19日&#xff0c;Qt的在线安装器的默认页面是没有5.15.2版本的。你需要&#xff1a;在“选择组件”界面&#xff0c;选择“Archive”&#xff0c;点击“…

大学生入学审核系统的设计与实现(源码+数据库+文档)

亲测完美运行带论文&#xff1a;文末获取源码 文章目录 项目简介&#xff08;论文摘要&#xff09;运行视频包含的文件列表&#xff08;含论文&#xff09;后台运行截图 项目简介&#xff08;论文摘要&#xff09; 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理…