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 声明到内核镜像产出的关键路径 linux-yocto.git 内核源码仓库 yocto-kernel-cache 内核元数据仓库 .patch 补丁文件 board / feature 补丁 .cfg / .scc 配置 内核配置片段 SRC_URI name=machine name=meta type=kmeta destsuffix=${KMETA} SRCREV_FORMAT do_fetch 下载源码与元数据 do_unpack 解压到工作目录 do_patch 应用 .patch 补丁 do_configure 合并 .cfg / .scc do_compile 编译内核与模块 内核产出 vmlinuz modules dtb 版本管理:PREFERRED_VERSION + SRCREV 锁定 PREFERRED_VERSION_linux-yocto = "6.6%" | SRCREV_machine / SRCREV_meta

掌握这张数据流图后,再回头看 linux-yocto 配方,就能清楚地知道每个变量和文件在构建链路中的位置。内核定制的本质,就是在 SRC_URI 这条主线上,正确地追加补丁、配置片段和版本约束,而不是随意修改基础配方。