LWN: 使用TuxMake来确保kernel编译结果可重现!-程序员宅基地

技术标签: python  java  linux  编程语言  docker  

关注了就能看到更多这么棒的文章哦~

Portable and reproducible kernel builds with TuxMake

January 5, 2021

This article was contributed by Dan Rue
DeepL assisted translation
https://lwn.net/Articles/841624/

Linaro 公司有一个名为 TuxMake 的开源项目,始于 2020 年 5 月,目标是使构建 Linux 内核的过程变得更容易。它提供了命令行界面以及一个 Python 库,还有一套精心准备的可移植的基于 container image 形式发布的构建环境。通过 TuxMake,开发者可以针对已经支持的 target architecture、toolchain、kernel configuration 和 make target 的任意组合来编译生成目标文件。

构建一个 Linux 内核并不困难。按照文档安装好依赖的 package,并运行几个 make 命令就行。然而,如果一个开发者想要为多个架构、多个 toolchain 来构建内核,就会变得越来越复杂起来。大多数开发者和维护者都有一套自己编写和维护的定制脚本,来完成他们所需的编译工作。TuxMake 提供了一个通用的抽象层,从而可以每个开发者可以减少自己编写的构建脚本。

TuxMake 为 toolchain/architecture 的各种组合发布了不同的 container。这些 container 可以使得开发人员自己不需要在他们的系统上来安装多种不同的 toolchain,甚至安装同一个 toolchain 的不同版本。它也使得这个编译构建的环境是 reproducible 并且 portable 的(可复制且可移植的),毕竟采用这种解决方案之后,构建内核的开发环境就被按照版本来完善地记录管理好的,而且可以在互联网和邮件列表中互相共享。

TuxMake 有两个目标。首先,把那些导致开发者(尤其是新开发者)不愿意针对一些不常见的 toolchain/architecture 组合进行编译测试的那些有阻碍的因素解决掉;第二,让编译过程和其中出现的问题可以更容易地被描述和重现出来。

Features

已经支持的架构有 arc, arm, arm64, i386, mips, parisc, powerpc, riscv, s390, sh, sparc, 和 x86_64。已经获得支持的工具链有 GCC 8、9 和 10 版本,Clang 10、11 和 nightly (最新) 版本。针对这些组合,都支持生成 kernel 的配置(Kconfig)、内核映像文件、modules、device tree binaries (DTBs) ,以及 debug kernel image。TuxMake 团队计划后续能生成更多产物,比如生成 kselftest、cpupower、perf,甚至还有文档。

支持使用符合 Open Container Initiative(OCI)规范的 container 和 container runtime,以实现编译过程的 portability 和 reproducibility。Docker 和 Podman runtime 都支持,并且可以互相交换支持,完全取决于每个用户自己的偏好。Podman 是 Docker 的一个比较流行的替代品,因为它不不需要守护进程,不需要 root 权限。今后还可以根据需要添加额外的 container runtime。

How does it work?

在使用 TuxMake 的 Linux 系统终端中,你可以先来到一个你通常会直接运行 make 的 Linux 内核源代码目录,然后运行 tuxmake 。没有任何参数的话,tuxmake 会对所有选项使用默认值来执行编译。看起来会像下面这样:

$ tuxmake
    # to reproduce this build locally: tuxmake --target-arch=x86_64 \
    #    --kconfig=defconfig --toolchain=gcc --wrapper=none --runtime=null \
    #             config kernel xipkernel debugkernel modules dtbs

首先,会打印出 tuxmake 生成的命令,包括所有提供的参数。这些信息对于后续重新复现这个编译过程以及跟同事讨论的时候很有用处。

make --silent --keep-going --jobs=16 O=/home/drue/.cache/tuxmake/builds/676/tmp defconfig

这里就会使用 defconfig 来建立一个 .config。请注意,默认情况下,会保存在 ~/.cache/tuxmake 下自动创建的一个目录中。所有的中间文件和编译产物都会保存在那里。

make --silent --keep-going --jobs=16 O=/home/drue/.cache/tuxmake/builds/676/tmp
make --silent --keep-going --jobs=16 O=/home/drue/.cache/tuxmake/builds/676/tmp bzImage

