河北邯郸网站建设公司wordpress 去掉 index.php

张小明 2026/1/5 21:44:48
河北邯郸网站建设公司,wordpress 去掉 index.php,三大外包公司,城乡建设杂志社官方网站常规控制流#xff1a;程序正常执行的指令流向#xff0c;通过branch#xff08;条件分支#xff09;、jump#xff08;无条件跳转#xff09;指令改变执行顺序#xff0c;是处理器的常规工作状态。异常控制流#xff08;ECP#xff09;#xff1a;打破常规控制流的特…常规控制流程序正常执行的指令流向通过branch条件分支、jump无条件跳转指令改变执行顺序是处理器的常规工作状态。异常控制流ECP打破常规控制流的特殊情况分为两类exception异常由程序自身错误触发比如执行非法指令、访问不存在的内存地址interrupt中断由外部事件触发比如定时器到期、外部设备发送数据请求Trap 的定义RISC-V 架构将所有异常控制流统一称为Trap无论是异常还是中断都通过一套统一的机制处理。Machine 模式下完整 CSR 寄存器列表CSR控制状态寄存器是 RISC-V 处理 Trap 的核心Machine 模式最高权限模式下的 CSR 有明确的地址和权限划分下表为 PPT 中列出的完整列表同时解释关键权限缩写权限说明MROMachine 模式只读、MRWMachine 模式可读可写寄存器地址权限寄存器名功能描述新手理解要点机器信息类寄存器---用于识别硬件身份一般无需修改0xF11MROmvendorid厂商 ID区分不同芯片厂商的硬件0xF12MROmarchid架构 ID标识 RISC-V 架构版本0xF13MROmimpid实现 ID标识芯片的具体实现型号0xF14MROmhartid硬件线程 ID区分同一个处理器核内的不同 hartTrap 配置类寄存器---用于设置 Trap 的基础参数0x300MRWmstatus机器状态寄存器跟踪 hart 运行状态、控制全局中断开关核心寄存器Trap 处理时会自动修改其字段0x301MRWmisaISA 及扩展标识当前支持的 RISC-V 指令集扩展比如是否支持浮点指令、向量指令0x302MRWmedeleg机器异常委派寄存器配置异常是否委派给低权限模式处理新手初期可先不关注默认由 Machine 模式处理所有异常0x303MRWmideleg机器中断委派寄存器配置中断是否委派给低权限模式处理同上新手可先默认 Machine 模式处理所有中断0x304MRWmie机器中断使能寄存器精细化控制软件 / 定时器 / 外部中断的开关可单独打开或关闭某一类中断0x305MRWmtvec机器 Trap 向量基地址保存 Trap 发生时的跳转入口地址Trap 处理的 “总入口”必须配置0x306MRWmcounteren机器计数器使能寄存器控制性能计数器的访问权限新手初期学习 Trap 可暂不关注Trap 处理类寄存器---Trap 发生时的核心工作寄存器0x340MRWmscratch机器临时寄存器Machine 模式 Trap 处理的专用临时寄存器可用于保存任务上下文地址自定义用途0x341MRWmepc机器异常程序计数器保存 Trap 发生时的指令地址用于 Trap 处理完成后返回原执行流程0x342MRWmcause机器 Trap 原因寄存器标识 Trap 的具体类型中断 / 异常 具体编码软件通过它判断 Trap 来源0x343MRWmtval机器 Trap 值寄存器保存 Trap 的附加信息比如地址错误时的错误地址、非法指令的指令内容0x344MRWmip机器中断挂起寄存器标记当前等待处理的中断可通过它查询有哪些中断未处理内存保护类寄存器---控制物理内存访问权限0x3A0MRWpmpcfg0物理内存保护配置配置内存区域的访问权限新手初期学习 Trap 可暂不深入0x3BFMRWpmpaddr15物理内存保护地址寄存器保存内存保护区域的地址同上结构位域位段长度含义约束BASE[MXLEN-1:2]MXLEN-2 位Trap 入口函数的基地址必须四字节对齐MODE[1:0]2 位Trap 入口的地址配置方式WARL写任意值读合法值核心功能BASE指定 Trap 发生时处理器要跳转到的总入口地址比如代码中entry.S的trap_vector。MODE控制 Trap 入口的分配方式只有 2 种合法值参考 PPT 表格0Direct模式所有异常 / 中断都跳转到BASE地址我的代码用的就是这种所有 Trap 走同一个入口。1Vectored模式异常跳转到BASE中断跳转到BASE 4×cause不同中断有独立入口比如定时器中断走BASE4×7。// trap.c中初始化mtvec将BASE设为trap_vectorentry.S中的汇编标签 void trap_init() { w_mtvec((reg_t)trap_vector); // w_mtvec是riscv.h封装的csrw指令 }四字节对齐BASE 的低 2 位必须是 0硬件强制所以代码中trap_vector用.balign 4保证对齐。WARL 含义你可以给 mtvec 写任意值但硬件会自动把不合法的位比如 BASE 低 2 位非 0修正为合法值读的时候只能得到合法结果。结构位域整个寄存器MXLEN 位存储一个地址无分段。核心功能Trap 发生时硬件自动把当前或下一条指令的地址存入 mepc异常比如非法指令存导致异常的指令地址中断比如定时器存被中断指令的下一条地址。Trap 返回时执行mret指令硬件会把 mepc 的值恢复到 PC实现 “回到 Trap 发生前的位置”。可修改在 Trap 处理函数中可以修改 mepc比如跳过错误指令改变mret的返回地址。// trap.c中trap_handler返回新的mepc写回硬件 reg_t trap_handler(reg_t epc, reg_t cause) { reg_t return_pc epc; if (cause_code 7) { // 存储访问错误 return_pc 4; // 跳过错误指令4字节 } return return_pc; // 后续会csrw mepc, a0写回 }新手注意点mret 与 ret 的区别mret是 Trap 专用返回指令依赖 mepc普通函数返回用ret依赖 ra 寄存器。结构位域位段长度含义Interrupt[MXLEN-1]1 位Trap 类型1 中断0 异常Exception Code[MXLEN-2:0]MXLEN-1 位Trap 的具体类型编码核心功能标识 Trap 原因硬件在 Trap 发生时自动填充最高位区分 “中断异步” 和 “异常同步”剩下的编码对应具体类型参考下图常用编码Trap 类型Interrupt 位Exception Code场景示例软件中断13未开启代码中暂未用到定时器中断17后续开启定时器后触发调度外部中断111UART 输入触发加载访问错误05int a *(int *)0x0;存储访问错误07代码中trap_test写 0x0非法指令02执行不存在的指令// trap.c中通过mcause区分中断/异常 reg_t trap_handler(reg_t epc, reg_t cause) { if (cause MCAUSE_MASK_INTERRUPT) { // 最高位为1 → 中断 switch (cause MCAUSE_MASK_ECODE) { // 提取编码 case 7: uart_puts(timer interruption!\n); break; } } else { // 异常 panic(Sync exceptions! Code %ld\n, cause_code); } }结构位域整个寄存器MXLEN 位存储辅助信息无分段。核心功能提供 Trap 的附加调试信息硬件自动填充无信息时为 0常见场景地址错误加载 / 存储存错误的内存地址比如写 0x0 时mtval0x0非法指令存导致错误的指令本身其他 Trap可能为 0依硬件实现。注意点RISC-V 规范只定义了部分场景的 mtval 内容具体由硬件实现比如 QEMU 完全遵循规范调试时打印 mtval 能快速定位错误比如地址错误时直接看 mtval 的值。关键位段位段含义Trap 发生时的行为xIE控制对应权限级别的全局中断xM/S/U比如 MIEMachine 中断使能自动置 0关闭当前权限的中断xPIE保存 Trap 发生前的 xIE 值比如 MPIE 保存原 MIE自动填充为 Trap 前的 xIE 值xPP保存 Trap 发生前的权限级别xM/S无 UPP自动填充为 Trap 前的权限然后切换到 M 模式权限级别编码xPP 的值xPP 值权限级别缩写00User用户U01Supervisor监管S11Machine机器M核心功能跟踪处理器的运行状态中断使能、权限级别Trap 发生时自动 “保护现场”保存原中断状态、权限Trap 返回时通过mret恢复。注意点Trap 后的权限切换无论 Trap 发生前是 U/S 模式Trap 触发后会强制切换到 M 模式最高权限mret 的恢复逻辑mret会把 xPP 恢复为原权限把 xPIE 恢复到 xIE。总结这 5 个寄存器的 Trap 协作流程Trap 发生 → 硬件自动把当前 PC 存到mepc填充mcause类型 编码、mtval附加信息修改mstatusxIE 置 0保存原状态 / 权限PC 跳转到mtvec的 BASE 地址。软件处理entry.S trap.c保存寄存器上下文读取mepc/mcause处理逻辑比如跳过错误指令Trap 返回mret硬件从mepc恢复 PC从mstatus恢复中断状态、权限。Trap 处理涉及的核心寄存器RISC-V 在 Machine 模式下通过一系列控制状态寄存器CSR完成 Trap 的配置与处理关键寄存器及用途如下寄存器核心用途mtvecMachine Trap-Vector Base-Address核心功能保存 Trap 处理入口的基地址必须四字节对齐位域划分高位MXLEN-1:2BASE 段即 Trap 入口基地址低位1:0MODE 段控制 Trap 入口的配置模式两种模式新手需区分清楚Direct 模式所有异常和中断都跳转到 BASE 指定的地址处理逻辑集中Vectored 模式异常仍跳转到 BASE 地址中断则按类型跳转到BASE 4×中断编号的地址不同中断有独立入口mepcMachine Exception Program Counter核心功能记录 Trap 发生时的指令地址用于 Trap 返回关键区别若为异常触发的 Trapmepc 保存导致异常的指令地址若为中断触发的 Trapmepc 保存被中断指令的下一条指令地址特殊用法可在软件中修改 mepc 的值从而改变mret指令的返回地址mcauseMachine Cause核心功能告知软件 Trap 的具体原因位域划分最高位MXLEN-1Interrupt 位值为 1 表示是中断值为 0 表示是异常剩余位MXLEN-2:0Exception Code 段是具体的中断 / 异常编码可在参考文档 2 的 Table3.6 中查询编码对应的具体类型mtvalMachine Trap Value核心功能提供 Trap 的附加辅助信息无信息时值为 0典型场景内存访问错误mtval 保存错误的内存地址非法指令错误mtval 保存触发错误的指令本身mstatusMachine Status核心功能控制 hart 的运行状态重点关注 3 组字段新手只需掌握这 3 组xIExM/S/U对应权限级别的全局中断开关Trap 发生时会自动置 0关闭中断xPIExM/S/U保存 Trap 发生前的xIE值用于 Trap 返回时恢复中断状态xPPxM/S保存 Trap 发生前的权限级别Trap 触发后 hart 会强制切换到 Machine 模式mscratchMachine Scratch核心功能Machine 模式专用临时寄存器用途可自定义新手常用用法保存当前运行任务的上下文寄存器集合的内存地址方便 Trap 处理时快速访问上下文mieMachine Interrupt Enable核心功能精细化控制三类中断的开关可单独打开 / 关闭软件中断定时器中断外部中断mipMachine Interrupt Pending核心功能标记当前已经发生、但还未处理的中断软件可通过查询该寄存器了解待处理中断的情况RISC-V Trap 处理流程Trap 处理分为初始化、Top Half、Bottom Half、返回四个阶段具体流程如下Trap 初始化核心操作通过w_mtvec((reg_t)trap_vector)设置 Machine 模式的 Trap 向量基地址指定 Trap 处理入口。汇编层面需完成上下文初步保存如通过csrrw指令交换t6与mscratch再调用reg_save保存寄存器上下文。Trap 触发后的硬件自动状态转换中断状态切换将mstatus.MIE值复制到MPIE并清除MIE以关闭全局中断。地址与权限切换设置mepc保存指令地址同时将 PC 更新为mtvec指定的地址将原权限级别存入mstatus.MPP并切换到 Machine 模式。原因与附加信息记录通过mcause标记 Trap 类型通过mtval写入附加信息。软件层面的 Trap 处理Top Half Bottom Half保存上下文利用mscratch完成当前控制流的寄存器上下文保存。调用 C 语言处理函数将mepc、mcause传入trap_handler函数执行具体的 Trap 逻辑如异常修复、中断响应必要时调整mepc以修改返回地址。恢复上下文通过reg_restore指令恢复寄存器信息。从 Trap 返回执行mret指令触发硬件自动操作将 hart 权限级别恢复为mstatus.MPP的值且mstatus.MPP会重置为 U 模式无 U 模式则为 M 模式。将mstatus.MPIE的值恢复到MIE并重置MPIE为 1。将mepc的值恢复到 PC回到 Trap 发生前的执行流程。start.S系统启动→ kernel.c初始化链路→ sched.c任务创建/调度→ entry.Sswitch_to任务切换→ user.c任务执行触发Trap→ entry.Strap_vector汇编处理→ trap.cTrap逻辑处理→ entry.S上下文恢复mret返回一、系统启动阶段start.S纯汇编硬件级初始化#start.S核心作用RISC-V 上电后 PC 指向 0x80000000os.ld 指定的 RAM 起始地址start.S是第一个执行的代码完成 hart 隔离、BSS 清零、栈初始化。#include platform.h # 每个hart的栈大小1024字节 .equ STACK_SIZE, 1024 .global _start # 导出_start标签os.ld指定ENTRY(_start) .text _start: # 步骤1隔离非0号hart只让hart0运行其他休眠 csrr t0, mhartid # 读mhartid寄存器到t0底层csrr指令读CSR mv tp, t0 # tp寄存器存hartid扩展RISC-V调用约定中tp是线程指针这里复用存hartid bnez t0, park # t0≠0非hart0跳转到park标签休眠 # 步骤2清零BSS段未初始化全局变量必须手动清零 la a0, _bss_start # 加载_bss_start地址到a0底层laload address伪指令实际是auipcaddi la a1, _bss_end # 加载_bss_end地址到a1 bgeu a0, a1, 2f # a0≥a1BSS为空跳转到2f标签避免无效操作 1: sw zero, (a0) # 向a0指向的地址写0底层swstore word32位存储指令 addi a0, a0, 4 # a0432位系统每次清零4字节 bltu a0, a1, 1b # a0a1跳回1b标签循环清零 2: # 步骤3初始化每个hart的栈扩展RISC-V栈向下生长sp指向栈顶 slli t0, t0, 10 # t0 10 → t0*1024每个hart栈大小1024 la sp, stacks STACK_SIZE # sp指向stacks起始1024hart0栈顶 add sp, sp, t0 # 非hart0的sp偏移t0字节每个hart栈独立 j start_kernel # 跳转到kernel.c的start_kernel函数底层j无条件跳转 park: wfi # 休眠指令底层WFIWait For Interrupthart进入低功耗直到中断触发 j park # 无限循环休眠 # 扩展RISC-V调用约定要求sp16字节对齐这里.balign 16保证 .balign 16 stacks: .skip STACK_SIZE * MAXNUM_CPU # 为所有hart分配栈空间MAXNUM_CPU8共8*1024字节 .end # 汇编文件结束BSS 段清零原因编译器不会自动初始化 BSS 段未清零会导致全局变量值随机引发程序异常栈对齐要求RISC-V 官方调用约定ABI要求 sp 必须 16 字节对齐否则函数调用如 call 指令会触发未定义行为hart 隔离意义多核心系统中初始化阶段只让一个核心hart0执行避免竞态条件新手暂时先理解为 “防止多个核心同时初始化导致错误”。二、初始化链路kernel.cC 语言入口串联所有模块核心作用作为 C 语言总入口按顺序初始化外设、内存、Trap、调度器创建用户任务并启动调度。#include os.h /* 声明外部初始化函数只调用一次不放入os.h */ extern void uart_init(void); extern void page_init(void); extern void sched_init(void); extern void schedule(void); extern void os_main(void); extern void trap_init(void); void start_kernel(void) { // 步骤1初始化UART串口用于打印日志Trap调试核心依赖 uart_init(); uart_puts(Hello, RVOS!\n); // 步骤2初始化页内存管理为任务栈/堆分配内存 page_init(); // 步骤3初始化Trap设置Trap向量入口 trap_init(); // 步骤4初始化调度器设置mscratch0 sched_init(); // 步骤5创建用户任务task0/task1 os_main(); // 步骤6启动调度器首次任务切换 schedule(); // 理论上不会执行到这里调度器无限循环 uart_puts(Would not go here!\n); while (1) {}; }底层扩展函数调用顺序必须先初始化 UART否则无法打印调试信息再初始化内存否则任务栈无内存可用再初始化 Trap否则异常无法处理schedule () 的意义首次调用会触发第一个任务task0的执行此后调度器无限循环切换任务不会返回。三、Trap 初始化trap.c riscv.h对接硬件 CSR#trap.c#include os.h // 引入系统核心头文件包含reg_t/ptr_t类型定义、uart_puts/printf/panic函数声明、CSR操作函数如w_mtvec、MCAUSE相关掩码宏 // 声明entry.S中定义的trap_vector汇编标签Trap总入口用于写入mtvec寄存器 extern void trap_vector(void); /** * brief Trap初始化函数系统启动时由kernel.c的start_kernel调用 * note 核心作用将Machine模式的Trap向量基地址设置为trap_vector所有Trap的汇编入口 */ void trap_init() { /* * set the trap-vector base-address for machine-mode * 底层原理w_mtvec是riscv.h封装的csrw指令csrw mtvec, %0将trap_vector的地址写入mtvec寄存器 * 硬件行为当Trap中断/异常发生时CPU会自动跳转到mtvec指向的trap_vector地址执行 * 注意trap_vector必须4字节对齐entry.S中用.balign 4保证否则硬件无法识别 */ w_mtvec((reg_t)trap_vector); // (reg_t)将汇编标签地址转为32位寄存器类型匹配w_mtvec参数 } /** * brief Trap核心处理函数由entry.S的trap_vector汇编代码调用 * param epc: mepc寄存器的值Trap发生时的指令地址由entry.S通过csrr a0, mepc传入 * param cause: mcause寄存器的值Trap原因由entry.S通过csrr a1, mcause传入 * return reg_t: 新的mepc值entry.S会通过csrw mepc, a0写回硬件决定mret的返回地址 * note 核心逻辑区分异步中断、同步异常分别处理 */ reg_t trap_handler(reg_t epc, reg_t cause) { reg_t return_pc epc; // 初始化返回地址为原epc默认返回Trap发生的位置 // 提取Trap具体编码MCAUSE_MASK_ECODE0x7FFFFFFF掩码保留mcause低31位剥离最高位的中断/异常标记 reg_t cause_code cause MCAUSE_MASK_ECODE; // 判断是否为中断异步TrapMCAUSE_MASK_INTERRUPT0x80000000仅mcause最高位为1时表示中断 if (cause MCAUSE_MASK_INTERRUPT) { /* Asynchronous trap - interrupt异步中断由外部事件触发如定时器、外设 */ switch (cause_code) { case 3: // Machine软件中断编码3由ecall指令或软件写mip寄存器触发 uart_puts(software interruption!\n); // 串口打印中断类型新手阶段仅打印无实际处理 break; case 7: // Machine定时器中断编码7由CLINT定时器触发是时间片调度的核心 uart_puts(timer interruption!\n); break; case 11: // Machine外部中断编码11由PLIC转发如UART/网卡等外设触发 uart_puts(external interruption!\n); break; default: // 未识别的中断编码 printf(Unknown async exception! Code %ld\n, cause_code); break; } } else { /* Synchronous trap - exception同步异常由程序自身错误触发如非法指令、内存访问错误 */ printf(Sync exceptions! Code %ld\n, cause_code); // 打印异常编码方便调试 panic(OOPS! What can I do!); // 系统崩溃函数进入无限循环防止重复触发异常 //return_pc 4; // 关键修复逻辑RISC-V指令4字节4跳过错误指令让mret返回下一条指令取消注释可让程序继续执行 } return return_pc; // 返回新的mepc值entry.S会写回硬件mret根据该值返回 } /** * brief Trap测试函数由user.c的user_task0调用用于触发同步异常 * note 核心作用通过非法内存访问触发异常验证Trap处理逻辑 */ void trap_test() { /* * Synchronous exception code 7存储访问错误 * 底层原理0x00000000是未映射地址RVOS内核从0x80000000开始写该地址触发硬件异常 */ *(int *)0x00000000 100; // 向0x0地址写入100触发Store/AMO access fault编码7 /* * Synchronous exception code 5加载访问错误 * 注释原因避免和存储错误冲突取消注释可测试加载错误编码5 */ //int a *(int *)0x00000000; // 从0x0地址读取值触发Load access fault编码5 // 注意触发异常后会进入trap_handler并panic该语句默认无法执行修改trap_handler跳过错误指令后可执行 uart_puts(Yeah! Im return back from trap!\n); }整体调用链路系统启动 → trap_init()注册trap_vector到mtvec → user_task0调用trap_test → 非法写0x0触发异常 → 硬件跳转到trap_vectorentry.S→ 保存寄存器上下文 → 调用trap_handler → 识别异常编码7 → 执行panic死循环→ 修改后跳过错误指令 → mret返回继续执行中断 / 异常区分通过mcause最高位判断1 中断 / 异步0 异常 / 同步编码部分通过掩码提取mepc 的作用异常时存 “错误指令地址”中断时存 “下一条指令地址”修改return_pc可控制返回位置实战修改取消trap_handler中return_pc 4的注释可让trap_test跳过错误指令执行后续的uart_puts。核心函数作用trap_init完成 Trap 入口的 “硬件注册”是所有 Trap 处理的前提trap_handlerTrap 逻辑处理的核心新手阶段仅打印日志实际 OS 中会在定时器中断中调用schedule()实现任务切换trap_test异常触发用例验证 Trap 处理逻辑是否生效。#riscv.h:该文件是RISC-V 特权架构与 C 语言的桥梁通过内嵌汇编封装 CSR 寄存器的读写操作CSR 指令为特权指令无法直接用 C 实现所有函数为static inline静态内联避免函数调用开销直接嵌入到调用处如trap.c的w_mtvec、sched.c的w_mscratch。#ifndef __RISCV_H__ #define __RISCV_H__ #include types.h // 引入类型定义头文件核心是reg_t32位寄存器类型对应uint32_t /* * ref: https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/riscv.h * 说明该文件封装RISC-V Machine模式下CSR控制状态寄存器的读写操作 * 所有函数均为static inline静态内联避免函数调用开销直接嵌入调用处 */ /** * brief 读取tp寄存器线程指针的值 * return reg_t: tp寄存器的值RVOS中用于存储hartid即核心编号 * 内嵌汇编说明 * - mv %0, tp将tp寄存器的值移动到%0对应变量x * - r (x)输出约束%0使用通用寄存器结果写入x表示输出 */ static inline reg_t r_tp() { reg_t x; asm volatile(mv %0, tp : r (x) ); return x; } /** * brief 读取mhartid寄存器Machine Hart ID的值获取当前核心编号 * return reg_t: 当前核心hart的ID多核心系统中区分不同核心 * 内嵌汇编说明 * - csrr %0, mhartidCSR Read指令读取mhartid寄存器到%0x * - r (x)输出约束结果写入x */ static inline reg_t r_mhartid() { reg_t x; asm volatile(csrr %0, mhartid : r (x) ); return x; } /* -------------------------- mstatus寄存器相关宏定义 -------------------------- */ // mstatusMachine Status Register机器状态寄存器控制中断使能、权限级别等 #define MSTATUS_MPP (3 11) // MPPMachine Previous Privilege前序权限级别位11-12311b表示Machine模式 #define MSTATUS_SPP (1 8) // SPPSupervisor Previous Privilege前序S模式权限位8 #define MSTATUS_MPIE (1 7) // MPIEMachine Previous Interrupt Enable前序M模式中断使能位7 #define MSTATUS_SPIE (1 5) // SPIESupervisor Previous Interrupt Enable前序S模式中断使能位5 #define MSTATUS_UPIE (1 4) // UPIEUser Previous Interrupt Enable前序U模式中断使能位4 #define MSTATUS_MIE (1 3) // MIEMachine Interrupt EnableM模式全局中断使能位3 #define MSTATUS_SIE (1 1) // SIESupervisor Interrupt EnableS模式全局中断使能位1 #define MSTATUS_UIE (1 0) // UIEUser Interrupt EnableU模式全局中断使能位0 /** * brief 读取mstatus寄存器的值 * return reg_t: mstatus寄存器的当前值 */ static inline reg_t r_mstatus() { reg_t x; asm volatile(csrr %0, mstatus : r (x) ); return x; } /** * brief 写入值到mstatus寄存器 * param x: 要写入的32位值 * 内嵌汇编说明 * - csrw mstatus, %0CSR Write指令将%0x写入mstatus寄存器 * - : : r (x)输入约束%0使用通用寄存器值来自x无输出约束 */ static inline void w_mstatus(reg_t x) { asm volatile(csrw mstatus, %0 : : r (x)); } /* -------------------------- mepc寄存器相关操作 -------------------------- */ /* * machine exception program counter, holds the * instruction address to which a return from * exception will go. * 说明mepcMachine Exception Program Counter异常返回地址寄存器 * 存储Trap发生时的指令地址mret指令会将其值恢复到PC */ /** * brief 写入值到mepc寄存器控制mret的返回地址 * param x: 要写入的指令地址必须4字节对齐 */ static inline void w_mepc(reg_t x) { asm volatile(csrw mepc, %0 : : r (x)); } /** * brief 读取mepc寄存器的值获取Trap发生时的指令地址 * return reg_t: mepc当前存储的地址 */ static inline reg_t r_mepc() { reg_t x; asm volatile(csrr %0, mepc : r (x)); return x; } /* -------------------------- mscratch寄存器相关操作 -------------------------- */ /* Machine Scratch register, for early trap handler */ // 说明mscratchMachine Scratch Register机器临时寄存器 // RVOS中用于存储当前任务的context指针上下文切换核心 /** * brief 写入值到mscratch寄存器 * param x: 要写入的值RVOS中为context结构体指针 */ static inline void w_mscratch(reg_t x) { asm volatile(csrw mscratch, %0 : : r (x)); } /* -------------------------- mtvec寄存器相关操作 -------------------------- */ /* Machine-mode interrupt vector */ // 说明mtvecMachine Trap-Vector Base AddressTrap向量基地址寄存器 // 存储Trap发生时CPU跳转的入口地址如entry.S的trap_vector /** * brief 写入值到mtvec寄存器注册Trap总入口 * param x: Trap入口地址必须4字节对齐低2位为0 */ static inline void w_mtvec(reg_t x) { asm volatile(csrw mtvec, %0 : : r (x)); } /* -------------------------- mie寄存器相关宏定义与操作 -------------------------- */ /* Machine-mode Interrupt Enable */ // mieMachine Interrupt Enable机器中断使能寄存器控制各类中断的开关 #define MIE_MEIE (1 11) // MEIEMachine External Interrupt Enable外部中断使能位11 #define MIE_MTIE (1 7) // MTIEMachine Timer Interrupt Enable定时器中断使能位7 #define MIE_MSIE (1 3) // MSIEMachine Software Interrupt Enable软件中断使能位3 /** * brief 读取mie寄存器的值查看当前开启的中断类型 * return reg_t: mie寄存器当前值 */ static inline reg_t r_mie() { reg_t x; asm volatile(csrr %0, mie : r (x) ); return x; } /** * brief 写入值到mie寄存器开启/关闭指定中断 * param x: 要写入的值通过|MIE_MTIE等宏开启对应中断 */ static inline void w_mie(reg_t x) { asm volatile(csrw mie, %0 : : r (x)); } /* -------------------------- mcause寄存器相关宏定义与操作 -------------------------- */ /* Machine-mode Cause Masks */ // mcauseMachine Cause RegisterTrap原因寄存器掩码用于提取关键信息 #define MCAUSE_MASK_INTERRUPT (reg_t)0x80000000 // 中断标记掩码最高位1中断/异步0异常/同步 #define MCAUSE_MASK_ECODE (reg_t)0x7FFFFFFF // 异常/中断编码掩码低31位提取具体Trap类型 /** * brief 读取mcause寄存器的值获取Trap原因 * return reg_t: mcause当前值结合掩码解析中断/异常类型 */ static inline reg_t r_mcause() { reg_t x; asm volatile(csrr %0, mcause : r (x) ); return x; } #endif /* __RISCV_H__ */关键技术点CSR 指令csrr rd, csr读 CSR 寄存器到通用寄存器 rd如csrr %0, mhartidcsrw csr, rs写通用寄存器 rs 到 CSR 寄存器如csrw mtvec, %0内嵌汇编约束r (x)输出约束%0 使用通用寄存器结果写入 xr (x)输入约束%0 使用通用寄存器值来自 x常用操作示例开启定时器中断w_mie(r_mie() | MIE_MTIE);先读当前 mie 值再按位或 MTIE 位读取 Trap 原因编码reg_t code r_mcause() MCAUSE_MASK_ECODE;判断是否为中断if(r_mcause() MCAUSE_MASK_INTERRUPT)举个例子w_mie(r_mie() | MIE_MTIE);先读当前 mie 值再按位或 MTIE 位#define MIE_MTIE (1 7)是典型的「位标记宏」作用是把数字 1 移动到指定的二进制位上专门用来标记寄存器的某一位二进制1 70000 0000 0000 0000 0000 0000 1000 000032 位第 7 位为 1其余为 0步骤 1r_mie()—— 读取当前 MIE 寄存器的所有位值// 来自riscv.h的函数作用是读取MIE寄存器的完整32位值 static inline reg_t r_mie() { reg_t x; asm volatile(csrr %0, mie : r (x) ); // 执行csrr指令读mie到x return x; }假设当前 MIE 寄存器的值是0x08即1 3软件中断已开启那么r_mie()返回0x08二进制0000 0000 0000 0000 0000 0000 0000 1000。核心目的先读取当前中断使能状态避免直接写入覆盖其他已开启的中断比如软件中断。步骤 2| MIE_MTIE—— 按位或仅开启定时器中断位按位或|的规则是只要对应位有一个为 1结果就为 1全 0 才为 0。当前MIE值r_mie()返回0000 0000 0000 0000 0000 0000 0000 1000 0x08软件中断开启 | MIE_MTIE17 0000 0000 0000 0000 0000 0000 1000 0000 0x80 ---------------------------------------------------------------------- 结果 0000 0000 0000 0000 0000 0000 1000 1000 0x88步骤 3w_mie(...)—— 把修改后的值写回 MIE 寄存器上面例子中最终写入 MIE 的值是0x88此时第 3 位MSIE1 → 软件中断仍开启第 7 位MTIE1 → 定时器中断新开启其他位 0 → 外部中断等仍关闭。四、任务管理与调度sched.c os.h entry.Sswitch_to核心作用实现任务创建、FIFO 调度、上下文切换核心是switch_to汇编函数。模块 1os.h - 上下文结构体定义数据载体struct context { reg_t ra; // 返回地址任务入口/切换点 reg_t sp; // 栈指针任务栈顶 reg_t gp; // 全局指针未使用保留 reg_t tp; // 线程指针存hartid未切换 reg_t t0; // 临时寄存器 reg_t t1; reg_t t2; reg_t s0; // 保存寄存器调用者需保存 reg_t s1; reg_t a0; // 参数/返回值寄存器 reg_t a1; reg_t a2; reg_t a3; reg_t a4; reg_t a5; reg_t a6; reg_t a7; reg_t s2; reg_t s3; reg_t s4; reg_t s5; reg_t s6; reg_t s7; reg_t s8; reg_t s9; reg_t s10; reg_t s11; reg_t t3; reg_t t4; reg_t t5; reg_t t6; // 临时寄存器最后一个单独保存 };寄存器分类RISC-V 将寄存器分为临时寄存器t0-t6调用者不保存、保存寄存器s0-s11调用者必须保存、参数寄存器a0-a7上下文切换需保存所有寄存器除 gp/tpra 的作用任务创建时 ra 存任务入口函数地址如 user_task0任务切换时 ra 存切换点地址。模块 2sched.c - 核心函数拆解1. sched_init调度器初始化void sched_init() { w_mscratch(0); // 底层写mscratch寄存器为0首次切换时处理 // 扩展mscratch是Machine模式临时寄存器这里用来存当前任务的context指针 }2. task_create任务创建#define MAX_TASKS 10 #define STACK_SIZE 1024 uint8_t __attribute__((aligned(16))) task_stack[MAX_TASKS][STACK_SIZE]; // 任务栈16字节对齐 struct context ctx_tasks[MAX_TASKS]; // 任务上下文数组 static int _top 0; // 任务总数 static int _current -1; // 当前运行任务ID int task_create(void (*start_routin)(void)) { if (_top MAX_TASKS) { // 步骤1设置任务栈指针栈向下生长sp指向栈顶 ctx_tasks[_top].sp (reg_t) task_stack[_top][STACK_SIZE]; // 步骤2设置任务入口地址ra存函数指针 ctx_tasks[_top].ra (reg_t) start_routin; _top; return 0; } else { return -1; } }3. scheduleFIFO 调度器void schedule() { if (_top 0) { panic(Num of task should be greater than zero!); return; } // 循环选择下一个任务FIFO _current (_current 1) % _top; struct context *next (ctx_tasks[_current]); switch_to(next); // 调用entry.S的switch_to切换上下文 }模块 3entry.S - switch_to上下文切换核心汇编# void switch_to(struct context *next); # a0入参指向新任务的context .globl switch_to .balign 4 # 地址4字节对齐 switch_to: # 步骤1交换t6和mscratch获取当前任务context指针 csrrw t6, mscratch, t6 # 扩展csrrwCSR Read/Write交换t6和mscratch的值 # - 首次切换时mscratch0 → t60跳过保存 # - 非首次切换时mscratch存当前任务context指针 → t6指针 # 步骤2首次切换t60跳转到1f标签跳过保存 beqz t6, 1f # 步骤3保存当前任务的寄存器上下文到t6指向的内存 reg_save t6 # 步骤4单独保存t6寄存器reg_save中未保存 mv t5, t6 # t5 当前任务context指针 csrr t6, mscratch # 从mscratch读回原t6值 STORE t6, 30*SIZE_REG(t5) # 保存t6到context的t6字段30*4字节位置 1: # 步骤5更新mscratch为新任务的context指针 csrw mscratch, a0 # 步骤6恢复新任务的寄存器上下文 mv t6, a0 # t6 新任务context指针 reg_restore t6 # 从context恢复所有寄存器 # 步骤7返回执行新任务的代码ra存任务入口 ret # 扩展ret指令会跳转到ra寄存器指向的地址任务入口/切换点底层扩展csrrw 的妙用mscratch 不能直接作为 load/store 的基地址RISC-V 指令限制所以用 t6 作为中转reg_save/reg_restore见下文 Trap 处理部分和 Trap 的上下文保存逻辑完全复用保证切换一致性ret 指令的作用任务首次执行时ra 存任务入口函数如 user_task0ret 会跳转到该函数非首次执行时ra 存切换点地址ret 回到切换前的位置。五、Trap 处理核心entry.Strap_vector trap.ctrap_handler核心作用Trap 触发后先通过汇编保存上下文再调用 C 函数处理逻辑最后恢复上下文并返回。模块 1entry.S - 宏定义寄存器保存 / 恢复#define LOAD lw # 加载指令32位 #define STORE sw # 存储指令32位 #define SIZE_REG 4 # 寄存器大小32位4字节 # 保存通用寄存器到context除gp/tp/t6 .macro reg_save base STORE ra, 0*SIZE_REG(\base) # ra → base0 STORE sp, 1*SIZE_REG(\base) # sp → base4 STORE t0, 4*SIZE_REG(\base) # t0 → base16跳过gp/tp STORE t1, 5*SIZE_REG(\base) STORE t2, 6*SIZE_REG(\base) STORE s0, 7*SIZE_REG(\base) STORE s1, 8*SIZE_REG(\base) STORE a0, 9*SIZE_REG(\base) STORE a1, 10*SIZE_REG(\base) STORE a2, 11*SIZE_REG(\base) STORE a3, 12*SIZE_REG(\base) STORE a4, 13*SIZE_REG(\base) STORE a5, 14*SIZE_REG(\base) STORE a6, 15*SIZE_REG(\base) STORE a7, 16*SIZE_REG(\base) STORE s2, 17*SIZE_REG(\base) STORE s3, 18*SIZE_REG(\base) STORE s4, 19*SIZE_REG(\base) STORE s5, 20*SIZE_REG(\base) STORE s6, 21*SIZE_REG(\base) STORE s7, 22*SIZE_REG(\base) STORE s8, 23*SIZE_REG(\base) STORE s9, 24*SIZE_REG(\base) STORE s10, 25*SIZE_REG(\base) STORE s11, 26*SIZE_REG(\base) STORE t3, 27*SIZE_REG(\base) STORE t4, 28*SIZE_REG(\base) STORE t5, 29*SIZE_REG(\base) # t6单独保存因为t6用作base指针 .endm # 从context恢复通用寄存器包括t6 .macro reg_restore base LOAD ra, 0*SIZE_REG(\base) LOAD sp, 1*SIZE_REG(\base) LOAD t0, 4*SIZE_REG(\base) LOAD t1, 5*SIZE_REG(\base) LOAD t2, 6*SIZE_REG(\base) LOAD s0, 7*SIZE_REG(\base) LOAD s1, 8*SIZE_REG(\base) LOAD a0, 9*SIZE_REG(\base) LOAD a1, 10*SIZE_REG(\base) LOAD a2, 11*SIZE_REG(\base) LOAD a3, 12*SIZE_REG(\base) LOAD a4, 13*SIZE_REG(\base) LOAD a5, 14*SIZE_REG(\base) LOAD a6, 15*SIZE_REG(\base) LOAD a7, 16*SIZE_REG(\base) LOAD s2, 17*SIZE_REG(\base) LOAD s3, 18*SIZE_REG(\base) LOAD s4, 19*SIZE_REG(\base) LOAD s5, 20*SIZE_REG(\base) LOAD s6, 21*SIZE_REG(\base) LOAD s7, 22*SIZE_REG(\base) LOAD s8, 23*SIZE_REG(\base) LOAD s9, 24*SIZE_REG(\base) LOAD s10, 25*SIZE_REG(\base) LOAD s11, 26*SIZE_REG(\base) LOAD t3, 27*SIZE_REG(\base) LOAD t4, 28*SIZE_REG(\base) LOAD t5, 29*SIZE_REG(\base) LOAD t6, 30*SIZE_REG(\base) # 恢复t6 .endm模块 2entry.S - trap_vectorTrap 总入口.globl trap_vector .balign 4 trap_vector: # 步骤1交换t6和mscratch获取当前任务context指针 csrrw t6, mscratch, t6 # 步骤2保存除t6外的所有寄存器到context reg_save t6 # 步骤3单独保存t6寄存器 mv t5, t6 # t5 context指针 csrr t6, mscratch # 读回原t6值 STORE t6, 30*SIZE_REG(t5) # 保存t6到context # 步骤4恢复context指针到mscratch csrw mscratch, t5 # 步骤5准备trap_handler入参并调用 csrr a0, mepc # a0 mepcTrap触发时的PC csrr a1, mcause # a1 mcauseTrap原因 call trap_handler # 调用C语言处理函数 # 步骤6trap_handler返回新的mepc写回硬件 csrw mepc, a0 # 步骤7恢复所有寄存器上下文 csrr t6, mscratch reg_restore t6 # 步骤8Trap返回跳回mepc指向的地址 mret # 扩展mretMachine Return硬件自动恢复 # 1. 权限级别恢复为mstatus.MPP # 2. 中断使能恢复为mstatus.MPIE # 3. PC mepc模块 3trap.c - trap_handlerC 语言 Trap 逻辑reg_t trap_handler(reg_t epc, reg_t cause) { reg_t return_pc epc; // 提取异常/中断编码掩码MCAUSE_MASK_ECODE0x7FFFFFFF reg_t cause_code cause MCAUSE_MASK_ECODE; if (cause MCAUSE_MASK_INTERRUPT) { /* 异步Trap - 中断mcause最高位1 */ switch (cause_code) { case 3: // 软件中断 uart_puts(software interruption!\n); break; case 7: // 定时器中断 uart_puts(timer interruption!\n); break; case 11: // 外部中断 uart_puts(external interruption!\n); break; default: printf(Unknown async exception! Code %ld\n, cause_code); break; } } else { /* 同步Trap - 异常mcause最高位0 */ printf(Sync exceptions! Code %ld\n, cause_code); panic(OOPS! What can I do!); // 异常panic // 扩展可修改return_pc 4跳过错误指令避免无限Trap } return return_pc; // 返回新的mepc地址 }底层扩展mepc 的意义异常触发时mepc 存错误指令地址如写 0x0 的 store 指令中断触发时mepc 存被中断指令的下一条地址mcause 掩码MCAUSE_MASK_INTERRUPT0x80000000最高位区分中断 / 异常MCAUSE_MASK_ECODE0x7FFFFFFF提取具体编码mret 与 ret 的区别ret普通函数返回跳转到 ramretTrap 返回由硬件处理权限 / 中断状态恢复跳转到 mepc。六、实战场景trap_test 触发异常完整流程1. 触发代码user.cvoid trap_test(void) { /* 写0x0地址无效内存触发存储访问错误异常编码7 */ *(int *)0x00000000 100; uart_puts(Yeah! Im return back from trap!\n); } void user_task0(void) { uart_puts(Task 0: Created!\n); while (1) { uart_puts(Task 0: Running...\n); trap_test(); // 触发异常 task_delay(DELAY); task_yield(); } }2. 完整执行流程user_task0执行 → trap_test写0x0 → 硬件触发存储访问异常 → PC跳转到mtvec指向的trap_vector → entry.S交换t6/mscratch → 保存寄存器 → 调用trap_handler → trap.c识别同步异常编码7→ panic系统卡死3. 扩展修改让异常不崩溃reg_t trap_handler(reg_t epc, reg_t cause) { reg_t return_pc epc; reg_t cause_code cause MCAUSE_MASK_ECODE; if (cause MCAUSE_MASK_INTERRUPT) { // 中断处理 } else { printf(Sync exceptions! Code %ld\n, cause_code); if (cause_code 7) { // 存储访问错误 return_pc 4; // 跳过错误的store指令4字节 uart_puts(Skip error instruction!\n); } else { panic(OOPS! What can I do!); } } return return_pc; }底层扩展跳过错误指令的原理RISC-V 指令长度为 4 字节32 位return_pc 4让 mepc 指向错误指令的下一条mret 后不会重复触发异常0x0 地址无效的原因os.ld 指定内核从 0x80000000 开始0x0 属于未映射的内存区域硬件会触发访问错误。七、辅助模块uart.c page.c printf.cTrap 调试支撑1. uart.c串口打印核心作用Trap 处理时打印日志底层通过操作 UART 寄存器实现字符输出#define UART0 0x10000000L // QEMU Virt的UART地址 #define LSR_TX_IDLE (1 5) // 发送寄存器空闲标志 int uart_putc(char ch) { // 等待发送寄存器空闲 while ((uart_read_reg(LSR) LSR_TX_IDLE) 0); return uart_write_reg(THR, ch); // 写字符到发送寄存器 }2. page.c内存管理核心作用为任务栈 / 堆分配内存初始化时划分堆区域并管理页分配#define PAGE_SIZE 4096 void page_init() { ptr_t _heap_start_aligned _align_page(HEAP_START); // 堆地址对齐 // 计算可分配页数量初始化页描述符 // 扩展页管理是操作系统内存管理的基础保证内存分配的连续性 }3. printf.c格式化打印核心作用封装 UART 实现格式化输出方便 Trap 调试时打印 cause_code、地址等信息int printf(const char* s, ...) { va_list vl; va_start(vl, s); int res _vprintf(s, vl); // 解析格式化字符串并调用uart_puts va_end(vl); return res; }核心代码逻辑总结上下文切换switch_to和trap_vector复用reg_save/reg_restore通过 mscratch 传递 context 指针Trap 处理流程硬件触发→汇编保存上下文→C 处理逻辑→mret 返回关键指令csrrw交换 CSR 和通用寄存器、mretTrap 返回、wfihart 休眠核心数据结构struct context是任务 / Trap 上下文的载体必须和寄存器保存顺序严格一致。
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

