Yocto理论篇 | Yocto共享状态缓存

本文介绍OpenEmbedded构建系统中的增量构建原理及其实现。增量构建通过校验和检测任务输入变化,避免重复构建,提高效率。文章详细解释了如何通过共享状态代码追踪任务输出变更,以及如何利用这些信息加速构建过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一般OpenEmbedded构建系统从头开始构建一切,除非BitBake 可以确定部件不需要重新构建。从根本上说,从头构建是有吸引力的,因为它意味着所有部件都是全新构建的,不存在可能导致问题的过时数据。当开发人员遇到问题时,他们通常会默认从头开始构建,这样他们从一开始就有一个已知的状态。

从零开始构建镜像对这个过程来说既是一个优点,也是一个缺点。如前一段所述,从头开始构建可以确保一切都是最新的,并且从已知的状态开始。然而,从头开始建设也需要更长的时间,因为它通常意味着重建不一定需要重建的东西。

Yocto Project实现了支持增量构建的共享状态代码。共享状态代码的实现回答了以下问题,这些问题是开放式增量构建支持系统中的基本障碍:

  • 系统的哪些部分发生了变化,哪些部分没有发生变化?
  • 如何删除和替换已更改的软件?
  • 不需要从头开始重建的预构建组件在可用时如何使用?

对于第一个问题,构建系统通过创建任务输入的校验和(或签名)来检测给定任务的“inputs”中的更改。如果校验和发生变化,系统假设输入已更改,需要重新运行任务。对于第二个问题,共享状态(sstate)代码跟踪哪些任务向构建过程添加了哪些输出。这意味着可以删除、升级或以其他方式操作给定任务的输出。第三个问题的部分解决方案解决了第二个问题,假设构建系统可以从远程位置获取sstate对象,并在认为它们是有效的情况下安装它们。

注意:

  • 构建系统不会将PR 信息作为共享状态包的一部分进行维护。因此,存在影响维护共享状态提要的考虑因素。
  • 生成系统中支持增量生成的代码不是简单的代码。

下面将详细介绍整个增量构建体系结构、校验和(签名)以及共享状态。

1 总体架构

在确定需要构建系统的哪些部分时,BitBake是基于每个任务而不是按配方工作的。您可能会想知道为什么使用每个任务比每个配方优先级更高。为了帮助解释,考虑启用IPK打包后端,然后切换到DEB。在这种情况下,do_install 和do_package 任务输出仍然有效。但是,使用按配方的方法,构建将不包括.deb文件。因此,您必须使整个构建无效并重新运行它。重新运行所有内容并不是最好的解决方案。而且,在这种情况下,核心是必须“taught”很多关于具体任务的知识。这种方法不能很好地扩展,并且不允许用户在不接触打包的登台核心的情况下轻松地在层中添加新任务或作为外部配方。


2 校验和(签名)

共享状态代码使用校验和(任务输入的唯一签名)来确定任务是否需要再次运行。因为任务输入的更改会触发重新运行,因此流程需要检测给定任务的所有输入。对于shell任务,这是相当容易的,因为构建过程为每个任务生成一个“run”shell脚本,并且可以创建一个校验和,可以很好地了解任务的数据何时更改。

使问题复杂化的是,有些东西不应该包括在校验和中。首先,有一个给定任务的实际特定构建路径—WORKDIR。工作目录是否更改并不重要,因为它不应影响目标包的输出。此外,构建过程的目标是使本机包或跨包可重定位。

注意:本机包和交叉包都在构建主机上运行。但是,跨包为目标体系结构生成输出。因此校验和需要排除WORKDIR。排除工作目录的简单方法是将WORKDIR设置为某个固定值,并为“run”脚本创建校验和。另一个问题来自“run”脚本,这些脚本包含可能被调用或可能未被调用的函数。增量构建解决方案包含用于计算shell函数之间依赖关系的代码。这段代码用于将“run”脚本缩减到最小值集,从而缓解了这个问题,并使“run”脚本更具可读性。

