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

智能推荐

while循环&CPU占用率高问题深入分析与解决方案_main函数使用while(1)循环cpu占用99-程序员宅基地

文章浏览阅读3.8k次,点赞9次,收藏28次。直接上一个工作中碰到的问题,另外一个系统开启多线程调用我这边的接口,然后我这边会开启多线程批量查询第三方接口并且返回给调用方。使用的是两三年前别人遗留下来的方法,放到线上后发现确实是可以正常取到结果,但是一旦调用,CPU占用就直接100%(部署环境是win server服务器)。因此查看了下相关的老代码并使用JProfiler查看发现是在某个while循环的时候有问题。具体项目代码就不贴了,类似于下面这段代码。​​​​​​while(flag) {//your code;}这里的flag._main函数使用while(1)循环cpu占用99

【无标题】jetbrains idea shift f6不生效_idea shift +f6快捷键不生效-程序员宅基地

文章浏览阅读347次。idea shift f6 快捷键无效_idea shift +f6快捷键不生效

node.js学习笔记之Node中的核心模块_node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是-程序员宅基地

文章浏览阅读135次。Ecmacript 中没有DOM 和 BOM核心模块Node为JavaScript提供了很多服务器级别,这些API绝大多数都被包装到了一个具名和核心模块中了,例如文件操作的 fs 核心模块 ,http服务构建的http 模块 path 路径操作模块 os 操作系统信息模块// 用来获取机器信息的var os = require('os')// 用来操作路径的var path = require('path')// 获取当前机器的 CPU 信息console.log(os.cpus._node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是

数学建模【SPSS 下载-安装、方差分析与回归分析的SPSS实现(软件概述、方差分析、回归分析)】_化工数学模型数据回归软件-程序员宅基地

文章浏览阅读10w+次,点赞435次,收藏3.4k次。SPSS 22 下载安装过程7.6 方差分析与回归分析的SPSS实现7.6.1 SPSS软件概述1 SPSS版本与安装2 SPSS界面3 SPSS特点4 SPSS数据7.6.2 SPSS与方差分析1 单因素方差分析2 双因素方差分析7.6.3 SPSS与回归分析SPSS回归分析过程牙膏价格问题的回归分析_化工数学模型数据回归软件

利用hutool实现邮件发送功能_hutool发送邮件-程序员宅基地

文章浏览阅读7.5k次。如何利用hutool工具包实现邮件发送功能呢?1、首先引入hutool依赖<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.19</version></dependency>2、编写邮件发送工具类package com.pc.c..._hutool发送邮件

docker安装elasticsearch,elasticsearch-head,kibana,ik分词器_docker安装kibana连接elasticsearch并且elasticsearch有密码-程序员宅基地

文章浏览阅读867次,点赞2次,收藏2次。docker安装elasticsearch,elasticsearch-head,kibana,ik分词器安装方式基本有两种,一种是pull的方式,一种是Dockerfile的方式,由于pull的方式pull下来后还需配置许多东西且不便于复用,个人比较喜欢使用Dockerfile的方式所有docker支持的镜像基本都在https://hub.docker.com/docker的官网上能找到合..._docker安装kibana连接elasticsearch并且elasticsearch有密码

随便推点

Python 攻克移动开发失败!_beeware-程序员宅基地

文章浏览阅读1.3w次,点赞57次,收藏92次。整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)近年来,随着机器学习的兴起,有一门编程语言逐渐变得火热——Python。得益于其针对机器学习提供了大量开源框架和第三方模块,内置..._beeware

Swift4.0_Timer 的基本使用_swift timer 暂停-程序员宅基地

文章浏览阅读7.9k次。//// ViewController.swift// Day_10_Timer//// Created by dongqiangfei on 2018/10/15.// Copyright 2018年 飞飞. All rights reserved.//import UIKitclass ViewController: UIViewController { ..._swift timer 暂停

元素三大等待-程序员宅基地

文章浏览阅读986次,点赞2次,收藏2次。1.硬性等待让当前线程暂停执行,应用场景:代码执行速度太快了,但是UI元素没有立马加载出来,造成两者不同步,这时候就可以让代码等待一下,再去执行找元素的动作线程休眠,强制等待 Thread.sleep(long mills)package com.example.demo;import org.junit.jupiter.api.Test;import org.openqa.selenium.By;import org.openqa.selenium.firefox.Firefox.._元素三大等待

Java软件工程师职位分析_java岗位分析-程序员宅基地

文章浏览阅读3k次,点赞4次,收藏14次。Java软件工程师职位分析_java岗位分析

Java:Unreachable code的解决方法_java unreachable code-程序员宅基地

文章浏览阅读2k次。Java:Unreachable code的解决方法_java unreachable code

标签data-*自定义属性值和根据data属性值查找对应标签_如何根据data-*属性获取对应的标签对象-程序员宅基地

文章浏览阅读1w次。1、html中设置标签data-*的值 标题 11111 222222、点击获取当前标签的data-url的值$('dd').on('click', function() { var urlVal = $(this).data('ur_如何根据data-*属性获取对应的标签对象

推荐文章

热门文章

相关标签