2. Linux 内核源码结构:获取方式、顶层目录与构建体系
理解 Linux 内核源码的组织方式是进行内核配置与嵌入式系统构建的第一步。本章将系统介绍内核源码的获取途径、顶层目录的功能划分,以及支撑整个内核配置与编译的 Makefile 与 Kconfig 体系。
2.1 内核源码获取方式
获取 Linux 内核源码主要有两种官方途径:通过 kernel.org 下载稳定发布包,或通过 Git 仓库 克隆完整的开发历史。两种方式各有适用场景。
| 获取方式 | 命令 / 示例 | 适用场景 | 特点 |
|---|---|---|---|
| kernel.org 压缩包 | |
嵌入式产品发布、固定版本构建 | 体积小、版本固定、无需网络 |
| Git 克隆主线仓库 | |
内核开发、跟踪最新补丁 | 完整历史、可切换分支、占用空间大 |
| Git 克隆稳定仓库 | |
长期维护(LTS)项目 | 包含稳定分支与补丁集 |
实践建议: 对于嵌入式 Linux 产品开发,推荐从 kernel.org 下载 LTS 版本的 tar.xz 包,解压后纳入版本管理。这样可保证构建环境可复现,且避免克隆整个 Git 历史(通常超过 3GB)。
2.2 顶层目录结构
Linux 内核源码的顶层目录结构清晰反映了其模块化设计思想。每个顶层目录对应一个核心子系统或构建基础设施。以下是最关键的目录及其功能说明。
2.3 关键目录详解
2.3.1 arch/ — 体系结构相关代码
该目录按 CPU 架构划分子目录,如 arch/arm/、arch/arm64/、arch/x86/、arch/riscv/。每个子目录下包含:
- boot/:引导代码(如压缩后的内核入口)
- kernel/:架构特定的核心代码(如中断处理、信号)
- mm/:架构特定的内存管理(如页表操作)
- mach-xxx/:特定 SoC 的板级支持代码(旧式 ARM)
- dts/:设备树源文件(Device Tree Source)
2.3.2 drivers/ — 设备驱动
这是内核中最大的目录,按设备类型划分子目录:
drivers/gpio/、drivers/i2c/、drivers/spi/— 总线协议驱动drivers/net/、drivers/mmc/、drivers/usb/— 外设接口驱动drivers/clk/、drivers/pinctrl/— 时钟与引脚控制drivers/input/、drivers/media/— 输入与多媒体设备
2.3.3 fs/ — 文件系统
包含所有内核支持的文件系统实现:
fs/ext4/、fs/btrfs/、fs/xfs/— 本地磁盘文件系统fs/squashfs/、fs/jffs2/、fs/ubifs/— 嵌入式只读与闪存文件系统fs/proc/、fs/sysfs/、fs/debugfs/— 虚拟文件系统fs/nfs/、fs/cifs/— 网络文件系统
2.3.4 include/ — 内核头文件
头文件分为两类:
include/linux/— 内核内部使用的通用头文件(如include/linux/gpio.h)include/uapi/— 用户空间 API 头文件(如include/uapi/linux/ioctl.h)include/asm-generic/— 体系结构无关的汇编头文件模板
2.4 Makefile 与 Kconfig 体系
Linux 内核的配置与编译系统由 Kconfig(配置语言)和 Makefile(编译规则)两大组件构成。它们共同实现了从配置选项到最终内核镜像的完整构建流程。
2.4.1 Kconfig 语法示例
每个子目录下的 Kconfig 文件定义了该目录的配置选项。以下是一个典型的 GPIO 控制器驱动配置:
# drivers/gpio/Kconfig 片段
config GPIO_PL061
tristate "ARM PrimeCell PL061 GPIO support"
depends on ARM_AMBA
help
Say yes here to support the ARM PrimeCell PL061 GPIO device.
This driver can also be built as a module.
关键语法元素:
- tristate:三态选项(y=编译进内核,m=编译为模块,n=不编译)
- bool:布尔选项(只能 y 或 n)
- depends on:依赖关系,只有前置条件满足时该选项才可见
- select:反向选择,选中当前选项时自动选中另一个选项
- help:帮助文本,在 menuconfig 界面中显示
2.4.2 Makefile 条件编译规则
与 Kconfig 对应的 Makefile 使用 obj-$(CONFIG_XXX) 模式实现条件编译:
# drivers/gpio/Makefile 片段
# 基础对象
obj-y += gpiolib.o
obj-$(CONFIG_GPIOLIB) += gpiolib-devres.o
# 条件编译:根据 CONFIG 决定是否编译
obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o
obj-$(CONFIG_GPIO_CDEV) += gpiolib-cdev.o
# 如果 CONFIG_GPIO_PL061=y,则 gpio-pl061.o 被编译进内核
# 如果 CONFIG_GPIO_PL061=m,则 gpio-pl061.o 被编译为独立模块
# 如果 CONFIG_GPIO_PL061=n,则 gpio-pl061.o 不被编译
嵌入式开发要点: 在移植内核到新板卡时,开发者通常需要:
- 在
arch/arm/mach-xxx/或设备树中添加板级描述 - 在
drivers/下添加或修改外设驱动 - 在对应目录的 Kconfig 中添加新配置选项
- 在对应目录的 Makefile 中添加
obj-$(CONFIG_XXX)编译规则 - 通过
make menuconfig或make defconfig生成 .config
2.5 配置生成与编译命令速查
| 命令 | 功能 | 输出文件 |
|---|---|---|
make menuconfig |
基于 ncurses 的交互式配置界面 | .config |
make defconfig |
使用架构默认配置生成 .config | .config |
make ARCH=arm defconfig |
指定 ARM 架构的默认配置 | .config |
make savedefconfig |
将当前 .config 精简为最小 defconfig | defconfig |
make olddefconfig |
基于现有 .config 更新,新选项使用默认值 | .config |
make -j$(nproc) |
编译内核与模块 | vmlinux, zImage, *.ko |
make modules_install |
安装内核模块到 /lib/modules/ | 模块文件 |
常见陷阱: 在嵌入式开发中,
make defconfig 生成的配置通常过于庞大(包含大量不需要的驱动)。建议基于目标硬件从零开始裁剪,或使用 vendor 提供的 defconfig 文件(如 arch/arm/configs/imx_v7_defconfig)作为起点。
2.6 本章小结
- 内核源码可通过 kernel.org 下载稳定包或 Git 克隆 获取,嵌入式项目推荐使用 LTS 版本的 tar.xz 包。
- 顶层目录中,arch/、drivers/、fs/、include/、kernel/ 是嵌入式开发最常打交道的目录。
- Kconfig 定义配置选项的层次结构与依赖关系,Makefile 根据 .config 中的 CONFIG_XXX 值决定编译哪些源文件。
- 掌握
obj-$(CONFIG_XXX)条件编译模式是理解内核构建体系的关键。