Linux Kconfig

最后更新: 2022-07-06 20:06:54 +0800 +0800
tags: Kconfig | Kernel | Linux
原文以英文撰写。您正在阅读AI翻译版本。

Kconfig 描述构建 Linux 内核配置时可选的选择。内核开发者编辑 Kconfig 文件来定义符号及其关系;用户通过 make menuconfig、make nconfig、make xconfig 等前端,或 olddefconfig 等非交互目标选择值。

生成的 .config 是 Kbuild 的输入,而不是完整的构建脚本。配置同步会生成供 Make 和 C 使用的文件,包括 include/config/auto.conf 和 include/generated/autoconf.h。

从根文件到一个配置 #

解析从顶层 Kconfig 开始。其 source 语句包含子系统文件,架构选择会引入 arch/$(SRCARCH)/Kconfig。其他包含形式有:

  • rsource,其路径相对于包含文件;
  • osource,忽略缺失文件;
  • orsource,组合相对路径和可选行为。

mainmenu 设置前端标题。menu/endmenu、if/endif、choice/endchoice、comment、config 和 menuconfig 构建条目结构。

同一符号可以在多个被包含文件中定义。Kconfig 会合并该符号的属性;它不会创建架构局部变量。定义必须使用一致的类型,依赖子句会累积,默认值顺序仍然重要。因此,公共文件可以声明不可见的能力,而架构定义可以选择它。

kconfig
# lib/Kconfig
config HAVE_FAST_CHECKSUM
    bool

config FAST_CHECKSUM
    bool "Use the generic fast checksum implementation"
    depends on HAVE_FAST_CHECKSUM

# arch/example/Kconfig
config EXAMPLE
    bool
    select HAVE_FAST_CHECKSUM

HAVE_* 符号表示实现能力。面向用户的功能随后依赖该能力,从而避免使用 select 绕过功能的实际依赖。

宏和预处理器层 #

Kconfig 会在菜单条目语言处理一行之前执行文本展开。其变量语法类似 Make:

makefile
immediate := $(ARCH)
deferred = $(ARCH)
message = architecture $(1), compiler $(2)

$(info,configuring $(immediate))
label := $(message,$(ARCH),$(CC))
  • := 立即展开右侧。
  • = 存储递归展开的值。
  • += 根据变量现有的展开风格追加。
  • $(name) 展开变量。
  • $(name,arg1,arg2) 展开参数化变量,其中 $(1)、$(2) 等是位置参数。

与 Make 不同,参数化 Kconfig 变量直接调用,不存在 $(call ...)。逗号分隔函数参数。内置函数包括 shell、info、warning-if、error-if、filename 和 lineno。内核还通过 Kconfig 函数机制增加了编译器选项探测等函数。

宏展开是文本处理,因此引号和空白对接收结果的语言仍然重要。应将预处理用于小型重复表达式和功能探测,而不是生成庞大且难以审计的菜单片段。

符号、类型和提示 #

config NAME 定义名为 NAME 的符号;.config 和 C 将其暴露为 CONFIG_NAME。 menuconfig 定义相同类型的符号,但会告知兼容前端该条目旨在包含子菜单。

有五种符号类型:

类型.config 中的示例生成的 C 形式
boolCONFIG_FOO=y 或 # CONFIG_FOO is not set#define CONFIG_FOO 1 或不存在
tristateCONFIG_FOO=y、CONFIG_FOO=m 或未设置CONFIG_FOO、CONFIG_FOO_MODULE 或二者都没有
stringCONFIG_FOO="value"带引号的字符串宏
intCONFIG_FOO=42整数宏
hexCONFIG_FOO=0x2a十六进制整数宏

bool "Enable foo" 和 tristate "Build foo" 将类型与 prompt 结合。没有可见提示的符号不能在配置前端中编辑,但默认值、select 或其他表达式仍可以为其赋值。

kconfig
config FOO
    tristate "Foo transport"
    depends on NET
    default m if MODULES
    help
      Build support for the Foo transport.

