share state 机制¶
简要介绍share state(简写为sstate)原理与使用。
sstate 原理¶
yocto默认继承了sstate类,sstate机制将SSTATETASKS定义的任务指定的文件目录生成压缩包(缓存)以供后续复用。构建时存在sstate缓存且任务校验和没有发生改变,则会执行对应的task_setscene任务解压压缩包得到输出文件;如果相关任务校验和改变了,则需要重新获取源码进行编译。
校验和:每一个执行的任务都会存在一个校验和用于判断是否需要重新构建。基础hash(直接输入到任务的内容信息)与依赖任务的hash生成总的校验和,根据该值变化决定是否复用缓存。可以通过查看SSTATE_DIR目录下的siginfo文件名来查看校验和。
相关字节:
SSTATE_DIR:指向共享缓存所在目录,默认为"${TOPDIR}/sstate-cache";
SSTATE_MIRRORS:共享缓存镜像机制,可以从镜像指定位置实时获取缓存;
BB_HASHEXCLUDE_COMMON:一些不被计算到hash值的公共变量,参与组成其他字节;
BB_HASHBASE_WHITELIST:列出从校验和和相关性数据中排除的变量;由BB_HASHEXCLUDE_COMMON和其他值组成;
BB_HASHCONFIG_WHITELIST:列出从基本配置校验和中排除的变量;由BB_HASHEXCLUDE_COMMON和其他值组成;
BB_SIGNATURE_EXCLUDE_FLAGS:一些不被计算到hash值的公共变量标志,如果是任务标志,则 prefuncs、postfuncs、exports 等会对hash值有影响;具体要分析底层python脚本。
vardepsexclude与vardeps标志:使字节或任务计算校验和时不依赖或依赖对应变量。
BB_HASHEXCLUDE_COMMON是BB_HASHBASE_WHITELIST和BB_HASHCONFIG_WHITELIST的公共组成部分,因此可以修改BB_HASHEXCLUDE_COMMON对两者同时产生影响。
sstate 缓存复用机制¶
缓存指sstate-cache目录下的*.tgz文件,构建时存在对应的tgz文件且校验和不变时则执行对应的task_setsecne任务;sstate-cache目录下还存在*.siginfo文件,目前测试来看该文件只是提供了一种检测校验和的手段,即使不存在也不会使缓存不能使用;每个执行任务都存在一个siginfo文件。
Note
do_build任务不存在siginfo文件。
构建linux-openeuler(qemu-arm),总共执行了59个任务。通过查看日志信息,只执行了54个任务;查看SSTATE_DIR目录,存在54份siginfo文件;其余5个任务应该是每个依赖包的do_build任务,不存在相应的siginfo文件。
$ bitbake linux-openeuler -g //生成linux-openeuler依赖信息
$ cat pn-buildlist //查看依赖的包
以下是linux-openeuler(qemu-arm)及其所有依赖包的任务数与siginfo文件数:
linux-openeuler:22任务与siginfo文件;
pseudo-native:9任务与siginfo文件;
binutils:7任务与siginfo文件;
gcc-cross-arm:7任务与siginfo文件;
depmodwrapper:9任务与siginfo文件;
任务数与siginfo文件数一致且对应,符合每个执行任务都有一份对应的siginfo文件提供校验信息。
开发者自定义 sstate 任务方式¶
举例说明如何定义sstate任务。
SSTATETASKS += "do_test_sstate" //声明为sstate任务
do_test_sstate () {
// 开发者可自定义此测试任务
touch a.txt
ln -s ./a.txt ./b.txt
echo ${D}${includedir} >> b.txt
}
do_test_sstate[dirs] = "${WORKDIR}/test_in" //工作目录
do_test_sstate[sstate-inputdirs] = "${WORKDIR}/test_in" //缓存输入目录
do_test_sstate[sstate-outputdirs] = "${WORKDIR}/test_out" //拷贝缓存输入内容到此目录
# do_test_sstate[sstate-plaindirs] = "${WORKDIR}/test_in" //上述两目录相同时定义方式
addtask do_test_sstate after do_fetch before do_patch //声明任务执行顺序
python do_test_sstate_setscene () {
sstate_setscene(d) //复用缓存
}
addtask do_test_sstate_setscene //定义缓存复用任务,以task + _setscene命名
开发者可在poky查看更多sstate任务例子。
sstate 使用方法¶
此处推荐3种使用方法,如下:
1、拷贝上一次构建成功的sstate-cache目录到当前构建目录下;
$ cp -r buildA/sstate-cache buildB/sstate-cache //buildA为已成功构建的目录,buildB为刚刚完成初始化的构建目录
$ cd buildB
$ bitbake package_name //构建所需包
利用sstate_mirrors机制,可以做到根据需求获取缓存,首先把已成功构建的缓存放在公共服务器上。
2、开发者使用网络在线获取缓存;会受到网络因素影响,可能会出现随机性的错误,当前暂不使用;需在local.conf脚本添加如下内容:
# 服务器链接地址
SSTATE_MIRRORS ?= "file://.* http://someserver.tld/share/sstate/PATH;downloadfilename=PATH"
3、开发者下载缓存到本地路径;缺点是当开发者只需要某一部分缓存时也会把全部的缓存下载下来,造成资源浪费;需在local.conf脚本添加如下内容:
# 本地绝对路径
SSTATE_MIRRORS ?= "file://.* file:///some/local/dir/sstate/PATH"
sstate 使用效果¶
这里举几个简单例子说明缓存的效果,开发者可自行测试。
例1:使用 -c clean 将任务的输出文件删除,但不删除缓存;再重新构建,这个过程会特别迅速,这就是因为存在缓存。
$ bitbake pseudo-native //构建pseudo-native成功
$ bitbake pseudo-native -c clean
$ bitbake pseudo-native
例2:使用 -c cleansstate 会在 clean 的基础上把缓存也删掉。
$ bitbake pseudo-native //构建pseudo-native成功
$ bitbake pseudo-native -c cleansstate
$ bitbake pseudo-native
例3: 使用 –no-setscene 选项不使用缓存构建。
$ bitbake pseudo-native //构建pseudo-native成功
$ bitbake pseudo-native -c clean
$ bitbake pseudo-native --no-setscene //不使用缓存构建
sstate应用过程中解决的疑难问题¶
在初步复用sstate的过程中,总是频繁触发重新构建,最初定位是工具链缓存复用的问题,通过使用bitbake-diffsigs工具具体定位到是工具链gcc-runtime-external包构建时的do_install任务在postfuncs标志下的do_install_appended任务的校验和会随着构建目录改变而改变。 根因是gcc-runtime-external的一个匿名函数使用replace函数修改了do_install_appended的内容导致校验和会随构建目录改变而改变,当前只能让do_install任务的校验和不受do_install_appended影响以复用缓存。
开发者可能关心的问题¶
bitbake zlib -c configure
bitbake zlib -c populate_sysroot
这两个任务的差异是populate_sysroot为sstate任务,存在缓存,而configure任务非sstate任务,只存在siginfo文件;因此populate_sysroot任务不会重新构建,而configure会重新构建。
$ bitbake libpcre2
$ cd WORKDIR_of_libpcre2/temp //日志所在目录
$ cat log.task_order
do_package_qa_setscene (3924912): log.do_package_qa_setscene.3924912
do_populate_lic_setscene (3924914): log.do_populate_lic_setscene.3924914
do_package_write_rpm_setscene (3924913): log.do_package_write_rpm_setscene.3924913
do_populate_sysroot_setscene (3924915): log.do_populate_sysroot_setscene.3924915
do_packagedata_setscene (3925129): log.do_packagedata_setscene.3925129
do_deploy_source_date_epoch_setscene (3925173): log.do_deploy_source_date_epoch_setscene.3925173
do_fetch (3925748): log.do_fetch.3925748
do_unpack (3926514): log.do_unpack.3926514
do_patch (3927219): log.do_patch.3927219
do_prepare_recipe_sysroot (3970565): log.do_prepare_recipe_sysroot.3970565
do_configure (3970676): log.do_configure.3970676
do_compile (3979602): log.do_compile.3979602
do_install (3989915): log.do_install.3989915
do_package (3990522): log.do_package.3990522
结论是某些任务使用了缓存,但是package任务没有使用缓存,导致重新编译;经过一些测试发现需要依赖任务的populate_sysroot任务执行结束,再执行构建才不会重新构建。