2. 内核源码获取与配置:从kernel.org获取源码、使用git管理内核源码、内核配置系统简介(Kconfig/Makefile)

做嵌入式Linux开发,第一步就是拿到内核源码。这事儿听起来简单,但里面门道不少。我见过不少新手直接从网盘下载一个压缩包就开始搞,结果后面版本混乱、补丁打不上,折腾好几天才发现是源码版本不对。

这一章,咱们就把源码获取和配置系统这两个基本功打扎实。说白了,就是解决两个问题:源码从哪来,以及怎么告诉内核你要什么。

2.1 从kernel.org获取源码

kernel.org 是Linux内核的官方发布站点。所有主线版本、稳定版、长期支持版(LTS)都从这里发布。我个人习惯只从这上面下载,第三方渠道的源码我一般不碰——万一被人动了手脚,查都查不出来。

内核版本号有固定的命名规则,举个例子:

版本号 含义 适用场景
5.10.100 主线版本5.10的第100个稳定补丁 通用嵌入式系统
5.10.100-rt50 实时抢占(RT)补丁版本 工业控制、机器人
5.4.200 LTS长期支持版本 产品量产、车载系统
6.1.25 较新LTS版本 新硬件平台、功能需求多

下载方式有两种:

  • 直接下载压缩包:在 kernel.org 找到对应版本,点 .tar.xz 链接下载。解压后就能用。
  • 通过 git 克隆:这个我后面细说。推荐用这种方式,方便后续管理。
我的建议:做工业产品,优先选 LTS 版本。比如 5.10 或 6.1,社区支持周期长,安全补丁跟得上。别追新,稳定第一。

2.2 使用git管理内核源码

git 是内核社区的官方版本管理工具。Linus Torvalds 本人就是 git 的创造者,所以内核源码天然就是为 git 设计的。

为什么要用 git?我举个例子。有一次我在给一个工控板移植驱动,需要同时维护三个版本:一个是原厂 BSP 内核,一个是主线内核,还有一个是我自己裁剪后的版本。没有 git,光文件对比就能把人逼疯。

基本操作流程:

# 克隆主线仓库(只克隆最新版本,省时间)
git clone --depth=1 https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

# 进入目录
cd linux

# 查看当前版本
git describe --tags

# 切换到指定标签(比如某个稳定版)
git checkout v5.10.100

# 创建自己的开发分支
git checkout -b my_industrial_board_v1

这里有个关键点:--depth=1 是浅克隆,只下载最新一次提交。如果你不需要完整的提交历史,用这个能省大量时间和磁盘空间。我刚开始做嵌入式时不知道这个参数,每次克隆几十 GB 数据,等得心焦。

避坑指南:我曾经犯过一个错——直接在主线分支上改代码。后来社区发布了安全补丁,我想合并却发现冲突一大堆。正确的做法是:永远在你自己创建的分支上工作,主线分支只用来拉取上游更新。

如果你需要给内核打补丁,git 的 am 和 format-patch 命令是标配:

# 应用补丁
git am 0001-my-driver-fix.patch

# 生成补丁(从当前分支相对于主线的改动)
git format-patch origin/master --stdout > my_changes.patch

2.3 内核配置系统简介(Kconfig/Makefile)

内核配置系统,说白了就是一套菜单系统。你通过它告诉内核:我要哪些功能,不要哪些功能。背后是两个核心文件在起作用:Kconfig 和 Makefile。

2.3.1 Kconfig:定义配置选项

Kconfig 文件定义了配置项的结构、依赖关系和帮助信息。每个内核子系统、驱动模块都有自己的 Kconfig 文件,分散在各个目录下。

看一个实际例子——GPIO 子系统的 Kconfig 片段:

menu "GPIO Support"

config GPIOLIB
    bool "GPIO Library Support"
    help
      This enables support for the GPIO library.
      Say Y here if your board has GPIO pins.

