第一章:内核全景图——Linux内核架构概览

说实话,我第一次接触Linux内核源码时,整个人是懵的。几万个文件,上千万行代码,从哪看起?

后来我慢慢悟出一个道理:读内核,先看骨架,再看血肉。今天我们就来搭这个骨架。

1.1 Linux内核架构概览

Linux内核本质上是一个宏内核(Monolithic Kernel)。什么意思呢?就是所有核心功能——进程管理、内存管理、文件系统、网络协议栈、设备驱动——全都跑在内核空间里,共享同一个地址空间。

我当年刚入行时,有个老前辈跟我说:“你想想看,如果把这些模块拆成微内核,每个模块之间靠消息传递,性能得打多少折扣?”确实,Linux选择宏内核,核心原因就是性能

但宏内核也有代价——一个驱动崩溃,整个系统可能就挂了。所以后来有了可加载内核模块(LKM)机制,算是做了个折中。

核心架构分层(从上到下):

  • 系统调用接口:用户空间进入内核空间的唯一入口
  • 进程管理:调度、创建、同步、信号
  • 内存管理:虚拟内存、页分配、SLAB分配器
  • 文件系统:VFS层 + 具体文件系统(ext4、btrfs等)
  • 网络协议栈:套接字、TCP/IP、Netfilter
  • 设备驱动:字符设备、块设备、网络设备
  • 硬件抽象层:中断处理、DMA、MMU

下面这张图是我自己画的内核架构关系图,你看一眼就能明白各模块怎么协作的:

用户空间(User Space) 应用程序、C库(glibc) 系统调用接口(Syscall) 内核空间(Kernel Space) 进程管理 调度器 / 同步 / 信号 CFS / 进程间通信 内存管理 页分配 / SLAB / vmalloc 缺页异常 / KSM 文件系统 VFS / ext4 / btrfs 页缓存 / inode 网络协议栈 套接字 / TCP/IP Netfilter / 路由 设备驱动 字符 / 块 / 网络设备 PCI / USB / I2C 硬件抽象层 中断 / DMA / MMU 时钟 / 定时器 硬件层(CPU / 内存 / 磁盘 / 网卡)

1.2 内核源码目录结构分析

下载一份Linux内核源码,解压后你会看到这样的顶层目录。我建议你按这个顺序去理解:

linux-6.x/
├── arch/          # 架构相关代码(x86、arm、riscv等)
├── block/         # 块设备层(I/O调度、bio)
├── crypto/        # 加密API
├── Documentation/ # 内核文档(必看!)
├── drivers/       # 设备驱动(占一半以上代码量)
├── fs/            # 文件系统(VFS + 各具体实现)
├── include/       # 头文件(内核API定义)
├── init/          # 内核初始化(main.c)
├── ipc/           # 进程间通信(信号量、消息队列)
├── kernel/        # 核心子系统(调度、信号、时间)
├── lib/           # 内核工具函数(printf、链表、红黑树)
├── mm/            # 内存管理(页分配、SLAB、oom)
├── net/           # 网络协议栈(socket、tcp、udp)
├── scripts/       # 编译脚本、配置工具
├── security/      # 安全模块(SELinux、AppArmor)
├── sound/         # 音频子系统
├── tools/         # 用户空间工具(perf、objtool)
└── usr/           # initramfs 相关

我的阅读建议:别从arch/开始看,那是给特定CPU架构准备的。先从kernel/、mm/、fs/这三个目录入手,它们包含了最核心的逻辑。我个人习惯是:先看init/main.c,那是内核的入口点,然后顺着start_kernel()函数一路跟下去。

1.3 内核版本演进与选择

Linux内核版本号现在用的是主版本.次版本.补丁级别的格式。举个例子:6.1.25,6是主版本,1是次版本,25是补丁号。

这里有个坑,我当年踩过——不要盲目追新。我曾经在生产环境上用了5.19,结果某个网卡驱动有bug,折腾了两天。后来我学乖了:

版本类型 特点 适用场景
LTS(长期支持) 维护周期长达6年,稳定 生产环境、嵌入式产品
主线(Mainline) 最新特性,每2-3个月发布 开发测试、学习研究
稳定(Stable) 基于主线打补丁 需要最新驱动但求稳
下一个(Next) 合并窗口前的代码 内核开发者、尝鲜

对于阅读源码来说,我推荐选一个LTS版本,比如6.1.x或5.15.x。为什么?因为文档齐全,社区讨论多,遇到问题容易找到答案。

1.4 阅读前的准备工作

工欲善其事,必先利其器。我列一个清单,你照着准备就行:

  1. 获取源码:从 kernel.org 下载,或者用 git 克隆 Linus 的仓库
  2. 搭建阅读环境:我个人推荐 VSCode + clangd 或者 Source Insight(Windows下)。别用记事本看,会疯的。
  3. 编译内核:至少编译一次,确保环境没问题。命令很简单:
make defconfig
make -j$(nproc)

避坑指南:我曾经在编译时忘了安装 flexbison,结果报了一堆莫名其妙的错。Ubuntu下执行 sudo apt install build-essential flex bison libncurses-dev 就能搞定。

  1. 准备工具链:gcc、make、elfutils、perf(性能分析时用)
  2. 找一份参考资料:我推荐《Linux内核设计与实现》(LKD)第三版,虽然老但经典。还有《深入理解Linux内核》(ULK)第三版,细节多。
  3. 配置内核模块:用 make menuconfig 打开配置界面,把你要研究的模块编译进内核或作为模块。

嗯,准备工作就这些。别想着一次全看懂,内核源码是读不完的。我的经验是:带着问题去读。比如“进程是怎么创建的?”、“内存页是怎么分配的?”,然后顺着代码追下去。

好了,第一章就到这里。记住:读内核源码,耐心比聪明更重要。


公众号:蓝海资料掘金营,微信deep3321