到目前为止,shell脚本的解决方案已经存在。Python任务呢?即使这些任务比较困难,也可以采用同样的方法。这个过程需要弄清楚Python函数访问的变量以及它调用的函数。同样,增量构建解决方案包含的代码首先确定变量和函数的依赖关系,然后为用作任务输入的数据创建校验和。

WORKDIR 的例子一样,存在依赖关系应该被忽略的情况。对于这些情况,可以使用如下行指示生成过程忽略依赖项:

PACKAGE_ARCHS[vardepsexclude] = "MACHINE"           

此示例确保PACKAGE_ARCHS 变量不依赖于MACHINE的值,即使它引用了MACHINE的值。

同样,在某些情况下,您需要添加BitBake无法找到的依赖项。您可以使用以下行来完成此操作:

PACKAGE_ARCHS[vardeps] = "MACHINE"

这个例子显式地添加MACHINE 变量作为PACKAGE_ARCHS的依赖项。

作为一个例子,考虑一个内嵌Python的例子,BitBake无法找出依赖关系。在调试模式下运行(即使用-DDD)时,BitBake会在发现无法确定依赖关系的内容时生成输出。

到目前为止,仅讨论对任务的直接投入。基于直接输入的信息在代码中称为“basehash”。但是,任务的间接输入问题仍然存在——已经构建并存在于Build Directory中的项目。特定任务的校验和(或签名)需要添加特定任务所依赖的所有任务的哈希值。选择要添加的依赖项是一个策略决策。然而,其效果是生成一个主校验和,它结合了basehash和任务依赖项的散列。

在代码级别上,存在多种方法可以影响basehash和依赖任务哈希。在BitBake配置文件中,可以为BitBake提供一些额外的信息,以帮助它构造basehash。下面的语句有效地生成了一个全局变量依赖排除列表(即变量从未包含在任何校验和中):

BB_HASHBASE_WHITELIST ?= "TMPDIR FILE PATH PWD BB_TASKHASH BBPATH DL_DIR \
     SSTATE_DIR THISDIR FILESEXTRAPATHS FILE_DIRNAME HOME LOGNAME SHELL TERM \
     USER FILESPATH STAGING_DIR_HOST STAGING_DIR_TARGET COREBASE PRSERV_HOST \
     PRSERV_DUMPDIR PRSERV_DUMPFILE PRSERV_LOCKDOWN PARALLEL_MAKE \
     CCACHE_DIR EXTERNAL_TOOLCHAIN CCACHE CCACHE_DISABLE LICENSE_PATH SDKPKGSUFFIX"

前面的例子排除了WORKDIR ,因为该变量实际上是作为TMPDIR中的路径构造的,TMPDIR位于白名单中。

决定通过依赖链包含哪些依赖任务哈希的规则更为复杂,通常使用Python函数来完成。meta/lib/oe/sstatesig.py显示了两个示例,并说明了如何在需要时将自己的策略插入到系统中。这个文件定义了OE-Core使用的两个基本签名生成器:“OEBasic”和“OEBasicHash”。默认情况下,BitBake中启用了一个虚拟的“noop”签名处理程序。这意味着行为与以前的版本没有变化。默认情况下,OE-Core通过中的此设置使用“OEBasicHash”签名处理程序bitbake.conf文件:

BB_SIGNATURE_HANDLER ?= "OEBasicHash" 

“OEBasicHash” BB_SIGNATURE_HANDLER 与 “OEBasic” 版本相同,但将任务哈希添加到stamp文件中。这将导致更改任务哈希的任何元数据更改,从而自动导致任务再次运行。这就消除了对PR 值的需要,对元数据的更改会自动在构建中产生涟漪。

还值得注意的是,这些签名生成器的最终结果是使一些依赖项和哈希信息可用于构建。这些信息包括:

  • BB_BASEHASH_task-taskname:配方中每个任务的基本哈希。
  • BB_BASEHASH_filename:taskname:每个依赖任务的基哈希。
  • BBHASHDEPS_filename:taskname:每个任务的任务相关性。
  • BB_TASKHASH:当前正在运行的任务的哈希值。

