从零写OS(四十二):让 busybox 跑起来 —— 符号链接、fork/exec、调度器 cpu_pin bug

上一章实现了 TCP/IP 栈,用户程序能发 HTTP 请求了。但验证时发现 /bin/ls 完全没反应,wget_test 也跑不起来。这一章记录排查过程——一共修了 6 个 bug,涉及 ext2 fast symlink、VFS 路径解析、内核栈溢出、缺失 syscall,以及两个调度器 cpu_pin 问题。 最终效果: / # ls bin etc lost+found / # cd bin /bin # ls busybox cp kill mv pwd sh wget_test cat echo ls ps rm umount /bin # wget_test wget_test start connecting... connected! request sent HTTP/1.0 200 OK ... DONE /bin # 背景:busybox 的目录结构 Makefile 用这种方式制作 ext2 镜像: sudo cp busybox-x86_64 /tmp/ext2mnt/bin/busybox sudo cp busybox-x86_64 /tmp/ext2mnt/bin/sh # sh 是真实复制 for cmd in ls cat echo pwd ...; do sudo ln -sf /bin/busybox /tmp/ext2mnt/bin/$cmd # 其他命令是符号链接 done /bin/sh 是真实文件,/bin/ls 等是指向 /bin/busybox 的符号链接。 ...

June 1, 2026 · 4 min · 大飞

从零写OS(十九):exec 与用户程序

之前所有代码都跑在内核态(ring0)。这一章第一次让用户程序在 ring3 里运行——加载 ELF 文件,跳到用户态执行,用户程序通过 syscall 和内核通信。 这是操作系统最核心的一个边界:内核和用户程序之间的隔离。 exec 做什么 exec 的语义是"用一个新程序替换当前进程": 从文件系统读 ELF 文件 解析 ELF header,找到各个段的加载地址 创建新的用户页表,把各段加载进去 分配用户栈 设置进程的 rip = 入口地址,rsp = 栈顶,cs/ss = 用户段,放入就绪队列 调度器下次选中这个进程,就会跳到用户态开始执行。 进入用户态:iretq 第一次进入用户态不能用 sysret——没有对应的 syscall。用 iretq: static void enter_usermode(uint64_t rip, uint64_t cs, uint64_t rflags, uint64_t rsp, uint64_t ss) { __asm__ volatile( "push %4\n" // ss "push %3\n" // rsp "push %2\n" // rflags(IF=1,开中断) "push %1\n" // cs(0x23,ring3 代码段) "push %0\n" // rip(entry point) "iretq" :: "r"(rip), "r"(cs), "r"(rflags), "r"(rsp), "r"(ss) ); } iretq 弹出这 5 个字段,CPU 检查 cs 的 RPL 发现是 ring3,自动完成特权级切换。 ...

May 9, 2026 · 2 min · 大飞
京ICP备14031575号-3