小知识:探索Linux内核:Kconfig的秘密

深入了解linux配置/构建系统是如何工作的。

自从linux内核代码迁移到git之后,linux内核配置/构建系统(也称为kconfig/kbuild)已经存在了很长时间。然而,作为支持基础设施,它很少受到关注;即使在日常工作中使用它的内核开发人员也从未真正考虑过它。

为了探索linux内核是如何编译的,本文将深入研究kconfig/kbuild内部进程,解释.config文件和vmlinux/bzimage文件是如何生成的,并介绍一个用于依赖性跟踪的智能技巧。

kconfig

构建内核的第一步总是配置。kconfig帮助使linux内核高度模块化和可定制。kconfig为用户提供了许多配置目标:

config 使用面向行的程序更新当前配置 nconfig 使用基于ncurses菜单的程序更新当前配置 menuconfig 使用基于菜单的程序更新当前配置 xconfig 利用基于qt的前端更新当前配置 gconfig 利用基于gtk+的前端更新当前配置 oldconfig 使用提供的.config作为基础更新当前配置 localmodconfig 更新未加载的当前配置禁用模块 localyesconfig 更新当前配置,将本地mods转换为核心 defconfig 从arch提供的defconfig中获得默认配置的新配置 savedefconfig 将当前配置保存为./defconfig(最小配置) allnoconfig 使用“no”回答所有选项的新配置 allyesconfig 新配置,在该配置中,所有选项都以“是”接受 allmodconfig 在可能的情况下选择新的配置模块 alldefconfig 将所有符号设置为默认值的新配置 randconfig 具有对所有选项的随机答案的新配置 listnewconfig 列出新选项 olddefconfig 与oldconfig相同,但在不提示的情况下将新符号设置为默认值 kvmconfig 为kvm客户端内核支持启用其他选项 xenconfig 启用xen dom0和来宾内核支持的其他选项 tinyconfig 配置尽可能小的内核

我认为menuconfig是这些目标中最受欢迎的。目标由不同的主机程序进行处理,这些程序由内核提供,并在内核构建过程中生成。一些目标有一个gui(为了用户的方便),而大多数没有。与kconfig相关的工具和源代码主要位于scripts/kconfig/在内核源代码中。我们可以从scripts/kconfig/makefile,有几个主机程序,包括conf, mconf,和nconf。除了conf,它们每个都负责基于gui的配置目标之一,因此,conf和他们中的大多数人打交道。

从逻辑上讲,kconfig的基础结构有两个部分:一个实现了新语言要定义配置项(请参阅内核源代码下的kconfig文件),而其他配置项则解析kconfig语言并处理配置操作。

大多数配置目标的内部流程大致相同(如下所示):

%小知识:探索Linux内核:Kconfig的秘密-猿站网-插图

注意,所有配置项都有一个默认值。

第一步读取源根下的kconfig文件以构造初始配置数据库;然后根据此优先级读取现有配置文件来更新初始数据库:

.config /lib/module/$(shell,uname-r)/.config /etc/kernel-config /boot/config-$(shell,uname-r) arch_defconfig arch/$(arch)/defconfig

如果您正在进行基于gui的配置,则通过menuconfig或基于命令行的配置oldconfig,数据库将根据您的自定义进行更新。最后,将配置数据库转储到.config文件中。

但是.config文件不是内核构建的最终素材;这就是为什么syncconfig目标存在。syncconfig以前是一个名为silentoldconfig,但是它不像旧名字说的那样,所以它被重命名了。此外,由于它是内部使用(而不是为用户),它被从列表中删除。

下面是一个例子syncconfig作用:

%小知识:探索Linux内核:Kconfig的秘密-1猿站网-插图

syncconfig接受.config作为输入并输出许多其他文件,这些文件分为三类:

auto.conf & tristate.conf用于生成文件文本处理。例如,您可能在组件的makefile中看到这样的语句:

obj-$(config_generic_calibrate_delay) += calibrate.o

autoconf.h在c语言源文件中使用。

空头文件include/config/用于在kbuild期间进行配置依赖项跟踪,下面将对此进行解释。

配置之后,我们将知道哪些文件和代码段没有编译。

kbuild