3 共享状态

解决前面讨论的共享状态和半个状态的校验和问题。问题的另一半是能够在构建期间使用校验和信息,以及能够重用或重建特定组件。

sstate 类是如何“capture”给定任务的快照的相对通用的实现。其思想是构建过程不关心任务输出的来源。输出可以是新生成的,也可以从某个地方下载和解包。换句话说,构建过程不需要担心它的起源。

存在两种类型的输出。一种是在WORKDIR中创建一个目录。一个很好的例子是do_install 或do_package的输出。另一种类型的输出发生在将一组数据合并到共享目录树(如sysroot)中时。

Yocto项目团队试图将实现的细节隐藏在sstate类中。从用户的角度来看,将共享状态包装添加到任务中非常简单,这是一个取自deploy 类的do_deploy 示例:

DEPLOYDIR = "${WORKDIR}/deploy-${PN}"
SSTATETASKS += "do_deploy"
do_deploy[sstate-inputdirs] = "${DEPLOYDIR}"
do_deploy[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}"

python do_deploy_setscene () {
    sstate_setscene(d)
}
addtask do_deploy_setscene
do_deploy[dirs] = "${DEPLOYDIR} ${B}"
do_deploy[stamp-extra-info] = "${MACHINE_ARCH}"       

下表说明了前面的示例:

  • SSTATETASKS 中添加“do_deploy”会在do_deploy 任务之前和之后添加一些必需的sstate相关处理(在sstate 类中实现)。
  • do_deploy[sstate-inputdirs] = "${DEPLOYDIR}"声明当正常运行时(即不使用sstate缓存时),do_deploy将其输出放在${DEPLOYDIR}中。此输出将成为共享状态缓存的输入。
  • do_deploy[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}"这一行将共享状态缓存的内容复制到${DEPLOY_DIR_IMAGE}

注意:如果do_deploy不在共享状态缓存中,或者如果它的输入校验和(签名)与缓存输出时不同,那么任务将运行以填充共享状态缓存,然后共享状态缓存的内容将复制到${DEPLOY_DIR_IMAGE}。如果do_deploy在共享状态缓存中,并且其签名指示缓存的输出仍然有效(即如果没有相关的任务输入发生更改),则共享状态缓存的内容将直接由do_deploy_setscene 任务复制到${DEPLOY_DIR_IMAGE},而跳过do_deploy 任务。

  • 以下任务定义是使先前设置生效所需的粘合逻辑:
python do_deploy_setscene () {
     sstate_setscene(d)
}
addtask do_deploy_setscene           

sstate_setscene()将上述标志作为输入,并通过共享状态缓存加速do_deploy 任务(如果可能)。如果任务被加速,sstate_setscene()将返回True。否则,它将返回False,并运行正常的do_deploy 任务。

  • do_deploy[dirs] = "${DEPLOYDIR} ${B}"这一行在do_deploy 任务运行之前创建${DEPLOYDIR}${B},并且还将do_deploy 的当前工作目录设置为${B}

注意:在sstate-inputdirssstate-outputdirs相同的情况下,可以使用sstate-plaindirs。例如,要保留do_package 任务的${PKGD}${PKGDEST}输出,请使用以下命令:

  • do_deploy[stamp-extra-info] = "${MACHINE_ARCH}"这一行将额外的元数据附加到stamp 文件中。在这种情况下,元数据使任务特定于机器的体系结构。
  • sstate-inputdirssstate-outputdirs也可以用于多个目录。例如,以下命令将PKGDESTWORK 和SHLIBWORK 声明为共享状态输入目录(填充共享状态缓存),将PKGDATA_DIR 和SHLIBSDIR 声明为相应的共享状态输出目录:
