资讯详情

RISC-V操作系统架构图细化:从特权模式到系统调用

📅 2026/10/18 2:20:30 | 华诺云谱 👁 阅读
RISC-V操作系统架构图细化:从特权模式到系统调用
在接触了不少做底层软硬件的开发者之后我越来越觉得能亲手画出一张让自己信服的操作系统架构图比看懂十张现成的架构图更有价值。尤其是当架构图的对象换成了 RISC-V 这种还在快速演进、生态尚未完全固化的指令集架构时细化工作更像是一次对操作系统本质的重新梳理。这篇文章不打算泛泛而谈“什么是操作系统架构”而是把 RISC-V 作为具体实例完整走一遍架构图细化的思考流程、分层逻辑、关键模块拆解以及那些容易被忽略却又十分要命的细节。1. 内容整体设计与思路拆解1.1 为什么选 RISC-V 作为架构图细化对象很多人问我画操作系统架构图用 x86 或者 ARM 不是更成熟、参考资料更多吗这话没错但恰恰因为资料多、旧体系包袱重画出来的架构图往往继承了太多历史遗留的“约定俗成”很难让人真正理解每一层为什么存在。RISC-V 给了我们一个更干净的起点。它没有历史包袱指令集文档开放特权架构Privileged Architecture定义得相对简洁清晰。从架构图细化的角度来说这意味着几乎所有层次都能找到“最初的设计动机”而不是“为了兼容某某旧标准才留着的”。细化一张 RISC-V 架构下的操作系统图基本上等于把操作系统从硬件往上重新推导了一遍这种“推导感”是其他指令集很难给的。另外RISC-V 的生态虽然年轻但已经覆盖了从微控制器到服务器处理器的全谱系这意味着操作系统架构图不能只画某一个场景而是要能兼容 MCU 上的轻量 RTOS 和应用处理器上的 Linux 级系统。这本身就提高了架构图细化的难度也提升了它的参考价值。1.2 架构图细化要回答的四个核心问题在绘制之前我习惯先厘清一个问题这张架构图到底是给谁看的、用来回答什么问题的。同一个操作系统从不同视角看画出来的图完全不一样。我这次选择的是“软硬件协同视角”即站在系统软件开发者内核、驱动、BSP的角度回答四个层面的问题硬件层提供了什么RISC-V 的 ISA 分层、特权模式、中断异常机制、定时器、调试接口等。这是架构图的底座也是很多内核开发者最容易忽视的一层。内核如何抽象和管理硬件上下文切换、虚拟内存、页表、系统调用、中断分发、设备驱动框架。这是架构图的主体是内核的核心领地。内核如何承接操作系统能力进程调度、内存管理、IPC、文件系统、网络协议栈。这些能力通过虚拟化、抽象、策略分离来沉淀。应用如何触达资源系统调用接口、C 库、运行时、应用框架。这部分决定了架构图“向上”的边界画到哪里。顺着这个思路架构图细化就不再是把网上搜来的图“画得更精细一点”而是真正从硬件寄存器、特权指令出发一层一层把抽象过程画出来。整个细化过程本质上是一次自底向上的“重建”而不是自顶向下的“美化”。1.3 细化后架构图应具备的特征基于上面的思考我对细化后的架构图提出了一些具体要求。这些要求也适合你拿来检验自己画的架构图是否合格层次边界清晰特权模式切换、地址空间切换、数据通路与控制通路要分开表达不能把 CPU 内部模块、内核模块、用户态库画成一锅粥。模式与状态可见RISC-V 的 U/S/M 模式以及可选的 H 扩展是架构图中必须显式画出的元素因为它们决定了哪些指令和 CSR 可访问。关键接口标注明确如ecall指令、mret/sret指令、CSR 读写、页表基址寄存器satp等要在图中标注出它们连接的两个模块。数据流与事件流分离中断、异常、系统调用是事件流DMA、MMIO、内存拷贝是数据流。混在一起画是架构图最常见的败笔。有了这些原则接下来就可以进入核心模块的细化环节。2. 核心细节解析与实操要点2.1 从特权模式说起架构图的“楼层”划分细化架构图最重要的第一步是把 RISC-V 的特权模式画对。很多网上的图把“内核态 / 用户态”画成上下两块但在 RISC-V 里这不仅仅是“两层楼”的关系而是一组可以嵌套、可以组合的权限模型。RISC-V 特权规范定义了三种基础模式U-ModeUser Mode用户态运行应用程序。无法访问关键 CSR无法执行特权指令。S-ModeSupervisor Mode内核态运行操作系统内核。可以访问satp、sscratch、stvec、sepc等监管者级别 CSR可以执行sret等指令。M-ModeMachine Mode机器态运行固件或 Hypervisor。拥有最高权限可以访问所有 CSR可以执行mret、wfi等指令。这三层楼的关系不是简单的“上层调用下层”而是通过**异常委托Interrupt/Exception Delegation**实现灵活的控制权交接。比如操作系统可以让用户态的程序直接访问某类中断通过mideleg/medeleg寄存器而不必每次都陷入 M-Mode。细化架构图时我会把特权模式画成一个竖向的“权限轴”而不是两个扁平的大区块。U 模式在底部M 模式在顶部或者在侧边S 模式居中。权限轴的旁边标注每一模式能访问的典型 CSR 和特权指令。这张图能明确回答一个问题系统调用为什么会从 U 模式陷入 S 模式答案是因为 U 模式根本无法访问 S 模式的资源只有通过ecall触发异常处理权移交给 S 模式才能完成权限的“跨越”。实操中容易犯的错把 M-Mode 和 S-Mode 混为一谈。在 RISC-V 里M-Mode 通常是 OpenSBI固件或安全监控器的领地而操作系统内核运行在 S-Mode。如果你的架构图里只有“内核”和“用户”两层却忽略 M-Mode 固件层那么 Trap 处理路径、中断委托关系、启动流程都会说不通。2.2 异常与中断模型架构图里的事件通路异常和中断是操作系统的“事件源”也是架构图中必须单独画出来的“通路”。我见过太多架构图把中断控制器画成一个孤零零的方块既不说明它连向哪个特权层也不解释中断在模式间怎么跳转等于白画。RISC-V 的异常与中断模型有以下几个关键要素细化时必须体现mtvec/stvecM 模式和 S 模式的中断入口地址。这张图要画出“中断发生 - 硬件跳转到stvec指向的入口 - 保存上下文 - 分发处理”的链路。mcause/scause异常原因寄存器用于区分是系统调用、缺页、非法指令还是外部中断。mepc/sepc异常返回地址指向被中断打断的那条指令。mstatus/sstatus状态寄存器其中 MIE/SIE 位控制全局中断使能。mip/sip、mie/sie中断挂起与使能寄存器结合 PLICPlatform-Level Interrupt Controller实现外部中断的仲裁。细化这张图的时候我会把“硬件自动完成的动作”和“软件完成的动作”用不同的颜色或线型区分开。硬件部分包括检测到异常、切换模式、跳转入口、保存部分状态软件部分包括保存完整上下文、识别异常源、调用处理函数、恢复上下文、执行mret/sret返回。这里有一个很容易被忽视的细节RISC-V 不会像 ARM 那样自动帮你保存/恢复大量寄存器上下文保存是软件职责。这意味着架构图里必须画出“通用寄存器保存区”和“CSR 保存区”这两个数据结构通常位于内核栈或sscratch指向的内存区域。如果这两块缺失进入中断处理函数时寄存器的值都会被破坏系统根本无法工作。2.3 虚拟内存与分页架构图里的地址翻译路径虚拟内存是操作系统架构图里最核心、也最容易画错的部分。RISC-V 使用 Sv39 / Sv48 等分页方案Sv39 表示 39 位虚拟地址三级页表Sv48 是四级页表页表基址存放在satpCSR 中。细化这层架构图时有两个问题必须回答清楚页表遍历是谁做的TLB 未命中之后发生了什么RISC-V 的页表遍历由硬件完成通过 MMU但页表的建立、修改、失效刷洗如sfence.vma指令是软件职责。架构图上要画出 CPU - MMU - 内存这条“地址翻译通路”同时标注satp寄存器作为页表切换的“开关”。我细化时习惯引入一个时间序列视角用户程序访问虚拟地址。MMU 检查 TLB命中则直接访问物理内存。TLB 未命中硬件根据satp指向的根页表逐级查找遇到页表项不存在或权限不匹配触发缺页异常。缺页异常陷入 S-Mode操作系统缺页处理程序运行。处理完成后返回 U-Mode重新执行触发缺页的指令。这张“异常路径”和“正常路径”并存的分支图能很好地解释为什么虚拟内存在 RISC-V 上不是可有可无的抽象而是一套贯穿特权模式、异常处理、页表管理、物理内存分配的完整机制。一个容易踩的坑很多内核教程用mmap或kmalloc直接讲内存分配但没讲清物理内存和虚拟内存的映射关系。架构图上如果只画一个“内存管理模块”读者永远搞不清页表和物理页帧怎么关联。细化时一定要把物理内存RAM和页表结构Page Table单独立出来中间用“映射关系”连线表达。3. 实操过程与核心环节实现3.1 自底向上的架构图分层框架接下来我会完整走一遍架构图细化的实操流程。首先要确定的是整体框架——我把它分成五层半注意这里不是随便分层而是每一层都对应 RISC-V 特权模型和硬件接口上的“自然边界”。硬件层底层RISC-V 核心、内存控制器、UART/SPI/I2C 外设、定时器、调试模块、PLIC 中断控制器。固件层M 模式OpenSBI / RustSBI 等负责平台初始化、提供 SBI 调用接口给 S 模式内核、管理 M 模式中断。内核层S 模式进程管理、内存管理、设备驱动、系统调用、中断异常分发、文件系统、网络协议栈。系统库层U 模式libcnewlib / musl / glibc、运行时环境、动态链接器。应用层U 模式应用程序、服务进程、用户态驱动如 FUSE、DPDK 风格的用户态网络等。加的那“半”层是SBI 抽象层它不是完整的独立层级而是 M 模式和 S 模式之间的一个“服务接口层”。在架构图上我建议把它画成一条横向贯通的总线条下方是 M 模式固件上方是 S 模式内核箭头指向代表“S 模式请求服务、M 模式提供服务”。这样画有三个好处明确了内核不是直接操纵硬件寄存器而是通过 SBI 调用访问部分平台资源解释了为什么同一份内核代码跑在 QEMU 和真实开发板上硬件初始化逻辑差异可以收敛到固件层为后续支持虚拟化H 扩展留下了扩展空间。3.2 中断路径的细化与标注方法现在我们挑一个具体模块来做“放大式”细化中断路径。我建议把中断路径分成三条子路径分别画清外部中断路径外设如 UART 收到数据- 中断信号送达 PLIC - PLIC 仲裁后送达 CPU 的 IRQ 引脚 - CPU 根据mstatus.MIE/sstatus.SIE决定是否响应 - 跳转mtvec/stvec- 软件读取mcause/scause确认中断源 - 调用对应 driver 的 ISR - 恢复现场。定时器中断路径RISC-V 的定时器通常是平台级如 SiFive CLINT在 M 模式下处理mtime比较触发。但操作系统内核需要定时器 tick所以固件层将定时器中断转发为 S-Mode 的stip中断。架构图上要把这个“转发”关系画出来否则你会疑惑内核的时钟中断到底是从哪来的。软件中断路径通过send IPI触发其他 hart 的软件中断ssip。这是多核架构图上必画的一部分。RISC-V 的核间中断不像 ARM 的 SGI 那么直接很多实现里是通过 CLINT/ACLINT 的 MSIP 寄存器完成。每条路径细化时我习惯在旁边加一个“时序小注”比如用 1 - 2 - 3 - 4 标记事件经过的顺序。这样做的好处是不仅能看到“谁和谁相连”还能知道“事件先到谁、后到谁”。一张静态结构图就具备了动态时序的说明力。这里有一个很有用的细化技巧不要用“数据流”画中断路径而要用“事件流”画。数据流是连续的事件流是离散的、有因果顺序的。把ecall之后的返回地址保存、寄存器保存、处理、恢复、sret返回拆成一个环远比一根弯弯绕绕的箭头更清晰。3.3 系统调用流程的边界确定系统调用是架构图里少数几个能明确画出“用户态到内核态再返回”闭环的地方。细化时我建议把ecall指令作为一个显式的图元放在用户态和内核态交界处。RISC-V 系统调用的标准路径如下用户程序将系统调用号放入a7寄存器参数放入a0~a5。执行ecall触发异常。CPU 切换到 S 模式跳到stvec指向的入口。内核保存用户上下文根据scause识别为系统调用异常根据a7分发到具体处理函数。处理完成后返回值写入a0。执行sret返回 U 模式恢复用户态继续执行。架构图上画这个流程时我还会加一个“边界框”左边是用户态地址空间右边是内核态地址空间中间用ecall指令作为门。一条箭头从用户态指向内核态标注“trap”另一条箭头从内核态指回用户态标注“sret”。这个边界框能直观地表达出系统调用不是“函数调用”而是一种受控的、需要模式切换的服务请求。另外要补充的是Linux 在 RISC-V 上对这层做了性能优化使用riscv_vsyscall机制加速部分系统调用如时间相关函数。细化架构图时如果你想体现这种优化可以在用户态加一个“VDSO 映射页”并标注“无需陷入内核即可读取的时间数据”。这种细节让架构图的“现代感”一下子拉满。3.4 多核多 Hart扩展的呈现方式RISC-V 的一个核心特性是“多 Hart”硬件线程每个 Hart 有独立的通用寄存器、CSR 和 PC。多核操作系统的架构图必须要体现出“每个 Hart 一套特权上下文共享一套内存和外设”的特征。我细化时会用一个分栏结构表达多核左侧栏标注 Hart 0、Hart 1、Hart 2……每一栏内部画出各自的 U/S/M 模式栈、sscratch指向的内核栈、页表基址satp。中间栏标注共享的物理内存、PLIC、CLINT、外设寄存器映射区。右侧栏画出核间同步与通信机制自旋锁的原子操作lr/sc、IPI 中断、共享内存队列。这样一张“三栏式”架构图比传统的一张图画全部 CPU然后箭头乱飞要清晰得多。它能直接解释几个多核场景不同 Hart 运行在不同进程上下文但它们共享内核代码段和页表同一内核镜像。一个 Hart 发起sfence.vma后需要通过 IPI 通知其他 Hart 刷新各自的 TLB否则会出现一致性问题。自旋锁基于lr.w/sc.w这类原子指令实现而不是靠关中断因为关中断只能影响当前 Hart 上的调度。这部分细化对做过 SMP 内核开发的人来说是“刚需”对初学者来说也是最容易打开认知边界的环节。4. 常见问题与排查技巧实录4.1 架构图细化时容易犯的错误盘点画 RISC-V 操作系统架构图下面的错误我在各类文档、博客甚至一些开源项目的 README 里都见过。整理出来当避坑清单用把 M 模式和 S 模式混为一谈这是最高频的错误。很多图只画“内核态 / 用户态”完全忽略 M 模式固件导致中断入口、委托关系全部失真。不标特权指令和 CSR架构图里只有模块方块没有ecall、mret、sret、satp这些“粘合剂”图就变成了一张静态的堆叠图回答不了任何动态问题。把中断和异常画成一条线RISC-V 把中断和异常统一放进 Trap 模型处理但来源不同异步 vs 同步、处理方式略有差异。画成一条线会让人误解同步异常也被当作中断屏蔽。没有画出 PLIC 的仲裁作用外部中断不是直接连到 CPU 的而是经过 PLIC 进行优先级仲裁并将最高优先级中断号送给 CPU。不画 PLIC相当于画网络架构却不画交换机。忽略 SBI 接口层OpenSBI 提供的ecall服务如定时器、IPI、控制台输出是内核获取平台服务的关键。不画 SBI内核访问 RTC、重启、关机等功能的路径就断了。多核图只画一个 CPU如果目标系统是 SMP架构图必须体现多 Hart 的存在否则调度、锁、TLB 一致性问题都没有附着点。4.2 排查架构图逻辑缺陷的三种验证手段细化完成之后怎么验证你的架构图不是“好看但错误”我会用以下三种方法做逻辑推演启动路径验证法沿着架构图从上电开始走一遍。上电后 CPU 处于 M 模式 - 执行 ROM/固件 - 初始化 DDR、时钟 - 跳转到 OpenSBI - OpenSBI 初始化、准备 S 模式环境 -mret进入 S 模式 - 内核入口_start- 设置页表写satp- 开启 MMU - 跳转到start_kernel。如果这条路径中任何一环在图上找不到对应的模块或连线图就是有缺陷的。上下文切换推演法选择一个进程线程切换场景沿着架构图推演定时器中断触发 - 进入内核 - 保存当前上下文到当前进程的内核栈 - 调度器选择下一个进程 - 恢复下一个进程的上下文 -sret返回用户态。每一步涉及的模块定时器、中断控制、进程管理、上下文数据结构都应该在图中“实体化”。特权升级推演法走一遍“用户程序写特权 CSR”的路径例如在 U 模式写satp。RISC-V 会触发非法指令异常illegal instruction陷入 S 模式由内核判断是修 bug 还是杀掉进程。架构图上如果 U 模式和 S 模式之间没有一个“权限守门”的机制表达说明这张图没有把特权隔离画透。这三种验证法本质上是把结构图还原成“状态机时间线”来看能让潜藏的逻辑矛盾快速暴露。4.3 工具与呈现技巧架构图的细化不用一开始就追求用专业绘图工具。我自己的经验是先用笔在白板上画草稿把模块、连线、箭头、标注都理顺再用工具产出最终版本。常用工具方面喜欢轻量化的可以选 draw.io / diagrams.net它内置了不少 CPU、内存、接口形状并且支持分层图层。喜欢代码化、可版本管理的可以用 PlantUML 或 Mermaid绘图功能有限复杂架构建议谨慎不过如果你主要在代码仓库里维护架构文档Text-based 的方案确实有优势改动 diff 很方便。更精细的推荐用专业建模工具如 Enterprise Architect 或 StarUML但应付操作系统架构图有点杀鸡用牛刀。画图时几个提升可读性的小技巧颜色语义统一比如 M 模式用米色、S 模式用浅蓝、U 模式用淡绿中断事件流用红色数据流用灰色控制流用深蓝。一旦定下来全图保持一致。模块命名要规范不要用“内存管理”这种宽泛词建议用“页表管理PTM”“物理页帧分配器Page Allocator”“VFS”“Syscall Dispatcher”这类能准确对应代码结构的命名。连线正交化尽量让连线走向横平竖直不要画成弯曲的“意大利面”否则视觉负担太重。加图例在角落放一个图例框说明颜色、箭头、边框类型各自的含义。这套图例本身就是文档的一部分能大幅降低读者理解成本。5. 设计背后的取舍与未来演进5.1 为什么选择“自底向上”而不是“自顶向下”把架构图细化到一半的时候你可能会发现针对不同目标读者图的侧重点完全不一样给应用开发者看应该从系统调用接口往下画给驱动开发者看应该从中断、DMA、MMIO 往上画给内核核心开发者看必须把进程调度、内存管理、VFS 画得足够细。我这次选择“自底向上”是因为 RISC-V 本身作为年轻指令集其设计哲学就是“越简单的硬件抽象越能让上层软件做更多事”。从特权指令、CSR、异常模型出发推到调度器、文件系统读者能够沿一条清晰的“能力上升”路径理解操作系统为何是这副模样。反过来如果你从应用层往下画很容易陷入“每个模块都见过但不知道硬件为什么这样约束软件”的困境。当然自底向上的方式对读者要求更高它默认读者具备一定的计算机组成原理基础。对于纯应用层开发者可能先看系统调用层再决定是否向下深入更友好。所以架构图本身也可以做“分卷”设计一张全景图 若干局部放大图。全景图保持精简局部图随意细化互不耽误。5.2 向量扩展与虚拟化扩展的位置RISC-V 的架构图在细化时还必然会遇到两个扩展方向向量扩展Vector Extension和虚拟化扩展Hypervisor Extension。这决定了你的架构图是不是“面向未来”的。向量扩展方面它引入了新的向量寄存器v0~v31。上下文切换时这些寄存器也要保存/恢复。如果架构图里出现“CPU 核心”模块建议在旁边标注“向量寄存器文件”并明确连接“上下文管理模块”。否则后续支持向量指令时图又要推翻重画。虚拟化扩展H 扩展方面它新增了一组基于 S 模式的“客机”和“宿主机”状态切换。如果你的操作系统将来要支持跑多个 VM架构图里就要预留出 “VS-Mode” 或“虚拟化扩展层”的位置。细化时不需要真画出完整的虚拟化栈但至少要在 M 模式和 S 模式之间留出一条“H 扩展的潜在扩展带”这对架构图的长期演进很关键。5.3 与现有开源项目的对照为了确保你的架构图不是凭空想象强烈建议实际工程项目做对照验证。对照清单如下OpenSBI / RustSBI看 M 模式固件如何实现 SBI 调用确认架构图里 SBI 层的函数边界是否准确。Linux Kernelarch/riscv重点看arch/riscv/kernel/entry.STrap 入口、arch/riscv/mm/fault.c缺页异常路径、drivers/irqchipPLIC 驱动以及kernel/smp.c多核启动与 IPI。这些代码直接对应架构图的核心连线。RT-Thread / ZephyrRISC-V 版本轻量内核在 RISC-V 上的实现通常更精简适合作为入门对照特别是context switch和interrupt部分。QEMU 的 RISC-V virt 平台如果你想把架构图里画的硬件摆到实际环境里跑一跑QEMU virt 平台算是最方便的“仿真墙”。做这个对照的意义在于架构图不是纸面上的美感工程它的每一个箭头都必须能找到对应代码或寄存器行为的支持。要么是硬件行为CSR、MMU、PLIC要么是软件行为Trap Handler、调度器、SBI 服务。只有这样画出来的图才能拿来做系统设计评审、课堂讲解、或者新员工入门的导览图。5.4 架构图的更新维护习惯操作系统架构图最大的敌人不是“画不对”而是“画完就再也不改”。内核代码持续演进RISC-V 特权规范也会迭代目前已到 1.12后续还在继续完善架构图必须跟上。我的建议是把架构图和代码目录做映射每画一个模块标注对应的源码路径。这样代码重构时模块变更能自动“揪出”架构图需要更新的位置。建立“架构图评审”机制每两周花半小时对照最近的 git log检查是否有改动影响架构图的连线或边界。使用版本管理架构图源文件入库管理每次修订留下 commit message说明为什么改、改了什么。日后有人问“这块为什么这么画”翻开历史一目了然。我自己吃过亏有一版架构图没有跟代码同步维护半年后拿来讲解里面的中断委托模型已经跟代码行为对不上了。现场讲得信誓旦旦台下一人翻代码立刻指出矛盾相当尴尬。所以架构图不是一次性交付物而是一份需要持续维护的“活文档”。6. 写在最后的一些经验画了这么多版 RISC-V 操作系统架构图如果说有什么最关键的体会我想是这句话别急着画连线先把你对操作系统的理解写成一串问题清单。比如“中断从哪来、到哪去、谁处理、怎么恢复”“虚拟地址怎么变成物理地址、缺页了谁管”“线程切换时寄存器谁保存、存哪里”。这些问题能在纸上回答清楚了架构图只是把回答可视化而已。如果你刚开始接触 RISC-V 和操作系统可以直接用 QEMU 的 virt 平台做实验。先跑一个最小的内核打印字符串、开中断、配页表、跑通第一个系统调用每做一步就回到架构图上标注一步。这个过程下来你对操作系统的理解深度、对架构图的把握程度都会远超那些只背了概念、没动手画过图的人。后续如果你有兴趣可以把这张架构图继续朝两个方向深化一个是安全方向加入 PMPPhysical Memory Protection和比例安全隔离的呈现另一个是实时方向把中断优先级、调度延迟、嵌套抢占的约束画进去。每一条延伸线都会带你走到更细、更有意思的系统软件腹地。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