从零写OS(二十五):TTY 终端输入

到目前为止,进程只能输出,没有办法接收用户输入。SYS_READ 形同虚设,键盘按了也没反应。 这一章实现 TTY——Unix 对终端设备的最初抽象,让进程能以行为单位从串口读取输入。 TTY 是什么 TTY 原本是 teletypewriter(电传打字机)的缩写。在 Unix 里,它泛指"终端"——一个字符设备,既能接收键盘输入,又能输出文字。 现代 Linux 里的 /dev/tty、/dev/pts/0 都是 TTY 的后代。我们这里用串口 COM1 模拟一个最简单的 TTY。 整体架构 用户键盘输入 ↓ 串口硬件触发 IRQ4 isr_handler → tty_recv(c) ← 回显 + 写入 ring buffer ↓ 遇到 '\n' line_ready = 1 ↓ 进程 syscall SYS_READ(fd=0) tty_read(buf, len) ← 把一行搬到用户 buf ↓ 用户程序处理输入 三个关键组件: Ring Buffer:固定大小的环形缓冲区,存放还未被读走的字符 行规程(line discipline):积累字符,遇 \n 才通知"行就绪" IRQ4 中断处理:从串口读一个字节,交给 TTY Ring Buffer 环形缓冲区是一个固定数组,加上读指针和写指针: [ _ _ _ _ h e l l o \n _ _ ] ↑ ↑ rx_read rx_write rx_len = 6 写字符:rx_buf[rx_write] = c; rx_write = (rx_write + 1) % TTY_BUF_SIZE; rx_len++ ...

May 14, 2026 · 2 min · 大飞

从零写OS(五):中断,内核的神经系统

键盘按下一个键,CPU 是怎么知道的? 不是轮询——CPU 不会没事一直问"键盘有没有按键"。而是靠中断(Interrupt,硬件或软件触发的信号,让 CPU 暂停当前任务去处理紧急事件)。发生了什么事,硬件主动通知 CPU,CPU 暂停手头的事,跳去处理,处理完再回来。 这一章要让内核有能力响应中断。没有这个机制,内核什么都干不了。 中断的分类 x86 的 256 个中断号分三段: 范围 类型 例子 0~31 CPU 异常 除零、缺页、非法指令 32~47 硬件中断 时钟、键盘 48~255 软件中断 系统调用 CPU 异常是 CPU 自己触发的,比如访问了不该访问的内存,CPU 就抛一个缺页异常(Page Fault,访问未映射的虚拟地址时 CPU 触发的异常,操作系统据此做内存按需分配)。 硬件中断是外设触发的,经过 PIC(Programmable Interrupt Controller,可编程中断控制器,负责管理外设中断请求并转发给 CPU)发给 CPU。 IDT:中断的路由表 要处理中断,首先要告诉 CPU:每种中断发生时,去哪个函数处理? 这张路由表叫 IDT(Interrupt Descriptor Table,中断描述符表,256 项,每项对应一个中断号及其处理函数地址)。跟之前的 GDT 一样,是内存里的一张表,用 lidt 指令告诉 CPU 位置。 每个表项里存的是处理函数的地址,还有权限信息。CPU 一旦收到中断,就查这张表,跳到对应函数去执行。 中断处理的细节 CPU 进入中断处理函数之前,会自动把当前的执行状态压栈:rip(下一条指令地址)、cs、rflags、rsp、ss。处理完之后用 iretq(64 位中断返回指令)恢复状态,接着跑。 有一个坑:某些异常 CPU 会自动压入错误码(比如缺页异常),某些不会(比如除零)。为了让处理函数收到的栈结构一致,对没有错误码的异常,要手动压一个 0 占位。这件事用汇编宏做,每种异常一行,干净利落。 第一个测试:故意除以零 IDT 装好之后,最简单的验证方式是故意触发一个异常,看内核能不能捕获到: ...

May 6, 2026 · 1 min · 大飞
京ICP备14031575号-3