3. 内核配方结构
在 Yocto 项目中,内核并不是“直接下载源码然后编译”这么简单,而是通过一个标准的 BitBake 配方(recipe)来管理。这个配方通常命名为 linux-yocto_<version>.bb,它定义了内核源码从哪里来、使用哪个版本、应用哪些补丁、采用哪份配置,以及最终如何打包成内核镜像和模块。理解 linux-yocto 配方结构,是进行内核定制与补丁管理的第一步。
一、linux-yocto 配方的整体结构
linux-yocto 配方并不是一个孤立的文件,而是一组按版本和功能拆分的 .bb 与 .inc 文件。典型结构如下:
meta/recipes-kernel/linux/
├── linux-yocto_6.6.bb
├── linux-yocto_6.1.bb
├── linux-yocto-dev.bb
├── linux-yocto.inc
├── linux-yocto-rt_6.6.bb
├── linux-yocto-tiny_6.6.bb
└── linux-yocto/
├── linux-yocto_6.6.bbappend
└── linux-yocto_6.6.bbappend
其中,linux-yocto.inc 是公共逻辑,包含内核编译类、配置片段处理、模块打包等通用设置;linux-yocto_6.6.bb 则负责指定具体版本、源码仓库、分支和提交号。这种“公共 inc + 版本 bb”的拆分方式,使得同一套构建逻辑可以复用于多个内核版本。
核心要点:内核配方本质上是一个 BitBake 配方,它继承 kernel.bbclass 和 kernel-yocto.bbclass,从而获得内核特有的任务链:do_fetch → do_unpack → do_patch → do_configure → do_compile → do_install → do_package。
二、内核源码获取方式
Yocto 中内核源码的获取方式主要有三种,它们都通过 SRC_URI 变量声明:
- Git 仓库获取:最常见的方式,直接指定
git://协议、仓库地址、分支和提交号。适合需要跟踪上游或使用自定义内核仓库的场景。 - 本地源码获取:使用
file://指向本地目录或压缩包,适合离线开发或内部维护的内核源码。 - tarball 获取:使用
https://或http://下载官方发布的源码压缩包,适合稳定版本、不需要频繁改动的场景。
以 linux-yocto_6.6.bb 为例,其源码获取部分通常如下:
SRC_URI = "git://git.yoctoproject.org/linux-yocto.git;name=machine;branch=v6.6/standard/base \
git://git.yoctoproject.org/yocto-kernel-cache;type=kmeta;name=meta;branch=yocto-6.6;destsuffix=${KMETA} \
file://defconfig \
"
这里出现了两个 Git 仓库:一个是内核源码仓库 linux-yocto.git,另一个是内核元数据仓库 yocto-kernel-cache。后者用于存放配置片段、内核特性描述文件(.scc)和补丁,是 Yocto 内核定制的关键。
三、SRC_URI 的组成与语义
SRC_URI 是内核配方中最核心的变量之一,它决定了“源码从哪里来”和“补丁如何应用”。其常见参数如下表所示:
| 参数 | 含义 | 示例 |
|---|---|---|
name | 为源码源命名,便于在 SRCREV 中分别指定提交号 | name=machine |
branch | 指定 Git 分支 | branch=v6.6/standard/base |
type | 指定源码类型,kmeta 表示内核元数据 | type=kmeta |
destsuffix | 指定解压后的目标目录 | destsuffix=${KMETA} |
subdir | 指定源码解压到工作目录的子目录 | subdir=linux |
apply | 控制补丁是否应用,apply=no 表示仅下载不应用 | apply=no |
与 SRC_URI 配套使用的是 SRCREV,用于锁定具体提交号,保证构建可复现:
SRCREV_machine ?= "a1b2c3d4e5f6..."
SRCREV_meta ?= "f6e5d4c3b2a1..."
SRCREV_FORMAT = "machine_meta"
其中 SRCREV_FORMAT 必须与 SRC_URI 中的 name 对应,否则 BitBake 无法正确解析多个源码源的版本。
四、内核版本管理机制
Yocto 的内核版本管理遵循“配方名即版本”的原则。linux-yocto_6.6.bb 表示 6.6 系列内核,linux-yocto_6.1.bb 表示 6.1 系列。当需要切换内核版本时,通常不是修改现有配方,而是通过 PREFERRED_PROVIDER 和 PREFERRED_VERSION 在 local.conf 或发行版配置中指定:
PREFERRED_PROVIDER_virtual/kernel = "linux-yocto"
PREFERRED_VERSION_linux-yocto = "6.6%"
此外,LINUX_VERSION 变量用于声明实际内核版本号,PV 则通常由配方文件名自动推导。对于需要精确控制版本的项目,建议同时锁定 SRCREV 和 LINUX_VERSION,避免上游分支变动导致构建结果漂移。
实践建议:在 bbappend 中覆盖 SRC_URI 时,务必使用 :append 或 :prepend 而不是直接赋值,否则会丢失基础配方中的内核元数据仓库和配置片段。
五、典型场景与常见误区
假设我们需要为一块自定义开发板添加内核补丁,标准做法是在自己的 layer 中创建 linux-yocto_6.6.bbappend:
FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"
SRC_URI += "file://0001-my-board-support.patch \
file://my-board.cfg \
file://my-board.scc \
"
其中 .patch 文件会被 do_patch 自动应用,.cfg 和 .scc 文件则被内核配置系统识别,用于合并配置片段和启用内核特性。
常见误区:很多初学者直接在 SRC_URI 中写死一个 Git 提交号,却忘记同步更新 SRCREV_FORMAT,导致 BitBake 报“Multiple SRCREV”错误;或者在 bbappend 中直接覆盖 SRC_URI,结果内核元数据仓库丢失,配置片段无法生效。正确做法是始终使用追加方式,并保持 name 与 SRCREV_FORMAT 一致。
下面这张图概括了 linux-yocto 配方从源码获取到内核产出的核心数据流,帮助你在排查问题时快速定位是源码、补丁、配置还是版本管理环节出了偏差。
掌握这张数据流图后,再回头看 linux-yocto 配方,就能清楚地知道每个变量和文件在构建链路中的位置。内核定制的本质,就是在 SRC_URI 这条主线上,正确地追加补丁、配置片段和版本约束,而不是随意修改基础配方。