一、系列简介
本系列从 Linux 内核的用户可见接口出发,自顶向下剖析现代 Linux 操作系统的核心机制。与姊妹篇「从零开始的操作系统」(自底向上手写引导程序)不同,本系列聚焦于 Linux 内核本身的设计与实现——你每天使用的 Linux 系统背后,进程如何被创建与调度、内存如何被分配与回收、文件如何被组织与缓存、网络包如何穿越内核协议栈……每一章都配有可在你自己的 Linux 系统上验证的实践操作,让你从「会用 Linux」进阶到「理解 Linux」。
与姊妹系列的关系
- 「从零开始的操作系统」 = 自底向上,从硬件到内核入口(写引导程序)
- 「从零剖析 Linux 操作系统」 = 自顶向下,从系统调用到内核机制(读 Linux 源码)
- 两者互补,可独立阅读;本系列在涉及启动流程时会引用姊妹系列
二、场景驱动阅读路线
不想按部就班地从第 1 章读到第 20 章?没问题。以下 5 条路线从你日常遇到的真实问题出发,按”你需要什么→内核怎么实现”的顺序串联章节。每条路线可独立阅读,前置依赖已在路线内标注。
路线总览
路线A:我的程序跑起来发生了什么
场景:你在终端输入
./my_app按下回车——Shell 是怎么创建新进程的?程序代码如何被加载到内存?CPU 什么时候开始执行你的 main 函数?进程之间如何通信?
| 顺序 | 章节 | 为什么读这章 |
|---|---|---|
| 1 | Ch1 内核架构全景 | 建立”用户态/内核态隔离”的认知——程序的一切操作都必须穿越这道墙 |
| 2 | Ch2 系统调用 | 理解 fork()/execve() 如何从用户态陷入内核态完成进程创建 |
| 3 | Ch3 进程管理 | 核心:task_struct 全貌、fork-COW-exec 完整流程、进程状态机——回答”进程是什么” |
| 4 | Ch7 虚拟内存与VMA | 理解 execve() 后如何为进程搭建地址空间、缺页异常如何按需加载代码段 |
| 5 | Ch4 进程调度 | 回答”进程何时获得 CPU”——CFS 如何决定下一个运行的是谁 |
| 6 | Ch5 信号与IPC | 进程不是孤岛:Ctrl+C 如何终止进程、管道如何串联命令、共享内存如何加速数据传递 |
路线逻辑:从”程序启动”这一动作出发,先理解进程创建(Ch3),再理解进程的内存布局(Ch7),然后理解进程如何被调度执行(Ch4),最后理解进程间如何交互(Ch5)。
路线B:为什么容器能隔离
场景:Docker run 一个容器,容器里的进程看不到宿主机其他进程、文件系统被隔离、CPU/内存被限制——内核到底做了什么?容器和虚拟机有什么本质区别?
| 顺序 | 章节 | 为什么读这章 |
|---|---|---|
| 1 | Ch1 内核架构全景 | 理解内核空间/用户空间隔离——容器的隔离是”内核对用户空间的二次隔离” |
| 2 | Ch3 进程管理 | 容器的本质是进程——理解 task_struct 和 clone() flags 是 Namespace 的入口 |
| 3 | Ch7 虚拟内存与VMA | 内存 Namespace 如何让容器拥有独立的地址空间视图 |
| 4 | Ch15 Cgroups与Namespaces | 核心:7 种 Namespace 的隔离能力、Cgroup v2 控制器的资源限制——容器技术的内核根基 |
| 5 | Ch14 同步机制 | Cgroup 的资源统计与限制依赖内核同步原语,理解锁机制才能理解 cgroup 的并发安全 |
| 6 | Ch16 安全机制 | 容器逃逸的防线:Capabilities 权限拆分、Seccomp-BPF 系统调用过滤、SELinux 强制访问控制 |
路线逻辑:先建立”进程是容器的基本单元”的认知(Ch3),再理解内存隔离(Ch7),然后直击核心——Namespace+Cgroup(Ch15),最后理解容器的安全边界(Ch14→Ch16)。
路线C:文件读写性能调优
场景:数据库写入延迟飙升、日志文件 fsync 太慢、Direct I/O 和 Buffered I/O 该选哪个、io_uring 真的比 epoll 快吗?
| 顺序 | 章节 | 为什么读这章 |
|---|---|---|
| 1 | Ch1 内核架构全景 | 理解用户态/内核态切换开销——每次 read/write 都要穿越这道墙 |
| 2 | Ch2 系统调用 | 系统调用开销分析——频繁 I/O 的性能瓶颈可能就在 syscall 本身 |
| 3 | Ch9 VFS与文件系统 | 从 open() 到 read() 的 VFS 路径——dcache 命中与否直接影响延迟 |
| 4 | Ch8 页缓存与IO | 核心:Page Cache 命中/未命中的性能天壤之别、脏页写回策略、fsync 语义、Direct I/O、io_uring |
| 5 | Ch10 块设备与IO栈 | I/O 请求如何穿越 blk-mq 到达磁盘——I/O 调度器选择(BFQ vs mq-deadline)对延迟的影响 |
| 6 | Ch6 物理内存管理 | 内存紧张时 kswapd 回收 Page Cache 页面——“可用内存不足”是 I/O 性能骤降的常见根因 |
路线逻辑:从用户态 read()/write() 出发,先理解 VFS 层路径(Ch9),再深入 Page Cache 这一最关键的加速器(Ch8),然后追踪 I/O 到磁盘的完整路径(Ch10),最后理解内存压力如何反噬 I/O 性能(Ch6)。
路线D:系统出问题了怎么查
场景:凌晨 3 点报警——进程卡在 D 状态、OOM Killer 杀了你的 Java 进程、CPU 使用率 100% 但不知道谁在跑、网络延迟飙升。
| 顺序 | 章节 | 为什么读这章 |
|---|---|---|
| 1 | Ch1 内核架构全景 | /proc 和 /sys 是内核向用户空间暴露信息的窗口——排障的第一手数据源 |
| 2 | Ch18 追踪与可观测性 | 核心:ftrace 追踪函数调用、perf 采样分析热点、eBPF 在内核安全执行自定义探测逻辑 |
| 3 | Ch6 物理内存管理 | OOM Killer 的触发条件与选择策略——理解 oom_score 才能防止关键进程被杀 |
| 4 | Ch4 进程调度 | D 状态(TASK_UNINTERRUPTIBLE)进程的成因——等待 I/O 完成时调度器无法抢占 |
| 5 | Ch13 中断与软中断 | Softirq 和 Workqueue 的延迟处理——网络包处理瓶颈常出现在此处 |
| 6 | Ch20 综合实战 | 8 个真实诊断场景的端到端演练——OOM 分析、D 状态排查、调度延迟、I/O 瓶颈、网络丢包 |
路线逻辑:先掌握观测工具(Ch18),再理解三大类常见问题的内核机制——内存问题(Ch6)、调度问题(Ch4)、中断/网络问题(Ch13),最后通过实战场景综合运用(Ch20)。
路线E:网络请求穿越内核的完整旅程
场景:一个 HTTP 请求从网卡到达你的 Web 服务器,再返回响应——数据包如何穿越内核协议栈?epoll 为什么比 select 快?Netfilter 钩子在哪里拦截包?
| 顺序 | 章节 | 为什么读这章 |
|---|---|---|
| 1 | Ch1 内核架构全景 | 建立内核子系统全貌——网络协议栈是七大子系统之一 |
| 2 | Ch2 系统调用 | socket()/connect()/accept() 系统调用——用户态如何向内核发起网络请求 |
| 3 | Ch12 网络协议栈 | 核心:sk_buff 数据包表示、TCP 状态机、NAPI 轮询、Netfilter 5 个钩子点、网络命名空间 |
| 4 | Ch13 中断与软中断 | 网卡中断 → NAPI 轮询 → NET_RX_SOFTIRQ——收包的完整中断处理链路 |
| 5 | Ch8 页缓存与IO | Socket buffer 与 Page Cache 的关系——sendfile() 零拷贝如何绕过用户空间 |
| 6 | Ch14 同步机制 | 协议栈中的锁竞争——sk_buff 链表的并发保护、RCU 在路由表查找中的应用 |
路线逻辑:从用户态 socket API 出发(Ch2),深入协议栈核心(Ch12),理解收包的中断链路(Ch13),再看零拷贝优化(Ch8)和并发保护(Ch14)。
路线交叉参考
同一章节在不同路线中的关注点不同:
| 章节 | 路线A 关注点 | 路线B 关注点 | 路线C 关注点 | 路线D 关注点 | 路线E 关注点 |
|---|---|---|---|---|---|
| Ch1 | 用户态/内核态隔离 | 隔离的层次性 | syscall 开销 | /proc /sys 数据源 | 子系统全貌 |
| Ch3 | fork-COW-exec | clone() flags 与 Namespace | — | — | — |
| Ch4 | CFS 调度决策 | — | — | D 状态成因 | — |
| Ch6 | — | — | kswapd 回收 Page Cache | OOM Killer 策略 | — |
| Ch7 | 进程地址空间布局 | 内存 Namespace | — | — | — |
| Ch8 | — | — | Page Cache 核心 | — | sendfile 零拷贝 |
| Ch12 | — | — | — | — | 协议栈核心 |
| Ch13 | — | — | — | Softirq 瓶颈 | 收包中断链路 |
| Ch14 | — | cgroup 并发安全 | — | — | 协议栈锁竞争 |
| Ch15 | — | 核心章节 | — | — | — |
| Ch18 | — | — | — | 核心章节 | — |
三、知识导图
以下导图展示 21 章知识之间的网络关系。与线性目录不同,这里强调跨子系统的连接——一个
fork()调用同时牵动进程管理、虚拟内存、同步机制三个子系统;一个 Page Cache 同时服务于文件系统和内存管理。
概念关系图
章节网络关系图
知识关联参考表
按三层模型组织:用户可见层(你日常接触的概念)→ 内核机制层(内核如何实现)→ 内核基础层(共享的基础设施)。同一行的条目之间存在直接的知识依赖或概念映射。
| 用户可见概念 | 对应章节 | 内核机制 | 对应章节 | 共享基础 | 对应章节 |
|---|---|---|---|---|---|
进程创建 (fork/exec) | Ch3 | 写时复制 (COW) | Ch7 | 物理页帧分配 | Ch6 |
| 进程获得 CPU | Ch4 | CFS 调度 / 上下文切换 | Ch4 | 中断时钟触发调度 | Ch13 |
malloc() 分配内存 | Ch7 | 缺页异常 → 页表映射 | Ch7 | 伙伴系统 / Slub | Ch6 |
read() 读文件 | Ch9 | VFS → Page Cache 命中/穿透 | Ch8, Ch9 | bio → blk-mq → 磁盘 | Ch10 |
write() 写文件 | Ch8 | 脏页标记 → 延迟写回 | Ch8 | kswapd 页面回收 | Ch6 |
fsync() 持久化 | Ch8 | 脏页刷盘 + 磁盘 FUA | Ch8, Ch10 | 块设备 I/O 调度 | Ch10 |
| 网络收发数据 | Ch12 | sk_buff + TCP 状态机 | Ch12 | 网卡中断 → NAPI → Softirq | Ch13 |
epoll 高并发 | Ch12 | Socket 等待队列 + 唤醒 | Ch12 | 调度器唤醒抢占 | Ch4 |
| Docker 容器隔离 | Ch15 | 7 种 Namespace | Ch15 | clone() flags | Ch3 |
| 容器资源限制 | Ch15 | Cgroup v2 控制器 | Ch15 | 调度器 cgroup 支持 | Ch4 |
| 容器安全边界 | Ch16 | Capabilities + Seccomp | Ch16 | LSM 框架 + cred | Ch3 |
perf top 热点分析 | Ch18 | perf 采样 + 硬件计数器 | Ch18 | 内核模块插桩 | Ch17 |
bpftrace 动态追踪 | Ch18 | eBPF 验证器 + JIT | Ch18 | 内核同步 (RCU) | Ch14 |
| OOM Killer 杀进程 | Ch6 | 内存水位线 + oom_score | Ch6 | 进程凭证 (oom_score_adj) | Ch3 |
| 进程卡在 D 状态 | Ch4 | TASK_UNINTERRUPTIBLE | Ch4 | I/O 等待 + 块设备队列 | Ch10 |
sendfile() 零拷贝 | Ch8 | Page Cache → Socket 直传 | Ch8, Ch12 | DMA 引擎 | Ch11 |
| 系统启动慢 | Ch19 | start_kernel → systemd | Ch19 | 子系统初始化顺序 | Ch1 |
| 内核模块加载 | Ch17 | .ko ELF → 符号解析 | Ch17 | 设备驱动模型 | Ch11 |
Ctrl+C 终止进程 | Ch5 | SIGINT 信号投递 | Ch5 | 进程信号处理 | Ch3 |
| 管道 ` | ` 串联命令 | Ch5 | pipe → 内核缓冲区 | Ch5 | Page Cache (pipe_buffer) |
四、系列大纲
以下是按章节编号排列的完整目录。建议结合上方的场景驱动阅读路线和知识导图选择适合你的阅读顺序。
| 章节 | 标题 | 核心内容 |
|---|---|---|
| 0 | 系列导读 | 系列定位、场景路线、知识导图、环境搭建 |
| 1 | Linux 内核架构全景 | 用户/内核空间隔离、七大子系统、执行上下文、内核数据结构 |
| 2 | 系统调用 | 特权级切换、sys_call_table、VDSO、strace、系统调用开销 |
| 3 | 进程管理 | task_struct、进程状态机、fork-COW-exec、僵尸/孤儿进程 |
| 4 | 进程调度 | CFS、vruntime、调度类、实时调度、上下文切换、cgroup cpu |
| 5 | 信号与进程间通信 | 信号投递、管道、消息队列、共享内存、信号量、Unix 域套接字 |
| 6 | 物理内存管理 | struct page、Zone、伙伴系统、Slub 分配器、kmalloc/vmalloc、kswapd、OOM |
| 7 | 虚拟内存与 VMA | mm_struct、vm_area_struct、缺页异常处理、COW、mmap、页表实现 |
| 8 | 页缓存与 I/O 路径 | Page Cache、address_space、脏页写回、fsync、Direct I/O、io_uring |
| 9 | VFS 与文件系统 | super_block/inode/dentry/file、dcache、ext4、procfs/sysfs/tmpfs |
| 10 | 块设备与 I/O 栈 | bio、blk-mq、I/O 调度器、I/O 优先级、NVMe 驱动模型 |
| 11 | 设备驱动模型 | kobject、sysfs、字符/块/网络设备、udev、内核模块、DMA |
| 12 | 网络协议栈 | sk_buff、NAPI、TCP 状态机、Netfilter、网络命名空间 |
| 13 | 中断与软中断 | IDT、Top Half/Bottom Half、Softirq、Tasklet、Workqueue |
| 14 | 内核同步机制 | 原子操作、内存屏障、自旋锁、互斥锁、RCU、Futex、lockdep |
| 15 | Cgroups 与 Namespaces | 7 种 Namespace、Cgroup v2 控制器、容器运行时 |
| 16 | 安全机制 | Capabilities、SELinux、AppArmor、Seccomp-BPF、Audit |
| 17 | 内核模块 | .ko 结构、模块加载/卸载、符号导出、module_param、printk |
| 18 | 追踪与可观测性 | ftrace、perf、eBPF 程序/Map/验证器、bpftrace、BCC |
| 19 | Linux 启动流程 | start_kernel、initramfs、pivot_root、systemd、内核命令行 |
| 20 | 综合实战 | 8 个真实问题诊断场景,综合运用前 19 章知识 |
五、开发环境搭建
获取内核源码
# 方式一:从 kernel.org 下载稳定版源码wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.12.7.tar.xztar -xvf linux-6.12.7.tar.xz
# 方式二:通过 git 克隆(包含完整历史,较大)git clone --depth=1 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
# 方式三:使用 GitHub 镜像(国内速度更快)git clone --depth=1 https://github.com/torvalds/linux.git配置源码阅读环境
# 安装代码阅读工具sudo apt install cscope ctags universal-ctags
# 在内核源码根目录生成标签文件make tags # 生成 CTAGSmake cscope # 生成 CSCOPE
# VSCode 用户:安装 C/C++ 扩展 + C/C++ Extension Pack# 然后在 .vscode/c_cpp_properties.json 中配置内核源码路径编译最小内核(用于实验)
# 安装编译依赖sudo apt install build-essential libncurses-dev bison flex libssl-dev libelf-dev
# 生成默认配置make defconfig
# 自定义配置(可选)make menuconfig
# 编译内核(-j 参数根据 CPU 核心数调整)make -j$(nproc)
# 编译完成后在 QEMU 中启动qemu-system-x86_64 -kernel arch/x86/boot/bzImage -m 512M内核源码目录结构
linux/├── arch/ # 架构相关代码(x86、arm、riscv 等)├── block/ # 块设备层(blk-mq、I/O 调度器)├── certs/ # 证书与签名├── crypto/ # 加密 API├── drivers/ # 设备驱动(占内核代码量最大)├── fs/ # 文件系统(ext4、proc、sysfs 等)├── include/ # 头文件├── init/ # 内核初始化(start_kernel)├── ipc/ # System V IPC├── kernel/ # 核心子系统(调度、信号、模块等)├── lib/ # 通用库函数├── mm/ # 内存管理├── net/ # 网络协议栈├── samples/ # 示例代码(内核模块等)├── scripts/ # 构建与工具脚本├── security/ # 安全模块(SELinux、AppArmor)├── sound/ # 音频子系统(ALSA)├── tools/ # 用户态工具(perf、bpf 等)├── usr/ # initramfs 构建└── virt/ # 虚拟化(KVM)六、本系列的实践方法论
本系列遵循 观察 → 假设 → 验证 → 源码确认 的学习方法:
- 观察:通过
/proc、/sys、strace、perf等工具观察内核行为 - 假设:根据观察结果,对内核的实现方式提出假设
- 验证:通过编写测试程序或修改内核参数验证假设
- 源码确认:阅读内核源码,确认理解是否正确
每章的「动手实践」部分都遵循这一方法论,让你不仅知道”是什么”,更理解”为什么”。
推荐参考资料
经典教材
| 书籍 | 作者 | 特点 |
|---|---|---|
| 《Linux 内核设计与实现》 | Robert Love | 聚焦 Linux 内核实现,实践性强,适合入门 |
| 《深入理解 Linux 内核》 | Daniel P. Bovet 等 | 对内核各子系统深入剖析 |
| 《深入理解 Linux 网络技术内幕》 | Christian Benvenuti | 网络子系统实现详解 |
| 《Linux 设备驱动程序》 | Jonathan Corbet 等 | 设备驱动开发的权威参考 |
| 《Systems Performance》 | Brendan Gregg | 性能分析与可观测性的百科全书 |
| 《操作系统导论》(OSTEP) | Remzi H. Arpaci-Dusseau | 免费开源,对话式风格,适合建立宏观认知 |
在线资源
- Linux 内核官方文档 — 内核各子系统的权威文档
- Bootlin Elixir Cross Referencer — 在线内核源码交叉引用
- OSDev Wiki — 操作系统开发的百科全书
- Linux Kernel Newbies — 内核开发入门资源
- Brendan Gregg’s Linux Performance — 性能分析参考
源码阅读工具
- cscope:C 符号定义/引用查找,内核源码阅读的经典工具
- ctags / universal-ctags:标签跳转,配合 Vim/Emacs 使用
- Elixir Cross Referencer:在线版,无需本地配置
- VSCode + C/C++ Extension:图形化 IDE 阅读体验
- QEMU + GDB:内核动态调试,打断点、单步执行
准备好开始了吗?从 Linux 内核架构全景 开始你的 Linux 内核之旅吧!
参考
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