这样就会生成缺省内核以及 bzImage。由于第一个 make 调用已经完成了生成 bzImage 的工作,因此为 x86_64 明确再指定要生成 bzImage 似乎是多余的步骤。事实上,并不是所有的架构都能同样这样处理的,因此 tuxmake 并没有对这些架构进行特别处理,也没有在内核代码中加入对这些怪癖的支持,而是专门明确地指定最终要生成什么样的映像文件。大多数情况下,这一步其实是不需要的。

make --silent --keep-going --jobs=16 O=/home/drue/.cache/tuxmake/builds/676/tmp vmlinux
xz -T0 --keep /home/drue/.cache/tuxmake/builds/676/tmp/vmlinux

这两个命令会生成用于调试的 kernel image,保存到 output 目录使用 xz 压缩一下。就像之前一步一样,make vmlinux 可能看起来是多余的,因为 vmlinux 已经由 make 生成了。然而,debug kernel image 也可以单独生成。在一个完整的编译过程中,make vmlinux 将是一个不需要的操作,但如果只希望编译生成 debug kernel,那么它就会是主要的编译步骤。

grep -q CONFIG_MODULES=y /home/drue/.cache/tuxmake/builds/676/tmp/.config
make --silent --keep-going --jobs=16 \
     O=/home/drue/.cache/tuxmake/builds/676/tmp modules_install INSTALL_MOD_STRIP=1 \
     INSTALL_MOD_PATH=/home/drue/.cache/tuxmake/builds/676/tmp/modinstall
tar -caf /home/drue/.cache/tuxmake/builds/676/tmp/modules.tar.xz \
    -C /home/drue/.cache/tuxmake/builds/676/tmp/modinstall lib

如果在 build config 中启用了 kernel module,那么就会需要编译生成 module 文件,并使用 modules_install 来收集在一起,存放在 output 目录的 tar.xz 文件中。

I: config: PASS in 0:00:01.305649
I: kernel: PASS in 0:01:31.887716
I: debugkernel: PASS in 0:00:08.207033
I: modules: PASS in 0:00:00.869124
I: build output in /home/drue/.cache/tuxmake/builds/676

最后,显示每个 target 的构建状态(PASS/FAIL/SKIP)和构建时间,以及 output 目录的路径。

container 并不是必需的,缺省情况下也就没有使用。在没有使用 container runtime 的情况下,TuxMake 运行时会使用本地可用的 toolchain。而如果指定了 container runtime 的话,TuxMake 将在构建时先下载 container image(如果之前没有下载过的话),并在 container 之内来进行构建。它会将 Linux 源代码目录和 output 目录都 mount 到 container 之内,并在一个临时的 container 中逐步执行构建工作。这个 container 只在构建期间运行,完成后就会退出。

下面是一个更详细的例子,包括了上面说的这些所有步骤。这里会利用 Podman 来采用 Clang 编译生成一个 arm64 kernel,并打开 KASAN:

$ tuxmake -r podman -a arm64 -t clang-11 -k defconfig -K CONFIG_KASAN=y -w ccache
          # to reproduce this build locally: tuxmake --target-arch=arm64 --kconfig=defconfig \
          #   --kconfig-add=CONFIG_KASAN=y --toolchain=clang-11 --wrapper=ccache \
          #                --runtime=podman --image=tuxmake/arm64_clang-11 \
          #                config kernel xipkernel debugkernel modules dtbs

这里指定了使用 podman 作为 runtime,因此 TuxMake 将使用 Podman 来执行构建。内核将采用 Clang 11 版本 (-t clang-11) 来编译生成 aarch64 (-a arm64) 的目标。内核的配置是使用 defconfig target 来生成的,然后显式地打开 KASAN。并且,启用了 ccache (-w ccache) 来减少构建时间。

Trying to pull docker.io/tuxmake/arm64_clang-11...

此前如果没有下载过的话,这里会从 TuxMake 的公共 container registry 也就是 hub.docker.com/u/tuxmake 中来 pull arm64_clang-11 的 container。

# CONFIG_KASAN=y -> /home/drue/.cache/tuxmake/builds/685/tmp/0.config
make --silent --keep-going --jobs=16 O=/home/drue/.cache/tuxmake/builds/685/tmp \
     ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
     'HOSTCC=ccache clang' 'CC=ccache clang' defconfig