def_bool EXPR 和 def_tristate EXPR 是类型加 default EXPR 的简写。当可选条件为真时,range MIN MAX 限制 int 或 hex 符号。

三态表达式 #

逻辑 Kconfig 表达式使用有序值 n = 0、m = 1 和 y = 2。因此:

  • A && B 是两个值的最小值;
  • A || B 是最大值;
  • !A 是 2 - A。

比较会产生三态结果。括号控制分组,带引号的字面量表示字符串值。bool 不能保留 m;模块相关表达式会根据符号类型及是否启用模块进行折叠。

直接依赖施加上限:

kconfig
config USB_WIDGET
    tristate "USB widget"
    depends on USB && HAS_IOMEM

如果 USB=m,USB_WIDGET 不能变为 y。提示上的可选条件影响提示是否可见,而符号的直接依赖无论前端如何呈现,都会限制其值。

默认值、反向依赖和可见性 #

属性解决不同问题:

  • default VALUE if CONDITION 仅在用户尚未设置值时提供值。如果有多个默认值可见,则第一个可见默认值生效。默认值不会覆盖现有 .config 中保留的值。
  • depends on EXPR 限制符号的最大值和提示可见性。
  • select TARGET if CONDITION 创建反向依赖,并将 TARGET 提高到至少选择表达式的值。它不会遵守 TARGET 自身的直接依赖,因此只应选择前提已得到保证的简单不可见符号。
  • imply TARGET if CONDITION 是弱反向依赖。它提供一个仍可被可见用户选择降低的下限,同时目标的直接依赖继续限制结果。
  • visible if EXPR 限制 menu 块的可见性,而不改变其中符号的值。

例如:

kconfig
config DRIVER_FOO
    tristate "Foo device"
    depends on PCI
    select CRC32
    imply FW_LOADER

CRC32 应是没有未满足依赖的内部辅助符号。FW_LOADER 只是被鼓励启用,因为驱动没有它仍可运行。用 select 修复依赖循环是错误的,因为 Kconfig 会拒绝递归依赖,也不会使用 SAT 求解器推断首选方案。

菜单位置不是依赖图 #

源代码顺序和显式容器决定了大部分显示位置:

kconfig
menu "Network drivers"
    visible if NET

config NET_VENDOR_EXAMPLE
    bool "Example devices"

if NET_VENDOR_EXAMPLE
source "drivers/net/ethernet/example/Kconfig"
endif

endmenu

前端还会在后续条目足够强地依赖前一条目时推断隐式子菜单,使子条目只有在父条目可见时才能显示。语言文档描述了精确的提示依赖规则。

除此之外,依赖不会重新定位条目。符号可以依赖显示在不同部分的另一个符号,select 也不会使目标显示在选择器下方。因此,菜单布局传达导航,而依赖表达式传达有效值。将前者当作后者的渲染结果,会产生令人困惑的 Kconfig 文件。

生成的配置产物 #

前端写入 .config 后,Kbuild 会同步生成的输入。重要表示形式为:

text
.config
include/config/auto.conf
include/generated/autoconf.h

.config 是持久化的面向用户的赋值文件。auto.conf 将赋值提供给 Make,因此规则可以使用如下表达式:

makefile
obj-$(CONFIG_USB_WIDGET) += usb-widget.o

autoconf.h 将启用的符号提供给 C 预处理。内核 C 代码应包含正常的内核头文件,而不是直接包含 autoconf.h。诸如 IS_ENABLED(CONFIG_FOO) 的辅助宏可以处理内建和模块形式,而不必手写每种预处理器组合。

配置更新可以添加较新源码树引入的符号。make oldconfig 会询问新的选择,而 make olddefconfig 接受其默认值。make savedefconfig 会相对于默认值写入最小 defconfig;它不能替代归档完整的 .config。

参考资料 #

评论

在 GitHub 上阅读或评论(发表评论需要登录)