如何创建外卖网站证券投资网站做哪些内容

终极指南:AndroidGen-GLM-4-9B如何免费实现安卓自动化任务执行? 【免费下载链接】androidgen-glm-4-9b 项目地址: https://ai.gitcode.com/zai-org/androidgen-glm-4-9b 在移动AI领域面临数据稀缺挑战的背景下,智谱AI最新开源的Andro…

张小明 2025/12/24 4:14:39 网站建设

福州网站设计哪家好广东的一起(17)做网站

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容: 创建一个对比项目,展示IDEA Cursor插件与传统开发方式的效率差异。包括:1. 代码编写:统计完成相同功能所需时间;2. 错误率&#xff1…

张小明 2025/12/24 21:10:36 网站建设

域名没有网站可以备案门户网站系统有哪些平台

校园兼职招聘系统设计与实现 摘 要 在信息管理实践中,传统方法往往伴随着显著的时间消耗、较高的数据错误率、修改难度以及低效的数据检索过程。针对这些问题,本毕业设计提出并实现了校园兼职招聘系统,该系统通过在计算机上安装相应软件&am…

张小明 2025/12/24 19:44:04 网站建设

旅游做攻略的网站有哪些化妆品销售网站的源代码

在当今竞争激烈的互联网时代,用户体验已经成为决定产品成败的关键因素。想要掌握以用户为中心的设计精髓?《用户体验的要素》PDF下载为您提供了一条快速成长的捷径!🚀 【免费下载链接】用户体验的要素PDF下载介绍 《用户体验的要素…

张小明 2025/12/29 16:48:01 网站建设

网站怎么做外链接wordpress 去除顶部

目录简介:什么是DDOS攻击?一、使用阿里云的远程连接(VNC),而不是你自己的 SSH 工具1. **SSH 可能被“中间人劫持”(MITM)**2. **阿里云远程连接是“带外管理”(Out-of-Band&#xff…

张小明 2025/12/25 0:29:04 网站建设

已备案网站域名郑州网站建设怎么样

LobeChat 与 TensorRT-LLM 集成:释放 NVIDIA 显卡的极致推理性能 在如今大模型遍地开花的时代,用户早已不再满足于“能回答问题”的 AI 助手——他们想要的是秒回、流畅、像真人一样打字输出的交互体验。然而现实是,哪怕是在高端消费级显卡上…

张小明 2026/1/2 20:13:35 网站建设