从零写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(三十三):阻塞式 TTY —— read 不再忙等

上一章 busybox sh 成功显示了 / # 提示符,但 shell 拿不到任何输入——因为 tty_read 是忙轮询的,没有字符就直接返回 0,shell 以为收到 EOF,立刻退出。这一章实现真正的阻塞式 TTY,让 shell 能等待用户输入。 之前的问题 之前的 tty_read 大概是这样: int tty_read(char *buf, int len) { // 轮询串口状态寄存器 if (!(inb(0x3F8 + 5) & 1)) return 0; // 没数据就返回 0 *buf = inb(0x3F8); return 1; } 这有两个问题: 没有输入时返回 0,上层程序(shell)以为是 EOF 如果上层在循环里调这个,CPU 100% 占用 正确做法:没有输入时挂起进程,等串口中断来了再唤醒。 串口中断(IRQ4) COM1 对应 IRQ4,接在 PIC 主片的 IR4 引脚。要用串口中断,需要两步: 1. 在 PIC 上 unmask IRQ4: // PIC 主片 IMR 寄存器:0 表示开放,1 表示屏蔽 uint8_t mask = inb(0x21); mask &= ~(1 << 4); // 清除 bit4,开放 IRQ4 outb(0x21, mask); 2. 开启串口的接收中断: ...

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