ISR 极简 4 步流程¶
日期:2026-06-25 标签:rust, isr, riscv, interrupt, uart, dependency-injection
来源:StarryOS
uart_16550/src/async_/isr.rs:71-92通读。 范围:NS16550 ISR handler 的 4 步流程 + 依赖注入设计。
4 步流程¶
pub fn uart_isr_handler(_irq, base, fn_disable_rx, fn_disable_tx) {
unsafe {
let regs = IsrRegisters::new(base);
let isr = regs.read_isr(); // 1. 读 ISR 寄存器
match isr.interrupt_type() {
Some(RDR|RTI) => {
fn_disable_rx(); // 2. 禁用对应中断
RX_WAKER.wake(); // 3. 唤醒 waker
}
Some(THRE) => {
fn_disable_tx();
TX_WAKER.wake();
if regs.read_lsr().contains(TEMT) {
DRAIN_WAKER.wake();
}
}
_ => {}
}
// 4. 立即返回
}
}
全程 ~1.5 µs。
3 个中断类型¶
NS16550 ISR 寄存器的 bits 2-1 编码:
| bits 2:1 | 类型 | 处理 |
|---|---|---|
| 00 | ModemStatus | 忽略(_ => {}) |
| 01 | THRE | TX + 条件 DRAIN |
| 10 | RDR | RX |
| 11 | RTI | RX(与 RDR 合并) |
RDR + RTI 合并:两者都意味着"有 RX 数据"。
IsrRegisters:锁无关 MMIO 读取¶
pub(crate) struct IsrRegisters {
base: NonNull<u8>,
}
impl IsrRegisters {
pub(crate) unsafe fn read_isr(&self) -> ISR {
let ptr = self.base.as_ptr().add(offsets::ISR);
ISR::from_bits_retain(ptr.read_volatile())
}
}
不走 Uart16550::isr()(需要 SpinNoIrq 锁,违反 ISR 极简),改用 read_volatile 读 MMIO 寄存器。
read_volatile 防止编译器优化掉寄存器读。
DRAIN_WAKER 条件唤醒¶
| 时机 | THR_EMPTY | TEMT | 唤醒 |
|---|---|---|---|
| THRE 中断 + 移位寄存器还在发 | 1 | 0 | TX |
| THRE 中断 + 移位寄存器也空 | 1 | 1 | TX + DRAIN |
tcdrain 关心"全部数据离开芯片到线缆",必须 TEMT 才算完成。
依赖注入¶
fn_disable_rx: fn() 和 fn_disable_tx: fn() 是函数指针参数。
// uart_init.rs:171
fn uart_isr_wrapper(_irq: usize) {
uart_isr_handler(_irq, base,
|| UART_PORT.update_ier(IER::empty(), IER::DATA_READY),
|| UART_PORT.update_ier(IER::empty(), IER::THR_EMPTY),
);
}
uart_16550 crate 不知道 StarryOS 的 UART_PORT。调 OS API = OS 耦合,函数指针注入 = 解耦。
| 维度 | 调 OS API | 注入 |
|---|---|---|
| 解耦 | ❌ | ✅ |
| 性能 | 零开销 | ~1ns 函数指针 |
| 类型安全 | 编译期 | 编译期 |
Q8.2 修复¶
之前 ISR 中调 uart.isr() + disable_*_intr() 走 SpinNoIrq 锁。
Q8.2 修复:拆出 uart_isr_handler + 闭包,锁只在 update_ier 一处。