底层技术二十关
以 Linux 内核为主线,系统深挖底层技术:内核、硬件、编译器、存储、网络。
源码断言审校基线:本专栏引用的内核符号、行号与源码链接,均以 Linux 7.3-rc4(torvalds/linux master,2026-09-26 实测 VERSION=7/PATCHLEVEL=3/SUBLEVEL=0/EXTRAVERSION=-rc4)为审校基准;审校当日 master 的确切 commit 未记录,后续内核版本可能已有漂移。
第 1 关 · 进程与线程:task_struct 与进程创建
内核里没有"进程"和"线程"两种东西——只有 task_struct,以及 clone() 的 flags 旋钮。fork 快不是因为复制得快,而是因为几乎不复制。
第 2 关 · 系统启动:从 Bootloader 到第一个进程
先有鸡还是先有蛋?内核的作弊解是:0 号进程是静态定义的,不是由谁创建的。rest_init 的两行 kernel_thread 按代码顺序决定了 PID 1 和 2 的归属。
第 3 关 · 内存管理总览:页分配器与 slab
内核的内存只有两层生意:buddy 管"页从哪来",slab 管"页里怎么切"。buddy 公式是 addr XOR (PAGE_SIZE << order),2 的 n 次方不是巧合,是地址对齐互补的数学必然。
第 4 关 · 虚拟内存:页表、缺页异常、TLB
vma 是意图、页表是现状,缺页异常是连接两者的修复循环;另含 COW 全链条与 TLB 一致性代价。
第 5 关 · 页面回收:kswapd、LRU 与 swap
物理页不够时,内核变不出内存——只负责决定拆掉谁的映射。
第 6 关 · 调度器:EEVDF 与 vruntime
CFS 的挑选算法已经退役——6.6 起 fair 类里跑的是 EEVDF:挑选分两步,先看“有没有资格”(被亏欠者),再在有资格里选虚拟 deadline 最早的。
第 7 关 · 上下文切换:switch_to、switch_mm 与 PCID
进程切换分两步:换栈便宜,换地址空间贵。switch_to 只碰寄存器和栈,switch_mm 才碰 CR3 和 TLB——PCID 的存在,就是为了让第二步从“全刷 TLB”变成“换个标签”。
第 8 关 · 中断、系统调用与信号:从 IDT 到 sysret
用户态进内核只有三扇门:中断、异常、系统调用。SYSCALL 把进门压到几十条指令,IDT 把向量排成一张表,而信号是内核的“回马枪”——在返回用户态的最后一刻,把执行流劫持到 handler。
第 9 关 · 锁与并发:spinlock、mutex 与 RCU
读多写少时,最好的锁是没有锁——RCU 让读端开销趋近于零(经典 RCU 下只是一次抢占计数),代价是写者等一个“静默期”。
第 10 关 · VFS:inode、dentry 与 page cache
“一切皆文件”的代价,是 inode、dentry、page cache 三层缓存——以及 lookup 的每一步。
第 11 关 · 块层与 I/O:bio、多队列与 io_uring
从 write() 到 NVMe 只有一条路:bio → request → 硬件队列 → 完成。blk-mq 把锁从“一个大锁”拆成“每个 CPU 一份、每个硬件队列一份”;io_uring 把热路径上的系统调用绕过去了——提交和完成都在共享内存里发生,SQPOLL 模式下连“通知内核”那次调用也省了。
第 12 关 · ext4 与 journal:崩溃一致性
崩溃一致性不是“不丢数据”,而是“丢也丢得原子”:journal 把一组元数据修改先顺序写成日志,写完提交记录后再回写原位置。
第 13 关 · 网络协议栈:socket、TCP 与 NAPI
收包不靠中断——中断只负责“叫醒”,真正的搬运是 NAPI 轮询干的。
第 14 关 · 拥塞控制与性能:BBR、GRO 与 XDP
带宽是物理的,延迟是设计的——BBR 不拿丢包当油门,先建模再决策;XDP 在网卡驱动层就动手。
第 15 关 · 缓存一致性:MESI 与伪共享
多核的第一性原理是缓存行——两个核“各写各的变量”,可能只是在互相 invalidate。
第 16 关 · 流水线、分支预测与 Spectre
CPU 一直在赌,而且经常赌赢——Spectre 是“赌输了没擦干净”的代价。
第 17 关 · PCIe、DMA 与中断:设备如何与 CPU 协作
设备和 CPU 用内存地址对话——DMA 是“你别碰,我自己搬”,MSI-X 是“别广播,点对点叫我”。
第 18 关 · 内存模型:LKMM 与 happens-before
编译器和 CPU 都在重排你的代码——内存模型是“哪些重排不许做”的契约。
第 19 关 · 编译器优化:O2、内联与 PGO
O2 不是魔法——是几百个“大概率划算”的小赌注,外加一条“别骗我”的契约。
第 20 关 · 链接与加载:ELF、动态链接与内核模块
main 之前,ld.so 先替你算完地址——PLT/GOT 是“先跑起来,再慢慢算地址”的懒惰。