从零写OS(三十四):阻塞式管道与调度器的两个 Bug

上一章实现了阻塞式 TTY,这一章在同样的思路上让 pipe 的 read 也变成阻塞式,同时修复了调度器里隐藏的两个 Bug,让 fork + exec + wait4 的完整流程跑通。 问题:非阻塞 pipe read 的后果 ch33 之前,vfs_read 对 pipe 的实现是: if (pipe_buf_empty(p)) return 0; // 管道空 → 直接返回 0 对于 shell 管道命令,busybox sh 会: fork 出子进程执行左侧命令,写 pipe 父进程(或另一子进程)读 pipe,处理右侧命令 如果读端在写端还没写入时就读到 0,shell 认为 EOF,管道提前关闭,命令输出丢失。 解决方案:sti/hlt 等待 与 ch33 阻塞式 TTY read 相同的思路:在内核态用 sti; hlt 轮询等待。 // pipe read:等待数据或写端关闭 while (pipe_buf_empty(p) && pipe_has_writer(p)) { __asm__ volatile("sti" ::: "memory"); __asm__ volatile("hlt" ::: "memory"); __asm__ volatile("cli" ::: "memory"); } if (pipe_buf_empty(p)) return 0; // 写端已关闭且无数据 → EOF 关键点: ...

May 22, 2026 · 2 min · 大飞

从零写OS(二十六):poll —— 同时等待多个 fd

上一章实现了 TTY 输入,但用户程序只能这样等输入: .read_loop: syscall SYS_READ(fd=0, buf, 64) cmp rax, 0 jle .read_loop 问题很明显: CPU 全速空转:没有输入时 SYS_READ 每次返回 0,进程白白占用 CPU 无法同时等多个 fd:如果既要等 stdin,又要等 pipe,必须串行轮询,任一 fd 都可能饿死 poll 解决这两个问题。 poll 是什么 poll(fds[], nfds, timeout) 是一个系统调用: 传入一组 pollfd_t 结构,每个描述"我关心 fd X 的什么事件" 内核检查每个 fd 是否满足条件 返回就绪的 fd 数量,并在 revents 里标记哪些事件发生了 struct pollfd { int fd; // 监听哪个 fd short events; // 关心:POLLIN(可读)/ POLLOUT(可写) short revents; // 内核填写:实际发生了什么 }; int poll(struct pollfd *fds, int nfds, int timeout_ms); 有了 poll,程序可以: poll([{fd=0, POLLIN}, {fd=pipe_r, POLLIN}], 2, -1) → 等到任意一个有数据,才返回 实现架构 整体分四层: ...

May 14, 2026 · 3 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