Linux Kernel 内核整体架构
2026年7月23日 · 5310 字 · 11 分钟 · Linux 操作系统 内核 源码阅读
元信息
- 类型:技术博客 / 架构总览
- 作者/机构:知乎「玩转Linux内核」
- 原文链接:https://zhuanlan.zhihu.com/p/438248184
- 阅读日期:2026-07-23
- 关联主题:Linux 内核整体架构、子系统划分、源码目录
- 阅读状态:粗读并整理为架构地图
一句话结论
这篇文章适合当 Linux 内核入门的“地图”:Linux 内核的核心任务是向下管理硬件,向上通过系统调用给应用提供统一接口;为了完成这个任务,内核可以先粗分为进程调度、内存管理、VFS、网络、IPC 五类核心子系统,再把这些子系统映射到源码目录。
需要注意:原文基于 Linux 3.10.29,且体系结构相关内容偏 ARM。它适合作为整体认知框架,不适合直接当作现代 Linux 最新目录和实现细节的精确说明。
阅读目的
- 建立 Linux 内核“为什么这样分层”的第一张地图。
- 知道进程调度、内存管理、VFS、网络、IPC 各自解决什么问题。
- 把内核子系统和源码顶层目录对应起来,后续读源码时知道先去哪里找。
- 为后续专题阅读提供导航:调度看
kernel/,内存看mm/,文件系统看fs/,网络看net/,驱动看drivers/。
1. Linux 内核处在什么位置
从这张图看,Linux 内核夹在两层之间:
应用程序 / C 库 / Library Routine
-> 系统调用
Linux Kernel
-> 驱动和硬件控制
CPU / Memory / I/O / Network / 外设
直觉解释:应用程序不能随便直接操作硬件,否则系统会失控。内核就是中间的管理者,负责把危险、复杂、硬件相关的操作封装起来。
技术解释:用户态程序通过系统调用进入内核态,内核再调度 CPU、分配内存、访问文件、收发网络包、驱动硬件设备。
应用解释:写 read()、write()、malloc()、fork()、socket() 这类代码时,表面上调用的是库函数或系统调用,背后最终都会落到某个内核子系统。
2. 内核整体架构:五大核心子系统
原文把 Linux 内核核心功能拆成五类:
| 子系统 | 主要管理对象 | 给用户/上层提供什么 |
|---|---|---|
| Process Scheduler | CPU 时间 | 让多个进程/线程共享 CPU |
| Memory Manager | 物理内存、虚拟内存 | 地址空间、内存分配、内存映射、换页 |
| VFS | 文件、目录、设备抽象 | open/read/write/close 等统一文件接口 |
| Network | 网卡、协议栈、socket | TCP/IP、UDP、socket 通信接口 |
| IPC | 进程间通信对象 | 信号量、消息队列、共享内存、信号等通信机制 |
这五个子系统可以用一句话记:
调度管 CPU,内存管地址,VFS 管文件和设备入口,网络管包,IPC 管进程之间说话。
3. 进程调度:谁来用 CPU
进程调度解决的问题是:CPU 数量有限,但系统里有很多可运行任务,内核必须决定“下一刻谁上 CPU”。
原文把调度子系统拆成四层:
| 模块 | 作用 |
|---|---|
| Scheduling Policy | 调度策略,决定哪个任务优先运行 |
| Architecture-independent Scheduler | 体系结构无关的调度核心,负责通用调度逻辑 |
| Architecture-specific Schedulers | 体系结构相关部分,处理寄存器、上下文切换、CPU 指令差异 |
| System Call Interface | 向用户态暴露调度相关接口 |
直觉解释:调度器像一个 CPU 时间分配员,决定每个进程什么时候跑、跑多久、什么时候被换下。
技术解释:调度策略选择下一个任务,通用调度代码维护运行队列和调度类,架构相关代码完成上下文切换,把 CPU 寄存器状态从旧任务切到新任务。
应用解释:当程序调用 sched_yield()、设置优先级,或者系统发生时钟中断时,都可能影响调度决策。
源码入口先记:
| 目录 | 关系 |
|---|---|
kernel/ |
调度、进程核心逻辑所在的主要目录之一 |
arch/<arch>/ |
上下文切换、体系结构相关调度支持 |
include/linux/sched.h |
调度和任务结构相关头文件,后续读源码常遇到 |
4. 内存管理:虚拟地址和物理内存之间的桥
内存管理解决的问题是:应用看到的是自己的虚拟地址空间,但机器上真实存在的是有限的物理内存。内核负责建立、维护和保护这层映射。
原文把内存管理拆成三层:
| 模块 | 作用 |
|---|---|
| Architecture Specific Managers | 体系结构相关部分,处理页表格式、MMU、TLB、硬件访问细节 |
| Architecture Independent Manager | 体系结构无关部分,处理通用内存管理机制 |
| System Call Interface | 向用户态提供内存分配、释放、文件映射等接口 |
直觉解释:每个进程像拿到一张“自己的地址地图”,但这些地址不直接等于真实内存位置。内核和硬件一起把虚拟地址翻译到物理内存。
技术解释:内核以进程为单位维护地址空间,通过页表、缺页异常、页面分配、回收、交换、文件映射等机制管理内存。体系结构相关代码负责适配不同 CPU 的页表和 MMU 细节。
应用解释:malloc()、mmap()、进程启动、动态库加载、缺页异常、swap,都离不开内存管理子系统。
源码入口先记:
| 目录 | 关系 |
|---|---|
mm/ |
内存管理核心代码 |
arch/<arch>/mm/ |
体系结构相关内存管理代码 |
include/linux/mm.h |
内存管理常见结构和接口 |
5. VFS:把不同文件系统和设备统一成文件接口
VFS 解决的问题是:磁盘文件、设备、伪文件系统、不同格式的文件系统差异很大,但用户态希望用统一接口访问它们。
原文把 VFS 相关结构拆成这些模块:
| 模块 | 作用 |
|---|---|
| Device Drivers | 具体设备驱动,控制外部设备和控制器 |
| Device Independent Interface | 统一设备模型,屏蔽具体硬件差异 |
| Logical Systems | 具体逻辑文件系统,例如 ext、FAT 等 |
| System Independent Interface | 用统一接口表示设备和文件系统 |
| System Call Interface | 向用户态提供文件访问系统调用 |
直觉解释:VFS 是“文件访问的统一前台”。用户说我要 open/read/write,VFS 再把请求分发给具体文件系统或设备驱动。
技术解释:VFS 定义 inode、dentry、file、super_block 等统一对象,以及 file_operations、inode_operations 等操作表。具体文件系统和设备驱动实现这些接口。
应用解释:为什么 Linux 可以用 read() 读普通文件,也可以读设备文件?因为上层看到的是统一文件接口,底层由 VFS 和驱动完成分发。
源码入口先记:
| 目录 | 关系 |
|---|---|
fs/ |
VFS 和具体文件系统核心目录 |
drivers/ |
设备驱动,很多设备最终以文件接口暴露 |
block/ |
块设备层,和磁盘/文件系统 IO 密切相关 |
include/linux/fs.h |
VFS 核心结构和接口 |
这里要记住一个边界:原文说“一切皆文件”不是绝对的。Linux 大量设备和资源可以通过文件接口访问,但 CPU、内存、网络协议栈内部状态并不都天然是文件。它更准确的意思是:Linux 倾向于用文件抽象统一很多系统资源的访问入口。
6. 网络子系统:从 socket 到网卡
网络子系统解决的问题是:应用使用 socket 收发数据,但底层涉及协议栈、路由、缓存队列、网卡驱动和硬件。
原文把网络子系统拆成五层:
| 模块 | 作用 |
|---|---|
| Network Device Drivers | 网卡驱动,负责具体网络设备 |
| Device Independent Interface | 屏蔽网卡硬件差异 |
| Network Protocols | 实现 IP、TCP、UDP 等协议 |
| Protocol Independent Interface | 屏蔽协议差异,向上提供 socket 抽象 |
| System Call Interface | 向用户态提供网络相关系统调用 |
直觉解释:socket 是应用看到的“网络文件句柄”,网络子系统负责把写入 socket 的数据变成网卡能发出去的包。
技术解释:用户态系统调用进入 socket 层,再经过协议无关接口、具体协议栈、路由、邻居子系统、队列和网卡驱动,最后到硬件。
应用解释:connect()、send()、recv()、epoll() 背后都和网络子系统相关。后续读网络协议栈时,可以沿着“系统调用 -> socket -> TCP/UDP/IP -> device driver”这条链走。
源码入口先记:
| 目录 | 关系 |
|---|---|
net/ |
网络协议栈核心代码 |
drivers/net/ |
网络设备驱动 |
include/net/ |
网络子系统内部头文件 |
include/linux/net.h |
Linux 网络相关通用接口之一 |
7. IPC:进程之间怎么通信
原文没有展开 IPC,只把它列为核心子系统之一。
IPC 解决的问题是:进程默认有独立地址空间,不能直接随便读写彼此内存,但很多场景需要协作,所以内核提供受控通信机制。
常见 IPC 机制包括:
| 机制 | 用途 |
|---|---|
| signal | 异步通知进程发生了某件事 |
| pipe | 父子进程或相关进程之间的字节流通信 |
| message queue | 按消息传递数据 |
| semaphore | 进程间同步 |
| shared memory | 多进程共享同一段内存 |
| futex | 用户态锁和内核等待队列配合的基础机制 |
源码入口先记:
| 目录 | 关系 |
|---|---|
ipc/ |
System V IPC 等实现 |
kernel/signal.c |
信号相关核心逻辑 |
kernel/futex/ 或相关 futex 文件 |
futex 机制,具体路径随内核版本变化 |
8. 源码顶层目录怎么对应子系统
原文最后给出 Linux 源码顶层目录说明。后续阅读时,可以先按下面这张表导航:
| 目录/文件 | 主要作用 | 对应主题 |
|---|---|---|
include/ |
内核头文件,很多核心结构和接口定义在这里 | 全局 |
kernel/ |
进程、调度、时间、信号等核心内核逻辑 | 进程管理 / 调度 |
mm/ |
内存管理 | 内存管理 |
fs/ |
VFS 和具体文件系统 | 文件系统 |
net/ |
网络协议栈,不含大部分网卡驱动 | 网络 |
ipc/ |
进程间通信 | IPC |
arch/<arch>/ |
体系结构相关代码,例如 x86、arm | 架构适配 |
arch/<arch>/boot/dts |
Device Tree 文件,常见于 ARM/嵌入式 | 设备树 |
init/ |
内核启动初始化 | 启动流程 |
block/ |
块设备层 | 存储 / IO |
drivers/ |
设备驱动 | 驱动 |
lib/ |
内核内部库函数 | 通用工具 |
crypto/ |
加密/解密算法 | 安全 / 加密 |
security/ |
安全框架,例如 SELinux | 安全 |
virt/ |
虚拟化支持,例如 KVM | 虚拟化 |
usr/ |
initramfs 相关 | 启动 |
firmware/ |
固件文件 | 驱动 |
samples/ |
示例代码 | 学习参考 |
tools/ |
辅助工具,例如性能分析、自测试 | 工具 |
Kconfig |
配置系统入口 | 编译配置 |
Kbuild |
Kbuild 构建规则 | 编译系统 |
Makefile |
顶层构建入口 | 编译系统 |
scripts/ |
构建、配置、检查脚本 | 编译系统 |
Documentation/ |
内核文档 | 官方说明 |
MAINTAINERS |
维护者名单 | 查模块归属 |
机制拆解
输入
Linux 内核面对的输入不是一种,而是多类资源和请求:
- 应用程序发起的系统调用。
- 硬件设备产生的中断。
- CPU 时间片和调度事件。
- 缺页异常、内存压力。
- 网络包、磁盘 IO、设备事件。
处理过程
内核把这些输入分发给不同子系统:
系统调用
-> syscall interface
-> 调度 / 内存 / VFS / 网络 / IPC
-> 具体文件系统、协议栈、驱动或硬件相关代码
不同子系统之间不是孤立的。例如:
read()可能经过 VFS、具体文件系统、块设备层、驱动、调度和内存管理。send()可能经过 socket、TCP/IP 协议栈、网卡驱动、内存分配和调度。fork()同时涉及进程管理、内存管理、文件描述符、信号等。
输出
内核最终给用户态或硬件产生结果:
- 返回系统调用结果。
- 修改进程状态。
- 分配或回收内存。
- 读写文件或设备。
- 发送/接收网络包。
- 唤醒等待中的任务。
边界条件
- 原文是架构总览,不是实现细节说明。
- 原文基于 Linux
3.10.29,现代内核目录和实现已有变化。 - 原文偏 ARM 视角,x86、RISC-V 等体系结构相关路径和机制会不同。
- “五大子系统”是学习分类,不是内核源码中的硬边界。真实调用路径经常跨多个子系统。
关键细节
- Linux 内核的核心功能可以压缩成一句话:管理硬件资源,并通过系统调用向上提供受控接口。
- 子系统划分要围绕资源看:CPU、内存、文件/设备、网络、进程间通信。
- VFS 是理解 Linux 的关键抽象之一,因为它把普通文件、设备文件、伪文件系统统一到类似的访问模型下。
- 网络子系统虽然也提供 socket 这种“类文件描述符”接口,但内部协议栈和 VFS 不是一回事。
drivers/占据大量代码量,但驱动代码多不等于内核最核心思想都在驱动里;调度、内存、VFS、网络协议栈才是理解内核机制的主线。- 源码目录是阅读入口,不等于模块边界。比如一次文件读取可能同时经过
fs/、mm/、block/、drivers/。
常见误解
| 误解 | 更准确的理解 |
|---|---|
| Linux 内核就是驱动集合 | 驱动很多,但内核还包括调度、内存、VFS、网络、IPC、安全、虚拟化等核心机制 |
| 每个子系统只在一个目录里 | 顶层目录只是主要入口,真实实现经常跨目录 |
| “一切皆文件”表示所有资源都是文件 | 更准确地说,Linux 用文件接口统一了很多资源,但并非所有内部资源天然都是文件 |
| VFS 只管理磁盘文件 | VFS 还参与设备文件、伪文件系统、内存文件系统等统一抽象 |
| 系统调用就是 C 库函数 | C 库函数可能封装系统调用;真正进入内核的是 syscall |
我的理解
这篇文章最有用的地方不是每个模块的细节,而是提供了一个“阅读坐标系”。
学 Linux 内核很容易迷路,因为一个功能会跨很多目录。比如执行 ls,表面是用户命令,背后会走系统调用、VFS、目录项缓存、具体文件系统、块设备层、内存分配、调度。没有整体地图时,会以为每个文件都是孤立知识点。
更好的读法是先建立资源主线:
CPU -> 调度
内存 -> mm
文件和设备入口 -> VFS
网络包 -> net
进程协作 -> IPC
硬件差异 -> arch + drivers
启动和构建 -> init + Kbuild/Makefile/scripts
后续读任何专题,都先问:这个问题主要管理哪类资源?它从哪个系统调用或硬件事件进入?它最终落到哪个子系统?
和当前主题的关系
这是 Linux 内核阅读项目的“总览入口”。后续可以按它拆出几条阅读路线:
| 路线 | 先读目录 | 适合问题 |
|---|---|---|
| 进程与调度 | kernel/、include/linux/sched.h、arch/<arch>/ |
进程如何创建、切换、睡眠、唤醒 |
| 内存管理 | mm/、arch/<arch>/mm/ |
虚拟内存、缺页、页表、分配器 |
| 文件系统 | fs/、block/、drivers/ |
open/read/write 怎么落到磁盘或设备 |
| 网络协议栈 | net/、drivers/net/ |
socket、TCP/IP、网卡收发包路径 |
| 设备驱动 | drivers/、arch/<arch>/boot/dts |
设备如何描述、匹配、初始化和访问 |
| 编译系统 | Makefile、Kbuild、Kconfig、scripts/ |
内核如何配置和编译 |
疑问
| 问题 | 为什么重要 | 下一步 |
|---|---|---|
原文基于 Linux 3.10.29,现代内核顶层目录是否有明显变化? |
后续读当前内核源码时不能照搬旧版本说明 | 对照当前 Linux 源码顶层目录更新一版现代目录表 |
| 原文把 VFS 和设备驱动放在同一张图里,容易让人误以为驱动属于 VFS 子系统内部 | 驱动、设备模型、VFS、块层之间边界需要进一步澄清 | 后续读设备模型和 VFS 时画一张更准确的数据流图 |
| IPC 原文没有展开 | IPC 是理解进程协作和同步的重要入口 | 后续单独读 IPC、signal、futex、pipe 相关资料 |
可迁移启发
- 学复杂系统先找资源主线,再看模块目录。
- 总览图的价值是导航,不是替代源码细节。
- 读内核源码时,最好把“用户态 API / 系统调用 / 子系统 / 驱动或硬件”串成路径,不要只背目录名。
来源摘记
- 原文说明 Linux 内核对下管理硬件设备,对上通过系统调用向库函数或应用提供接口。
- 原文将内核核心功能划分为 Process Scheduler、Memory Manager、VFS、Network、IPC 五个子系统。
- 原文基于 Linux
3.10.29,并约定体系结构相关内容以 ARM 为分析对象。