组件式建筑,称为递归制作,是gnu的一种常见方式。制作,使管理一个大型项目。kbuild是递归make的一个很好的例子。通过将源文件划分为不同的模块/组件,每个组件都由自己的makefile管理。当您开始构建时,顶级makefile按正确的顺序调用每个组件的makefile,构建组件,并将它们收集到最终的执行程序中。

kbuild指的是不同类型的makefile:

makefile位于源根中的顶部makefile。 .config是内核配置文件。 arch/$(arch)/makefile是拱形makefile,这是对顶部makefile的补充。 scripts/makefile*描述所有kbuild makefile的通用规则。 最后,大约有500个kbuildmakefiles.

顶部的makefile包含archmakefile,读取.config文件,进入子目录,调用制作,使中定义的例程的帮助下实现每个组件的makefile。scripts/makefile*,构建每个中间对象,并将所有中间对象链接到vmlinux。核心文件documentation/kbuild/makefiles.txt描述这些制作文件的所有方面。

例如,让我们看看在x86-64上如何生成vmlinux:

%小知识:探索Linux内核:Kconfig的秘密-2猿站网-插图

(插图是根据理查德·y·史蒂文(richard y.steven)的博客。经提交人许可后予以更新和使用。

所有.o进入vmlinux的文件首先进入它们自己的built-in.a,这是通过变量表示的。kbuild_vmlinux_init, kbuild_vmlinux_main, kbuild_vmlinux_libs,然后收集到vmlinux文件中。

看看如何在linux内核中实现递归make,并借助简化的makefile代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# in top makefile
vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps)
+$(call if_changed,link-vmlinux)
# variable assignments
vmlinux-deps := $(kbuild_lds) $(kbuild_vmlinux_init) $(kbuild_vmlinux_main) $(kbuild_vmlinux_libs)
export kbuild_vmlinux_init := $(head-y) $(init-y)
export kbuild_vmlinux_main := $(core-y) $(libs-y2) $(drivers-y) $(net-y) $(virt-y)
export kbuild_vmlinux_libs := $(libs-y1)
export kbuild_lds     := arch/$(srcarch)/kernel/vmlinux.lds
init-y     := init/
drivers-y    := drivers/ sound/ firmware/
net-y      := net/
libs-y     := lib/
core-y     := usr/
virt-y     := virt/
# transform to corresponding built-in.a
init-y     := $(patsubst %/, %/built-in.a, $(init-y))
core-y     := $(patsubst %/, %/built-in.a, $(core-y))
drivers-y    := $(patsubst %/, %/built-in.a, $(drivers-y))
net-y      := $(patsubst %/, %/built-in.a, $(net-y))
libs-y1     := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2     := $(patsubst %/, %/built-in.a, $(filter-out %.a, $(libs-y)))
virt-y     := $(patsubst %/, %/built-in.a, $(virt-y))
# setup the dependency. vmlinux-deps are all intermediate objects, vmlinux-dirs
# are phony targets, so every time comes to this rule, the recipe of vmlinux-dirs
# will be executed. refer “4.6 phony targets” of `info make`
$(sort $(vmlinux-deps)): $(vmlinux-dirs) ;
# variable vmlinux-dirs is the directory part of each built-in.a
vmlinux-dirs  := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
$(core-y) $(core-m) $(drivers-y) $(drivers-m) \
$(net-y) $(net-m) $(libs-y) $(libs-m) $(virt-y)))
# the entry of recursive make
$(vmlinux-dirs):
$(q)$(make) $(build)=$@ need-builtin=1

递归的配方扩展,例如:

make -f scripts/makefile.build obj=init need-builtin=1

这意味着make将进入scripts/makefile.build继续建造每一个built-in.a。在.的帮助下scripts/link-vmlinux.sh,vmlinux文件最终位于源根下。

理解vmlinux与bzimage

许多linux内核开发人员可能不清楚vmlinux和bzimage之间的关系。例如,以下是它们在x86-64中的关系:

%小知识:探索Linux内核:Kconfig的秘密-3猿站网-插图