do_package[sstate-inputdirs] = "${PKGDESTWORK} ${SHLIBSWORKDIR}"
do_package[sstate-outputdirs] = "${PKGDATA_DIR} ${SHLIBSDIR}"         
  • 这些方法还包括在操作共享状态目录结构时获取锁文件的能力,以防文件的添加或删除是敏感的:
do_package[sstate-lockfile] = "${PACKAGELOCK}"

在后台,共享状态代码通过在SSTATE_DIR 和SSTATE_MIRRORS 中查找共享状态文件来工作。下面是一个例子:

SSTATE_MIRRORS ?= "\
file://.* http://someserver.tld/share/sstate/PATH;downloadfilename=PATH \n \
file://.* file:///some/local/dir/sstate/PATH"

注意:共享状态目录(SSTATE_DIR)被组织成两个字符的子目录,其中子目录名基于哈希的前两个字符。如果镜像的共享状态目录结构与SSTATE_DIR具有相同的结构,则必须在URI中指定“PATH”以使生成系统能够映射到相应的子目录。共享状态包的有效性可以通过查看文件名来检测,因为文件名包含本节前面描述的任务校验和(或签名)。如果找到有效的共享状态包,生成过程将下载它并使用它来加速任务。构建过程在任务加速阶段使用*_setscene任务。BitBake在主执行代码之前经过这个阶段,并尝试加速它可以找到共享状态包的任何任务。如果任务的共享状态包可用,则使用共享状态包。这意味着任务及其依赖的任何任务都不会执行。

作为一个真实的例子,其目的是在构建基于IPK的映像时,只有do_package_write_ipk 任务才会获取和提取它们的共享状态包。因为没有使用sysroot,所以它永远不会被提取。这也是为什么基于任务的方法比基于配方的方法更受欢迎的另一个原因,后者必须安装每个任务的输出。

