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 这个参数很实用,只拉取最新一次提交,省流量也省时间。完整历史有几十万次提交,你一般用不上。

小技巧: 国内访问 kernel.org 有时比较慢,可以找官方镜像。我个人常用 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 知识结构图:源码获取与结构分析

下面这张图,我把本章的核心知识点串起来了。你可以把它当作一个思维导图来看。

内核源码获取与结构分析知识图谱 内核源码获取与结构分析 源码获取 压缩包下载 (wget) Git 克隆 (git clone) 版本选择:Longterm 目录树结构 arch/ (架构相关) drivers/ (设备驱动) init/ (内核初始化) include/ (头文件) 配置与编译 顶层 Makefile Kconfig 配置系统 menuconfig 图形化 核心思路:先获取源码 → 理解目录结构 → 掌握 Makefile 和 Kconfig → 为后续裁剪做准备 嵌入式裁剪的关键在于知道“哪些目录可以删,哪些配置可以关” 实践1:下载并解压源码 熟悉目录结构 实践2:执行 make menuconfig 浏览 Kconfig 菜单树 实践3:查看顶层 Makefile 理解 ARCH/CROSS_COMPILE

3.6 动手实践:第一次配置与编译

光看不练不行。我建议你现在就动手做三件事:

  1. 下载一份源码,用 ls -l 看看各个目录的大小,心里有个数。
  2. 执行 make ARCH=arm help,看看顶层 Makefile 支持哪些目标。你会发现有很多有用的目标,比如 tags、cscope、headers_install 等。
  3. 执行 make ARCH=arm menuconfig,进去随便逛逛。按 / 可以搜索配置项,按 ? 可以看帮助。不用怕改坏,退出时选 Exit 不保存就行。
我的建议: 第一次接触内核源码,不用急着理解每一行代码。先建立“地图感”——知道什么功能在哪个目录,什么配置在哪个 Kconfig 文件里。后面裁剪和调试时,你才能快速定位问题。