外观
运行边界:程序、内核与机器分别负责什么
从一条写入请求出发
用户程序调用 write(1, "hi", 2),表达的是“向编号为 1 的已打开对象写两个字节”。它没有说明终端寄存器地址,也不能决定另一个进程何时运行。操作系统的价值正是把物理资源包装成稳定接口,同时检查调用者能否执行操作。
在 xv6 中,用户程序运行于 RISC-V U 模式,内核运行于 S 模式。权限模式决定哪些指令、控制寄存器、页面允许访问;它不是普通 C 变量。试图在用户态直接修改 satp,不能把自己变成内核。硬件拒绝后会陷入预先设置的异常入口。启动早期会涉及 M 模式,但常规进程与内核交互主要看 U/S 两种模式。
内核选择机制,硬件执行检查。例如内核写出 PTE 的 U/R/W/X 位,MMU 在访问时检查它们。一个漏洞可能来自设置错权限,也可能来自内核自己通过可写的直接映射修改了本不该修改的数据。因此“已经进入内核”不意味着参数自动可信。
flowchart LR A[用户 C 程序] --> B[用户系统调用桩] B -->|ecall| C[硬件切换特权并进入 trap 入口] C --> D[xv6 保存状态并检查请求] D --> E[内存或文件或设备操作] E --> F[恢复用户状态] F -->|sret| A
查看流程图文本
flowchart LR A[用户 C 程序] --> B[用户系统调用桩] B -->|ecall| C[硬件切换特权并进入 trap 入口] C --> D[xv6 保存状态并检查请求] D --> E[内存或文件或设备操作] E --> F[恢复用户状态] F -->|sret| A
三种地址、两个栈
读代码时先标注一个数是什么地址:用户虚拟地址、内核虚拟地址,还是物理地址。即使 xv6 的内核对部分 RAM 采用直接映射,数值常常相同,它们的语义也不相同。把用户传来的 0x4000 强转为内核指针,CPU 会按照当前内核页表翻译它,绝不会自动去查调用进程的用户页表。
每个进程既有用户栈,也有内核栈。用户栈保存应用函数调用;系统调用进入内核后需要可信栈,不能继续把用户可修改的栈当内核工作区。trapframe 保存跨特权边界的用户寄存器;context 保存内核调度切换需要的寄存器。它们服务于不同切换,不能互相替代。
把内核分成六组阅读,比按文件字母顺序有效:启动 entry.S/start.c/main.c;地址空间 vm.c/kalloc.c;进程 proc.c/swtch.S;陷入 trampoline.S/trap.c/syscall.c;文件 file.c/fs.c/log.c/bio.c;设备 uart.c/virtio_disk.c。实验分支还会加入 e1000.c/net.c 等文件。
获取与锁定官方实验代码
在自己的实验目录执行:
sh
git clone git://g.csail.mit.edu/xv6-labs-2025
cd xv6-labs-2025
git switch util
git rev-parse HEAD
make qemu这里的 git:// 是官方 handout 给出的仓库地址;网络若不允许 9418 端口,先确认网络访问条件,再选择能核验同一提交的镜像。不要把任意搜索到的学生答案仓库当作官方骨架。
本资料已核验以下分支。短 SHA 仅用于阅读识别,完整值保存在 labs/os/upstream-pins.json。
| 分支 | 已核验提交前缀 |
|---|---|
| util | db9a9d8b7d82 |
| syscall | 0e53502c9edd |
| pgtbl | cff452f8f70f |
| traps | 4270ccc3d1a6 |
| cow | cf0eb5bdaaa2 |
| net | 982b43b57332 |
| lock | af12b48372eb |
| fs | a95dec46d444 |
| mmap | 5648f647958d |
例如独立开展 COW,可从已核验提交建立自己的分支:
sh
git switch -c study-cow cf0eb5bdaaa24f3a46a80d2702af3c9e260c2692
make clean
make qemu切换前先保存自己的改动。实验测试期待相应骨架的接口,不能把 superpage 分支的所有改动直接带进 COW 后再把失败归因于 COW 思路。
环境与第一轮观察
需要能生成 RISC-V 目标的编译器、binutils、调试器和 qemu-system-riscv64。具体安装命令应跟随官方 tools 页和本机平台。宿主机是 ARM Mac,并不意味着 xv6 也改为 ARM:QEMU 仍模拟 RISC-V,交叉编译器仍生成 RISC-V 指令。
第一次启动只做三件事:运行 ls 看文件镜像里装了什么;按 Ctrl-p 看进程状态;用 Ctrl-a 后按 x 退出 QEMU。2025 的 make qemu 会重新构建文件镜像,需要保留上次镜像时使用 make qemu-fs。在宿主 shell 与 xv6 shell 之间保持清晰边界:make grade 在宿主执行,usertests -q 在 xv6 中执行。
自测:用户指针和内核指针数值相同,就能直接解引用吗?
不能。必须先确定当前页表、地址范围和访问目的。系统调用参数代表用户地址时,应使用相应的 copyin/copyout/copyinstr 路径。数值相同只是映射布局上的巧合,不是权限与生命周期的保证。