<think>好的,我现在需要回答用户关于Yocto项目的简易方法或非官方技巧的问题。用户提到了他们想要寻找一些非正式或简易的方法,可能是指那些不在官方文档中明确说明,但在实际使用中常用的技巧。首先,我需要回顾一下用户提供的引用内容,看看是否有相关的信息可以利用。 根据引用[1],Yocto项目的核心是BitBake和OpenEmbedded-Core,这两个组件负责构建引擎和核心配方。引用[2]提到了如何处理软件包的编译和安装任务,以及如何将软件添加到最终的rootfs中。引用[4]讨论了配方之间的成果物共享机制,特别是通过do_prepare_recipe_sysroot和do_populate_sysroot任务。这些信息可能对解答用户的问题有帮助。 接下来,我需要考虑用户可能需要的简易方法或非官方技巧。通常,这类技巧包括快速构建配置、利用现有层(layers)、调试技巧、配方编写捷径等。例如,用户可能想知道如何加速构建过程,或者如何快速添加自定义软件包而不需要从头编写复杂的配方。 首先,快速构建方面,Yocto用户通常通过调整并行编译选项(如BB_NUMBER_THREADS和PARALLEL_MAKE)来加快构建速度。另外,使用SSTATE_MIRRORS可以重用之前构建的缓存,避免重复编译,这也是一个常见的优化方法。 其次,关于添加软件到rootfs,除了通过官方的方法编写配方,用户可能可以使用packagegroup来快速打包一组软件,或者直接在local.conf中添加IMAGE_INSTALL_append来包含所需的软件包,而无需修改配方文件。这在测试阶段非常方便。 调试方面,使用devshell命令可以直接进入配方的工作目录,方便手动执行命令或检查构建环境。此外,使用bitbake -e命令可以查看所有变量的值,帮助定位配置问题。 另外,用户可能对如何快速创建新层(layer)感兴趣,使用yocto-layer create命令可以自动生成层的骨架结构,省去手动创建的麻烦。 还有,引用[4]提到的共享机制,用户可能需要知道如何让配方B使用配方A的成果物,比如头文件和库文件。了解如何正确使用DEPENDS变量和声明依赖关系是关键,这样可以确保构建顺序正确,并且必要的文件在sysroot中可用。 可能还需要提到一些实用脚本或工具,例如使用oe-pkgdata-util查找包所属的配方,或者使用devtool工具来快速修改和测试配方,这些都能提升开发效率。 最后,需要注意用户可能遇到的常见问题,比如如何处理许可证冲突,如何清理构建缓存,或者如何跳过某些耗时的任务(如运行测试)。这些都是实际使用中的经验之谈,属于非官方但实用的技巧。 总结一下,我需要将这些点组织成结构清晰的回答,涵盖构建优化、快速配置、调试技巧、层管理等方面,并结合用户提供的引用内容,确保回答的准确性和实用性。同时,生成的相关问题需要围绕这些技巧展开,帮助用户进一步探索相关主题。</think>以下是关于Yocto项目的简易方法与非官方技巧的总结,结合实际开发经验整理: --- ### 一、快速构建与配置优化 1. **并行编译加速** 在`local.conf`中设置并行编译参数,充分利用多核CPU: ```bash BB_NUMBER_THREADS = "8" # BitBake任务并行数 PARALLEL_MAKE = "-j 8" # Make命令并行编译线程数 ``` 2. **利用sstate缓存** 通过共享`sstate-cache`目录,跨项目复用已编译的中间成果物,减少重复编译时间: ```bash SSTATE_MIRRORS = "file://.* http://sstate-mirror.example.com/2.6/PATH;downloadfilename=PATH" ``` --- ### 二、快速添加软件包到rootfs 1. **直接追加软件包** 在`local.conf`中通过`IMAGE_INSTALL_append`快速添加软件包(无需修改配方): ```bash IMAGE_INSTALL_append = " nano htop my-custom-app" ``` 此方法适用于临时测试或简单场景[^2]。 2. **使用packagegroup简化依赖** 创建`packagegroup`配方(如`packagegroup-custom.bb`),将多个软件包组合成一个逻辑组: ```bash SUMMARY = "Custom package group" LICENSE = "MIT" inherit packagegroup RDEPENDS_${PN} = "package1 package2 package3" ``` --- ### 三、调试与开发技巧 1. **快速进入开发环境** 使用`devshell`命令直接进入配方的工作目录: ```bash bitbake -c devshell <recipe-name> ``` 可在其中手动执行编译命令或检查环境变量。 2. **查看变量值** 通过`bitbake -e`命令导出所有变量(含继承关系),便于调试: ```bash bitbake -e <recipe-name> | grep ^TARGET_CC= # 查看编译器路径 ``` 3. **跳过特定任务** 强制跳过已完成的步骤(如解压源码): ```bash bitbake <recipe-name> -c compile -f # 强制重新编译 ``` --- ### 四、配方编写捷径 1. **自动生成配方骨架** 使用`devtool`快速创建新配方: ```bash devtool add <recipe-name> <source-url> # 自动分析源码生成.bb文件 ``` 2. **复用现有配置** 通过`inherit`关键字继承通用功能(如`cmake`或`autotools`),减少重复代码: ```bash inherit pkgconfig cmake # 自动处理CMake工程配置 ``` --- ### 五、层(Layer)管理技巧 1. **快速创建新层** 使用`yocto-layer`工具生成层模板: ```bash yocto-layer create mylayer # 自动生成conf/layer.conf及目录结构 ``` 2. **动态加载层** 在`bblayers.conf`中通过`BBLAYERS`动态添加/移除层,无需重启构建环境。 --- ### 六、成果物共享机制 对于配方间的依赖(如头文件、库文件),Yocto通过以下步骤实现共享: 1. 配方A声明`do_populate_sysroot`任务,将成果物发布到sysroot[^4]。 2. 配方B在`DEPENDS`中添加配方A,通过`do_prepare_recipe_sysroot`从sysroot获取依赖文件。 ```bash DEPENDS = "recipe-a" # 确保配方A先构建 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值