从零写OS(五十一):三个崩溃,三个修复

性能优化之后,跑 ls | grep foo 时发现三类崩溃,分别来自三个不同的地方。 崩溃一:内核栈溢出 ls | grep foo 在 busybox sh 里需要同时运行三个进程:sh、ls、grep,加上 fork 的中间状态,进程数量一下上来了。 运行时随机崩溃,串口输出乱码。通过 QEMU 调试发现是内核栈溢出——某个进程的内核栈被覆盖了。 原来内核栈只有 4KB: #define STACK_SIZE 4096 p->stack = (uint8_t *)pmm_alloc(); // 只有 1 个物理页 busybox 的调用链比较深(sh → fork → exec → elf_load → ext2_read → bcache_get → …),4KB 撑不住。 改成 8KB,用 kmalloc 分配(pmm_alloc 只能分配 4KB): #define STACK_SIZE 8192 p->stack = (uint8_t *)kmalloc(STACK_SIZE); 崩溃二:cd .. 跳到根目录 / # mkdir -p /usr/bin / # cd /usr/bin /usr/bin # cd .. / # ← 应该到 /usr,结果跳到 / 路径解析遇到 .. 时,原来直接跳回根目录: ...

June 4, 2026 · 1 min · 大飞

从零写OS(二十二):管道 pipe 与 dup2

上一章实现了文件描述符,进程可以用 open/read/close 访问 ext2 文件。但两个进程之间还没有办法通信。shell pipeline cmd1 | cmd2 的本质是:cmd1 的输出直接流进 cmd2 的输入,中间不落磁盘。 这一章实现 pipe 和 dup2,打通进程间通信的最小路径。 pipe 是什么 pipe 是内核里的一块环形字节缓冲区。系统调用 pipe(fds) 返回两个文件描述符: fds[0] = 读端 fds[1] = 写端 写进程 → fds[1] → [内核 ring buffer 256字节] → fds[0] → 读进程 和普通文件一样,管道也走 fd → VFS → 底层数据 这三层,只不过底层数据不是磁盘,而是内核里的 pipe_t。 dup2 是什么 dup2(oldfd, newfd) 让 newfd 指向和 oldfd 同一个内核文件描述符,如果 newfd 已经打开则先关掉它。 shell pipeline 的核心操作就是 dup2 + exec: // 子进程(cmd1),stdout → pipe 写端 dup2(fds[1], 1); close(fds[0]); close(fds[1]); exec("cmd1"); // cmd1 以为自己在写 stdout,实际写进了管道 // 父进程(cmd2),stdin → pipe 读端 dup2(fds[0], 0); close(fds[0]); close(fds[1]); // 关掉写端,否则 cmd2 永远等不到 EOF exec("cmd2"); pipe 的 EOF 语义 read 管道什么时候返回 0(EOF)? ...

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