Files
Operating-System/IO_Interrupt/full_process.md
2026-03-22 17:40:44 +08:00

4.6 KiB
Raw Permalink Blame History

好,直接给你一条从“网卡收包 → 中断 → 内核 → 唤醒线程 → 用户 read 返回”纳秒级时间线。你就按这条线在脑子里“放电影”,把线程/中断/调度三者的关系锁死。


0初始状态t = 0

CPU 正在运行 线程A你的程序
线程A 调用了 read(socket)
→ 内核发现没有数据
→ 线程A 被阻塞(睡眠)
→ 调度器切换到 线程B比如别的程序

👉 当前:

CPU 在跑 线程B
线程A 在等数据sleep

1网卡收到数据t ≈ 10ns 级)

网卡 NIC 收到一个数据包(来自网络)

网卡内部:

1根据 hash → 选择 RX queue
2找到一个 descriptor
3准备 DMA

2DMA 写入内存t ≈ 100ns

网卡 → DMA → 内存ring buffer

👉 此时:

数据已经在内存里了
CPU 完全不知道
线程A 还在睡

3网卡决定“是否发中断”t ≈ 100ns ~ μs

取决于策略coalescing

可能:
- 立刻中断
- 等几个包一起中断

假设现在触发:

网卡发 MSIPCIe 写)

4中断到达 CPUt ≈ μs

Local APIC 收到中断
→ 通知 CPU core比如 CPU2

CPU此时

正在执行 线程B

5CPU 响应中断(关键点)

在指令边界:

CPU
1保存现场线程B的寄存器
2查 IDT
3跳转到网卡中断处理函数ISR

👉 现在:

CPU 不在跑线程
CPU 在跑“中断处理代码”

6中断“上半部”hardirq极短

ISR中断处理函数
    - 关闭该设备中断(防止风暴)
    - 标记需要后续处理softirq
    - 立刻返回

时间:

通常 < 5 微秒(必须非常短)

👉 此时:

数据还没真正处理
只是“登记了一下”

7进入 softirq下半部真正干活

CPU 进入 softirqNET_RX

这一步可能:

- 立刻执行
- 或稍后调度执行

softirq 做什么(核心)

1从 ring buffer 批量取包
2交给网络协议栈IP / TCP
3找到对应 socket
4把数据放入 socket buffer

8唤醒线程A关键转折点

发现:
这个 socket 正在被线程A等待

于是:

wake_up(线程A)
→ 线程A 从 sleep → runnable就绪态

👉 现在:

线程A 已经可以运行了
但还没运行

9调度器决定是否切换关键

此时 CPU 还在:

执行 softirq还没回到线程B

接下来两种情况:


情况 A不抢占常见

softirq 执行完
→ 返回中断
→ 恢复线程B

然后某个时刻:

调度器:
    发现线程A优先级高
    → 切换到线程A

情况 B立即抢占

softirq 结束时触发调度
→ 直接切换到线程A

10线程A 恢复执行t ≈ μs ~ ms

CPU 开始执行 线程A

此时:

read() 返回
buf 已经有数据

👉 用户程序看到:

“数据到了”

十一、把整条链压缩成一条“黄金路径”

网卡收包
→ DMA 写内存
→ 发中断
→ CPU 进入 ISR登记
→ softirq 处理数据
→ 放入 socket buffer
→ 唤醒线程A
→ 调度器切换
→ 线程A 继续执行 read()

十二、你要锁死的三件事


1中断干嘛

中断 = 告诉CPU“有事发生了”

2DMA干嘛

DMA = 提前把数据放到内存
不等CPU

3线程干嘛

线程 = 最终处理数据的执行主体

十三、终极一句话(一定要内化)

中断不会“处理你的数据”,
它只是把CPU拉进内核
真正干活的是 softirq 和线程,
真正决定谁运行的是调度器。

如果你这条时间线已经完全顺了,下一步可以直接进“更硬核”的:

👉 为什么 DPDK 能把这整条链砍掉(没有中断、没有内核、没有调度)还能更快?

那一层就是“极限性能模型”。