源根vmlinux被剥离、压缩、放入piggy.s,然后将其他对等对象链接到arch/x86/boot/compressed/vmlinux。同时,下面生成一个名为setup.bin的文件arch/x86/boot。可能有一个包含重定位信息的可选的第三个文件,具体取决于config_x86_relocs.

一个名为build由内核提供,将这两个(或三个)部分构建到最终的bzimage文件中。

依赖跟踪

kbuild跟踪三种依赖关系:

所有的前提文件(*.c和*.h) config_在所有先决条件文件中使用的选项 用于编译目标的命令行依赖关系。

第一个很容易理解,但是第二个和第三个呢?内核开发人员经常看到这样的代码片段:

?
1
2
3
#ifdef config_smp
__boot_cpu_id = cpu;
#endif

什么时候config_smp更改后,这段代码应该重新编译。编译源文件的命令行也很重要,因为不同的命令行可能导致不同的对象文件。

当.c文件通过#include指令,您需要编写这样的规则:

?
1
2
main.o: defs.h
recipe…

在管理一个大型项目时,您需要很多这样的规则;所有这些规则都会乏味。幸运的是,大多数现代c编译器可以通过查看#include源文件中的行。对于gnu编译器集合(gcc),只需添加一个命令行参数:-md depfile

?
1
2
3
4
5
# in scripts/makefile.lib
c_flags    = -wp,-md,$(depfile) $(nostdinc_flags) $(linuxinclude)   \
-include $(srctree)/include/linux/compiler_types.h    \
$(__c_flags) $(modkern_cflags)              \
$(basename_flags) $(modname_flags)

这将生成一个.d文件的内容如下:

?
1
2
3
4
init_task.o: init/init_task.c include/linux/kconfig.h \
include/generated/autoconf.h include/linux/init_task.h \
include/linux/rcupdate.h include/linux/types.h \

然后主机程序fixdep通过获取其他两个依赖项来处理其他两个依赖项。depfile命令行作为输入,然后以makefile语法输出.cmd文件,它记录目标的命令行和所有先决条件(包括配置)。看起来是这样的:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
# the command line used to compile the target
cmd_init/init_task.o := gcc -wp,-md,init/.init_task.o.d -nostdinc …
# the dependency files
deps_init/init_task.o := \
$(wildcard include/config/posix/timers.h) \
$(wildcard include/config/arch/task/struct/on/stack.h) \
$(wildcard include/config/thread/info/in/task.h) \
include/uapi/linux/types.h \
arch/x86/include/uapi/asm/types.h \
include/uapi/asm-generic/types.h \

在递归生成过程中将包含一个.cmd文件,提供所有依赖项信息,并帮助决定是否重新构建目标。

这背后的秘密是,fixdep将解析depfile(.d文件),然后解析其中的所有依赖文件,搜索所有config_string的文本,将它们转换为相应的空头文件,并将它们添加到目标的先决条件中。每次配置更改时,相应的空头文件也将被更新,因此kbuild可以检测到该更改并重新构建依赖于它的目标。因为还记录了命令行,所以很容易比较最后的编译参数和当前的编译参数。

展望未来

kconfig/kbuild很长一段时间没有变化,直到新的维护者山田正一郎(masahiro yamada)在2017年初加入,现在kbuild又在积极发展。如果你很快看到了与本文不同的东西,不要感到惊讶。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对服务器之家的支持。如果你想了解更多相关内容请查看下面相关链接

原文链接:https://opensource.com/article/18/10/kbuild-and-kconfig

声明: 猿站网有关资源均来自网络搜集与网友提供,任何涉及商业盈利目的的均不得使用,否则产生的一切后果将由您自己承担! 本平台资源仅供个人学习交流、测试使用 所有内容请在下载后24小时内删除,制止非法恶意传播,不对任何下载或转载者造成的危害负任何法律责任!也请大家支持、购置正版! 。本站一律禁止以任何方式发布或转载任何违法的相关信息访客发现请向站长举报,会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。本网站的资源部分来源于网络,如有侵权烦请发送邮件至:2697268773@qq.com进行处理。
建站知识

小知识:苹果M1芯片安装nginx 并且部署vue项目步骤详解

2023-3-22 2:29:04

建站知识

小知识:十个理由告诉你Linux为何越来越受欢迎

2023-3-22 2:38:01

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索