scripts/kconfig/merge_config.sh -m -O /home/drue/.cache/tuxmake/builds/685/tmp \
       /home/drue/.cache/tuxmake/builds/685/tmp/.config \
       /home/drue/.cache/tuxmake/builds/685/tmp/0.config
Using /home/drue/.cache/tuxmake/builds/685/tmp/.config as base
Merging /home/drue/.cache/tuxmake/builds/685/tmp/0.config
#
# merged configuration written to /home/drue/.cache/tuxmake/builds/685/tmp/.config (needs make)
#
make --silent --keep-going --jobs=16 O=/home/drue/.cache/tuxmake/builds/685/tmp \
     ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
     'HOSTCC=ccache clang' 'CC=ccache clang' olddefconfig

这里的 .config 也是通过构建 defconfig 来生成的,然后使用 merge_config.sh 来合并开发者想指定的 config 选项。剩余的构建工作将按照预期进行。与第一个例子的唯一区别是增加了构建 DTB 的功能,因为这是一个 arm64 内核。

make --silent --keep-going --jobs=16 O=/home/drue/.cache/tuxmake/builds/685/tmp \
     ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
     'HOSTCC=ccache clang' 'CC=ccache clang' dtbs
mkdir -p /home/drue/.cache/tuxmake/builds/685/tmp/dtbsinstall/dtbs
make --silent --keep-going --jobs=16 O=/home/drue/.cache/tuxmake/builds/685/tmp \
     ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
     'HOSTCC=ccache clang' 'CC=ccache clang' dtbs_install \
     INSTALL_DTBS_PATH=/home/drue/.cache/tuxmake/builds/685/tmp/dtbsinstall/dtbs
tar caf /home/drue/.cache/tuxmake/builds/685/tmp/dtbs.tar.xz \
    -C /home/drue/.cache/tuxmake/builds/685/tmp/dtbsinstall dtbs

DTBs build 的输出文件就是 output 目录下的 dtbs.tar.xz 文件,其中包含了所有的 DTB。

指定并管理 Linux 内核 config 选项一直是一个难题。TuxMake 提供了一个 --kconfig 参数,默认就是 defconfig 文件。如果选择了不同的文件,那么就会针对相应的 config target(如 tinyconfig、allmodconfig 或 allnoconfig)来进行编译。也支持使用那些不属于预设配置文件的路径或 URL。

此外还可以额外指定一些 Kconfig 选项,这是通过使用一个或多个 --kconfig-add 参数来指定的。 --kconfig-add 参数可以是一个配置文件代码片段的路径或 URL,也可以是一个 Kconfig 字符串,例如 CONFIG_KASAN=y。如果提供的是 URL,就会先下载下来。这里所额外指定的所有 config 选项都会用 scripts/kconfig/merge_config.sh 和 make olddefconfig 来合并起来。

在编译完成之后,目录中将包含一个 build log、压缩后的内核映像文件、kernel config、一个描述了 build 过程以及环境设置等各个方面的 JSON metadata 文件、system map 文件、压缩过的 debug kernel、压缩过的包含所有 module 文件的 tar 包、和压缩过的 DTB tar 文件。随着后续实现更多的 target,将会有更多的生成产物。

Portability and reproducibility

因为 TuxMake 使用的构建环境是可以共享的,而且比如 Kconfig 之类的配置都可以用 URL 来指定,所以 TuxMake 的构建命令可以简单发送给其他人。在向邮件列表报告构建问题的时候,一行 TuxMake 命令就包含了能复现这个构建问题的精确指令。随便哪个用户对着同一个 Linux 源代码目录来执行相同的 TuxMake 命令,都会看到相同的编译结果。

此外也可以支持每个 bit 都精确可重复生成的编译方式,但这需要额外的构建参数。首先,必须使用完全相同的 container。这可以通过 –image 参数来实现,该参数可以是指向某个 container 的完整路径,同时包括 sha256 digest 摘要数据。其次,环境变量 KBUILD_BUILD_TIMESTAMP, KBUILD_BUILD_USER, 和 KBUILD_BUILD_HOST 都必须要先用 -e 来设置好,因为它们会影响到最终生成的内核镜像文件。在正常情况下,只要上述这些数据都是采用同样的设置,那么编译出来的二进制文件就一定是与之相匹配的(除了在 kernel.org 上已经明确介绍了的那些 reproducible build 中的例外事项)。

