Linux 内核架构总览
内核在系统中的位置
Linux 是一个宏内核(Monolithic Kernel),所有核心服务——进程调度、内存管理、文件系统、网络协议栈、设备驱动——都运行在同一个内核地址空间(Ring 0)。与微内核不同,宏内核的优势是子系统间调用开销极低,代价是任何驱动 bug 都可能导致整个系统崩溃。
┌─────────────────────────────────────────────────────┐
│ 用户空间 (Ring 3) │
│ 应用程序 Shell 库 (glibc) 守护进程 │
└──────────────────────┬──────────────────────────────┘
│ 系统调用 (syscall)
┌──────────────────────▼──────────────────────────────┐
│ 内核空间 (Ring 0) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │
│ │ 进程调度 │ │ 内存管理 │ │ 文件系统 │ │ 网络栈 │ │
│ └──────────┘ └──────────┘ └──────────┘ └────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 设备驱动层 │ │
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 硬件抽象层 / 体系结构相关代码 (arch/) │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────┐
│ 硬件 │
│ CPU 内存 磁盘 网卡 GPIO I2C SPI ... │
└─────────────────────────────────────────────────────┘内核源码目录结构
| 目录 | 说明 |
|---|---|
arch/ | 体系结构相关代码(x86、arm、arm64、riscv…) |
drivers/ | 所有设备驱动,按子系统分类 |
fs/ | 文件系统(ext4、btrfs、proc、sysfs…) |
include/ | 内核头文件 |
kernel/ | 核心子系统(调度器、信号、时钟…) |
mm/ | 内存管理子系统 |
net/ | 网络协议栈 |
lib/ | 通用库(链表、红黑树、位图…) |
init/ | 内核启动入口 start_kernel() |
Documentation/ | 官方文档与 binding 规范 |
内核启动流程
上电 → Bootloader (U-Boot/GRUB)
→ 解压内核镜像 (zImage/Image)
→ start_kernel()
├── setup_arch() # 体系结构初始化
├── mm_init() # 内存管理初始化
├── sched_init() # 调度器初始化
├── init_IRQ() # 中断控制器初始化
├── time_init() # 时钟初始化
├── rest_init()
│ ├── kernel_thread(kernel_init) # PID 1
│ └── cpu_idle() # idle 进程
└── kernel_init()
├── do_initcalls() # 执行所有 __initcall 注册的函数
└── 挂载根文件系统,exec /sbin/initdo_initcalls() 是驱动开发者最关心的环节——所有通过 module_init() 注册的驱动初始化函数都在这里按优先级顺序执行。
内核版本与长期支持
| 类型 | 说明 | 示例 |
|---|---|---|
| 主线版本 (mainline) | Linus 维护,每 9-10 周发布 | 6.x |
| 稳定版 (stable) | 修复 bug,无新特性 | 6.x.y |
| 长期支持版 (LTS) | 维护 2-6 年,嵌入式首选 | 6.6 LTS、5.15 LTS |
| 发行版内核 | Ubuntu/RHEL 自行维护,含大量 backport |
嵌入式产品建议选用 LTS 版本,避免频繁跟进主线变动。
内核配置系统
内核通过 Kconfig 管理数千个编译选项:
bash
# 图形化配置(推荐)
make menuconfig
# 基于当前运行内核配置
make localmodconfig
# 查看某选项的依赖关系
grep -r "CONFIG_I2C" arch/arm64/configs/关键配置项(驱动开发必知):
CONFIG_MODULES=y # 支持可加载模块
CONFIG_MODULE_UNLOAD=y # 支持卸载模块
CONFIG_KALLSYMS=y # 符号表(oops 解析必需)
CONFIG_DEBUG_KERNEL=y # 内核调试支持
CONFIG_DYNAMIC_DEBUG=y # 动态调试(pr_debug 按需开启)
CONFIG_KASAN=y # 内存错误检测内核抢占模型
Linux 提供三种抢占配置,影响驱动的实时性要求:
| 配置 | 延迟 | 适用场景 |
|---|---|---|
PREEMPT_NONE | 最高吞吐 | 服务器 |
PREEMPT_VOLUNTARY | 中等 | 桌面 |
PREEMPT | 低延迟 | 嵌入式 |
PREEMPT_RT(PREEMPT_REALTIME) | 硬实时 | 工业控制 |
PREEMPT_RT 补丁将大量自旋锁替换为可睡眠的 rt_mutex,驱动编写时需注意不能在持有 spinlock 时调用可能睡眠的函数。