config GPIO_SYSFS
    bool "GPIO sysfs interface"
    depends on GPIOLIB
    help
      Export GPIO control through sysfs.
      Most new systems should use the character device interface instead.

config GPIO_MY_DRIVER
    tristate "My Custom GPIO Driver"
    depends on GPIOLIB && ARM
    help
      Driver for custom GPIO controller on ARM platforms.
      This can be built as a module.
endmenu

这里有几个关键语法:

  • bool:要么编译进内核(Y),要么不编译(N)。
  • tristate:三种状态——编译进内核(Y)、编译成模块(M)、不编译(N)。
  • depends on:依赖关系。比如 GPIO_SYSFS 依赖 GPIOLIB,如果没选 GPIOLIB,这个选项根本不会出现。
  • help:帮助信息,配置时按 ? 键能看到。
注意:依赖关系是链式的。你选了一个选项,它依赖的父选项会自动被选中。但反过来不会——取消父选项,所有依赖它的子选项都会被强制取消。我在项目中遇到过因为取消了一个看似无关的选项,结果整个驱动子系统都灰掉了的情况。

2.3.2 Makefile:控制编译行为

Makefile 文件根据 Kconfig 的配置结果,决定哪些文件被编译、怎么编译。同样以 GPIO 为例:

# drivers/gpio/Makefile

obj-$(CONFIG_GPIOLIB)       += gpiolib.o
obj-$(CONFIG_GPIO_SYSFS)    += gpio-sysfs.o
obj-$(CONFIG_GPIO_MY_DRIVER) += gpio-my-driver.o

# 如果是模块,加上模块信息
gpio-my-driver-objs := gpio-my-driver-core.o gpio-my-driver-regs.o

这里的 obj-$(CONFIG_XXX) 是核心语法:

  • 如果 CONFIG_GPIOLIB=y,等价于 obj-y += gpiolib.o,文件被编译进内核。
  • 如果 CONFIG_GPIOLIB=m,等价于 obj-m += gpiolib.o,文件被编译成可加载模块(.ko)。
  • 如果 CONFIG_GPIOLIB 未定义或为 n,这一行被忽略。

你想想看,这套机制其实很巧妙。Kconfig 负责问你要不要,Makefile 负责根据你的回答去编译。两者通过 CONFIG_ 前缀的宏定义连接起来。

2.3.3 配置操作流程

实际配置内核时,你不需要手写 Kconfig 或 Makefile。内核提供了一系列配置工具:

# 最常用的菜单式配置
make menuconfig

# 基于当前配置的文本界面
make nconfig

# 使用默认配置(适合快速开始)
make defconfig

# 保存当前配置到文件
make savedefconfig

make menuconfig 是我用得最多的。界面里按 / 可以搜索配置项,按 ? 看帮助,按 空格 切换 Y/M/N。配置完成后,配置结果保存在内核源码根目录的 .config 文件中。

小技巧:如果你拿到一块新板子,先执行 make defconfig 生成默认配置,然后在此基础上 make menuconfig 微调。别从零开始配,那太累了。我刚开始做嵌入式时,试图手配一个最小内核,结果漏了根文件系统驱动,内核启动到一半就 panic 了。

2.4 本章知识结构图

下面这张图把源码获取、git 管理、配置系统三者的关系串起来了:

内核源码获取与配置知识结构 源码获取 kernel.org git clone Git 版本管理 分支管理 补丁管理 内核配置系统 Kconfig Makefile 核心流程: 1. 从 kernel.org 或 git 获取源码 → 2. 创建自己的开发分支 3. 执行 make menuconfig 配置内核 → 4. 配置结果写入 .config 文件 5. Kconfig 定义选项,Makefile 根据 .config 决定编译哪些文件

2.5 小结

这一章我们干了三件事:

  • 从 kernel.org 拿到了干净的内核源码。
  • 用 git 管理源码,创建了自己的开发分支,学会了打补丁。
  • 理解了 Kconfig 和 Makefile 是怎么配合工作的。