例如,下面的命令会在 x86_64 host 上针对标签为 v5.10 的 Linux 源代码进行编译,产生一个以 8d066f679eac 开头的 bzImage sha256。无论使用 -r podman 还是 -r docker 都可以。

$ tuxmake --image \
  docker.io/tuxmake/x86_64_gcc@sha256:f8218cbfad8ecf6628fc44db864a402070feb87ff43a880e1409649172d4bc8c \
  -r podman -k tinyconfig \
  -e "KBUILD_BUILD_TIMESTAMP='Tue May 26 16:16:14 2020 -0500'" \
  -e "KBUILD_BUILD_USER=tuxmake" \
  -e "KBUILD_BUILD_HOST=tuxmake"

请注意,这个例子是用 TuxMake 0.11.0 来执行的,在可预见的未来应该都是没问题的。然而,TuxMake 未来的版本可能会在构建环境中引入额外的默认变量,从而导致这个例子不再能兼容。

Quality

TuxMake 的行为是否足够显而易见、足够透明、足够可靠,这是最重要的。如果无法拥有基本的信任以及质量,那么开发这个工具就得不偿失的。TuxMake 确保了 100% 的单元测试覆盖率,这意味着每一行代码都有至少一个基本测试能覆盖到。此外,它还包含了全面的集成测试,会使用一个内嵌的(包含在它自己的 Git 仓库之内)"fakelinux" 代码库,这样就可以用来针对每种支持的 runtime 环境来模拟大量的伪内核构建以及边边角角的情况。

遍历测试(Mutation testing)在 TuxMake 代码库中经常使用到,通过针对运行的代码来专门生成一些变化并且,并确保每种变化都有相应的 failing test case(专门确保出错的 test case),来发现那些不容易覆盖到的边角情况。。

该项目还采用了对所提供的 container image 内容的自动测试,以避免出现 regression。这些测试包括去检查需要的工具和编译器是否在默认的 $PATH 中可以直接用到,以及在实际构建中使用 container image 的集成测试。

针对每一个 merge request,以及针对每一个真正合入 TuxMake 的改动,都会使用 GitLab pipeline 来自动进行所有这些测试。

Getting Started

TuxMake 可以从源码来安装,也可以用 pip 安装。如果要想使用 container runtime,那么还要安装 Docker 或 Podman,并且确保用户有权限运行 container。其他的安装选项以及完整的文档在在 docs.tuxmake.org。新功能的提出,以及 bug fix 等会作为 GitLab issue 来跟踪。

除了命令行接口之外,TuxMake 还提供了一个 Python 接口,可以用来从 Python 代码发起 Linux 内核构建。大多数在命令行中可用的参数也可以用于 Build() 这个构造函数,下面是一个最小的例子:

import tuxmake.build

build = tuxmake.build.Build("/home/drue/src/linux-mainline")
build.run()

TuxMake 由 Linaro 和 TuxBuild 这个商业化的构建服务来赞助。TuxMake 在本地运行,来执行单个内核的构建,而 TuxBuild 则是一个集成到了持续集成(CI)系统中的 API,可以根据需要来并行执行大量 Linux 内核构建工作。

开发 TuxMake 是为了能解决不受限于具体 target、架构、toolchain、内核配置以及构建主机的环境的自动 Linux 内核构建问题。Git 解决了 Linux 源代码这一边的问题,使得人们可以轻松地确定某任何一个版本的内核代码,交流中不受时间和空间的限制。我们希望 TuxMake 能够提供一个通用的接口,来执行 Linux 内核构建、解决 reproducibility 问题,也可以清晰地交流清楚任何 Linux kernel build 的问题,从而帮助解决 Linux 的编译难题。

[I would like to thank Antonio Terceiro, TuxMake's author and maintainer, for his help with this article.]

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Linux_Everything/article/details/112855663

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include<stdio.h>#include<string.h>#include<stdlib.h>#include<malloc.h>#include<iostream>#include<stack>#include<queue>using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签