3. 内核源码获取与结构分析:从 kernel.org 获取源码,理解顶层 Makefile、Kconfig 与目录树
好,咱们正式开始动手。这一章,我带你从零获取一份 Linux 内核源码,然后把它拆开看看骨架长什么样。很多初学者喜欢直接 apt-get install 一个内核头文件就开始写驱动,我个人不太建议这么做。你想想看,做嵌入式系统裁剪,连源码长啥样都不知道,后面怎么下手?
3.1 从 kernel.org 获取源码
获取源码的方式其实就两种:下载压缩包,或者用 git 克隆。我个人的习惯是——做产品开发用 git,做快速实验用压缩包。
3.1.1 下载压缩包
打开 https://www.kernel.org,你会看到几个大版本:Mainline、Stable、Longterm。做嵌入式产品,我强烈建议选 Longterm(长期支持)版本。为什么?因为你在一个版本上做裁剪、打补丁、调驱动,可能要用好几年。选 Mainline 的话,三个月升一次级,谁也受不了。
- 产品量产 → Longterm 版本(如 6.1.y、5.15.y)
- 学习实验 → 最新的 Stable 版本
- 开发板 BSP → 厂商提供的内核 + kernel.org 补丁
下载命令很简单:
# 以 Linux 6.1 LTS 为例
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.76.tar.xz
tar -xf linux-6.1.76.tar.xz
cd linux-6.1.76
3.1.2 使用 git 克隆
如果你要跟踪补丁、做版本管理,用 git 更合适。我曾经在一个项目里,因为没做 git 管理,裁剪完的内核源码改了一堆地方,最后想回退某个配置都找不到原始状态,那叫一个痛苦。
git clone --depth=1 \
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git \
-b linux-6.1.y
--depth=1 这个参数很实用,只拉取最新一次提交,省流量也省时间。完整历史有几十万次提交,你一般用不上。
https://mirrors.tuna.tsinghua.edu.cn/kernel/,速度不错。
3.2 顶层目录树:先看骨架
源码解压后,第一件事不是急着编译,而是先看看目录结构。我每次拿到一个新版本,都会先跑一遍 ls -l,心里有个数。
arch/ # 架构相关代码(ARM、x86、RISC-V 等)
block/ # 块设备层
crypto/ # 加密子系统
Documentation/ # 内核文档
drivers/ # 设备驱动(最大目录)
fs/ # 文件系统
include/ # 头文件
init/ # 内核初始化
ipc/ # 进程间通信
kernel/ # 核心内核代码
lib/ # 库函数
mm/ # 内存管理
net/ # 网络协议栈
scripts/ # 编译脚本、工具
security/ # 安全模块
sound/ # 音频子系统
tools/ # 用户态工具
usr/ # initramfs 相关
这里我重点说几个目录:
- arch/:嵌入式开发最常打交道的目录。你做 ARM 就进
arch/arm/,做 RISC-V 就进arch/riscv/。里面包含启动代码、内存布局、设备树等。 - drivers/:这个目录最大,没有之一。我见过有人裁剪内核时,光 drivers 目录就占了源码体积的 60% 以上。裁剪的核心工作之一,就是在这里删掉不需要的驱动。
- init/:内核启动的入口,
main.c里的start_kernel()函数就在这。调试启动流程时经常要翻这个目录。
include/ 下的全局头文件。我曾经为了省事,直接在 include/linux/sched.h 里加了一个宏,结果编译其他模块时各种冲突,排查了两天才发现是头文件污染。正确的做法是在自己的驱动或板级目录里定义。
3.3 顶层 Makefile:内核编译的总控
顶层 Makefile 是内核编译的入口。你执行 make 时,实际上就是在执行这个文件。它不复杂,但有几个关键变量你必须知道。
| 变量/目标 | 作用 | 我的用法 |
|---|---|---|
ARCH |
指定目标架构 | make ARCH=arm |
CROSS_COMPILE |
交叉编译工具链前缀 | make CROSS_COMPILE=arm-linux-gnueabihf- |
O= |
指定输出目录 | make O=build(强烈推荐) |
V=1 |
显示完整编译命令 | 调试编译错误时用 |
defconfig |
生成默认配置 | make ARCH=arm defconfig |
menuconfig |
图形化配置界面 | 最常用的配置方式 |
这里我特别想强调 O= 这个参数。很多新手直接在源码目录里编译,结果 make clean 都清不干净。我的习惯是:
mkdir build
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- \
O=build defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- \
O=build -j4
这样所有编译产物都在 build/ 目录里,源码目录干干净净。想换配置?直接删掉 build/ 重来就行。
3.4 Kconfig:内核配置的“菜单系统”
Kconfig 是内核配置系统的核心。你执行 make menuconfig 时看到的菜单树,就是由各个目录下的 Kconfig 文件定义的。
一个典型的 Kconfig 条目长这样:
config CONFIG_MY_DRIVER
bool "My custom driver support"
depends on ARM
select CONFIG_GPIOLIB
help
Say Y here to enable my custom driver.
This driver is used for ...
几个关键语法:
- bool/tristate:bool 表示编译进内核(Y/N),tristate 多一个编译成模块(M)的选项。
- depends on:依赖关系。比如某个驱动依赖 ARM 架构,在其他架构下就不显示。
- select:反向依赖。选中这个选项时,自动选中另一个选项。我提醒一句:
select要慎用,用不好会导致配置冲突。 - help:帮助信息,写清楚这个选项是干什么的。
make savedefconfig 生成最小配置。它会从当前 .config 里提取出非默认值的选项,生成一个极简的 defconfig 文件。这样你就能清楚地看到自己改了哪些配置。
3.5 知识结构图:源码获取与结构分析
下面这张图,我把本章的核心知识点串起来了。你可以把它当作一个思维导图来看。
3.6 动手实践:第一次配置与编译
光看不练不行。我建议你现在就动手做三件事:
- 下载一份源码,用
ls -l看看各个目录的大小,心里有个数。 - 执行
make ARCH=arm help,看看顶层 Makefile 支持哪些目标。你会发现有很多有用的目标,比如tags、cscope、headers_install等。 - 执行
make ARCH=arm menuconfig,进去随便逛逛。按/可以搜索配置项,按?可以看帮助。不用怕改坏,退出时选Exit不保存就行。