Kconfig 语言

简介

配置数据库是组织成树状结构的配置选项的集合

+- Code maturity level options
|  +- Prompt for development and/or incomplete code/drivers
+- General setup
|  +- Networking support
|  +- System V IPC
|  +- BSD Process Accounting
|  +- Sysctl support
+- Loadable module support
|  +- Enable loadable module support
|     +- Set version information on all module symbols
|     +- Kernel module loader
+- ...

每个条目都有其自己的依赖关系。这些依赖关系用于确定条目的可见性。只有当其父条目也可见时,任何子条目才可见。

Kconfig 语法

配置文件描述了一系列菜单项,其中每一行都以关键字开头(帮助文本除外)。以下关键字结束菜单项

  • config

  • menuconfig

  • choice/endchoice

  • comment

  • menu/endmenu

  • if/endif

  • source

前五个关键字也开始定义菜单项。

config

"config" <symbol>
<config options>

这定义了一个配置符号 <符号>,并接受上述任何属性作为选项。

menuconfig

"menuconfig" <symbol>
<config options>

这与上面的简单配置条目类似,但它也给前端一个提示,即所有子选项应显示为单独的选项列表。为了确保所有子选项都将真正显示在 menuconfig 条目下,而不是在它之外,<config options> 列表中的每个项目都必须依赖于 menuconfig 符号。在实践中,这可以通过使用以下两种构造之一来实现。

(1):
menuconfig M
if M
    config C1
    config C2
endif

(2):
menuconfig M
config C1
    depends on M
config C2
    depends on M

在以下示例 (3) 和 (4) 中,C1 和 C2 仍然具有 M 依赖项,但由于 C0 不依赖于 M,它们将不再出现在 menuconfig M 下。

(3):
menuconfig M
    config C0
if M
    config C1
    config C2
endif

(4):
menuconfig M
config C0
config C1
    depends on M
config C2
    depends on M

choices

"choice"
<choice options>
<choice block>
"endchoice"

这定义了一个选择组,并接受 “prompt”、“default”、“depends on” 和 “help” 属性作为选项。

一个选择只允许选择一个配置条目。

comment

"comment" <prompt>
<comment options>

这定义了一个注释,该注释在配置过程中显示给用户,并且还会回显到输出文件中。唯一可能的选项是依赖项。

menu

"menu" <prompt>
<menu options>
<menu block>
"endmenu"

这定义了一个菜单块,有关更多信息,请参见上面的“菜单结构”。唯一可能的选项是依赖项和“visible”属性。

if

"if" <expr>
<if block>
"endif"

这定义了一个 if 块。依赖表达式 <expr> 被附加到所有封闭的菜单条目。

source

"source" <prompt>

这将读取指定的配置文件。此文件始终会被解析。

mainmenu

"mainmenu" <prompt>

如果配置程序选择使用它,这将设置配置程序的标题栏。它应该放置在配置的顶部,在任何其他语句之前。

‘#’ Kconfig 源文件注释

源文件行中任何位置的未加引号的 ‘#’ 字符表示源文件注释的开始。该行的其余部分是注释。

Kconfig 提示

这是一组 Kconfig 技巧,其中大多数技巧乍一看并不明显,并且大多数技巧已成为多个 Kconfig 文件中的惯用语。

添加通用功能并使用法可配置

实现某些架构相关但不适用于所有架构的功能/特性是一种常见的惯用语。推荐的方法是使用一个名为 HAVE_* 的配置变量,该变量在通用的 Kconfig 文件中定义,并由相关的架构选择。一个例子是通用的 IOMAP 功能。

我们会在 lib/Kconfig 中看到

# Generic IOMAP is used to ...
config HAVE_GENERIC_IOMAP

config GENERIC_IOMAP
      depends on HAVE_GENERIC_IOMAP && FOO

在 lib/Makefile 中,我们会看到

obj-$(CONFIG_GENERIC_IOMAP) += iomap.o

对于每个使用通用 IOMAP 功能的架构,我们会看到

config X86
      select ...
      select HAVE_GENERIC_IOMAP
      select ...

注意:我们使用现有的配置选项,并避免创建新的配置变量来选择 HAVE_GENERIC_IOMAP。

注意:使用内部配置变量 HAVE_GENERIC_IOMAP,引入它是为了克服 select 的限制,这将强制配置选项为 ‘y’,而不管依赖关系如何。依赖关系被移动到符号 GENERIC_IOMAP,我们避免了 select 强制符号等于 ‘y’ 的情况。

添加需要编译器支持的功能

有几个功能需要编译器支持。描述对编译器功能的依赖关系的推荐方法是使用 “depends on”,后跟一个测试宏。

config STACKPROTECTOR
      bool "Stack Protector buffer overflow detection"
      depends on $(cc-option,-fstack-protector)
      ...

如果您需要将编译器功能公开给 makefiles 和/或 C 源文件,则 CC_HAS_ 是配置选项的推荐前缀。

config CC_HAS_FOO
      def_bool $(success,$(srctree)/scripts/cc-check-foo.sh $(CC))

仅作为模块构建

要将组件构建限制为仅模块,请使用 “depends on m” 限定其配置符号。例如:

config FOO
      depends on BAR && m

将 FOO 限制为模块 (=m) 或禁用 (=n)。

编译测试

如果配置符号具有依赖关系,但如果未满足该依赖关系,则仍可以编译由配置符号控制的代码,则建议通过向依赖项添加“|| COMPILE_TEST”子句来增加构建覆盖率。这对于更奇特的硬件驱动程序尤其有用,因为它允许持续集成系统在更常见的系统上编译测试代码,并以这种方式检测错误。请注意,在未满足依赖关系的系统上运行时,经过编译测试的代码应避免崩溃。

架构和平台依赖项

由于存根的存在,现在大多数驱动程序都可以在大多数架构上编译。但是,这并不意味着在所有地方都有所有驱动程序都有意义,因为实际硬件可能只存在于特定的架构和平台上。对于片上 SoC IP 核来说尤其如此,这些 IP 核可能仅限于特定的供应商或 SoC 系列。

为了防止询问用户关于用户正在为其编译内核的系统上无法使用的驱动程序,并且如果这有意义,则控制驱动程序编译的配置符号应包含适当的依赖关系,从而将符号的可见性限制为(平台子集)驱动程序可以使用的平台。依赖关系可以是架构(例如 ARM)或平台(例如 ARCH_OMAP4)依赖关系。这不仅使发行版配置所有者,而且使配置内核的每个开发人员或用户的生活更简单。

可以通过将其与上面的编译测试规则组合来放宽这种依赖关系,从而导致:

config FOO

bool “支持 foo 硬件” depends on ARCH_FOO_VENDOR || COMPILE_TEST

可选依赖项

某些驱动程序可以选择使用另一个模块的功能,或者在禁用该模块的情况下干净地构建,但是在尝试从内置驱动程序使用该可加载模块时会导致链接失败。

在 Kconfig 逻辑中表达此可选依赖关系的最常见方法是使用稍微违反直觉的方式:

config FOO
      tristate "Support for foo hardware"
      depends on BAR || !BAR

这意味着要么对 BAR 有依赖关系,该依赖关系不允许 FOO=y 与 BAR=m 的组合,要么 BAR 被完全禁用。如果有多个驱动程序具有相同的依赖关系,则可以使用一个辅助符号,例如:

config FOO
      tristate "Support for foo hardware"
      depends on BAR_OPTIONAL

config BAR_OPTIONAL
      def_tristate BAR || !BAR

Kconfig 递归依赖关系限制

如果您遇到了 Kconfig 错误:“检测到递归依赖关系”,则您遇到了 Kconfig 的递归依赖关系问题,递归依赖关系可以概括为循环依赖关系。kconfig 工具需要确保 Kconfig 文件符合指定的配置要求。为了做到这一点,kconfig 必须确定所有 Kconfig 符号的可能值,如果两个或多个 Kconfig 符号之间存在循环关系,则目前无法做到这一点。有关更多详细信息,请参阅下面的“简单的 Kconfig 递归问题”小节。Kconfig 不执行递归依赖关系解析;这对 Kconfig 文件编写者有一些影响。我们将首先解释为什么会出现此问题,然后提供一个示例技术限制,该限制给 Kconfig 开发人员带来了影响。希望尝试解决此限制的急切开发人员应阅读以下小节。

简单的 Kconfig 递归问题

请阅读:Documentation/kbuild/Kconfig.recursion-issue-01

使用以下命令测试:

make KBUILD_KCONFIG=Documentation/kbuild/Kconfig.recursion-issue-01 allnoconfig

累积的 Kconfig 递归问题

请阅读:Documentation/kbuild/Kconfig.recursion-issue-02

使用以下命令测试:

make KBUILD_KCONFIG=Documentation/kbuild/Kconfig.recursion-issue-02 allnoconfig

解决 kconfig 递归问题的实用方法

遇到递归 Kconfig 问题的开发人员可以选择两种方法。我们在下面记录了它们,还提供了一个通过这些不同解决方案解决的历史问题列表。

  1. 删除任何多余的 “select FOO” 或 “depends on FOO”

  2. 匹配依赖关系语义

    b1) 将所有 “select FOO” 交换为 “depends on FOO”,或

    b2) 将所有 “depends on FOO” 交换为 “select FOO”

可以通过从 CORE_BELL_A_ADVANCED 中删除 “select CORE” 来测试 a) 的解决方法,因为 CORE_BELL_A 依赖于 CORE,这已经是隐含的了。有时可能无法删除某些依赖关系标准,对于这种情况,您可以使用解决方案 b)。

可以在示例 Kconfig 文件 Documentation/kbuild/Kconfig.recursion-issue-02 中测试 b) 的两种不同的解决方法。

以下是先前针对这些类型的递归问题进行修复的示例列表;所有错误似乎都涉及一个或多个 “select” 语句和一个或多个 “depends on”。

commit

fix

06b718c01208

select A -> depends on A

c22eacfe82f9

depends on A -> depends on B

6a91e854442c

select A -> depends on A

118c565a8f2e

select A -> select B

f004e5594705

select A -> depends on A

c7861f37b4c6

depends on A -> (null)

80c69915e5fb

select A -> (null) (1)

c2218e26c0d0

select A -> depends on A (1)

d6ae99d04e1c

select A -> depends on A

95ca19cf8cbf

select A -> depends on A

8f057d7bca54

depends on A -> (null)

8f057d7bca54

depends on A -> select A

a0701f04846e

select A -> depends on A

0c8b92f7f259

depends on A -> (null)

e4e9e0540928

select A -> depends on A (2)

7453ea886e87

depends on A > (null) (1)

7b1fff7e4fdf

select A -> depends on A

86c747d2a4f0

select A -> depends on A

d9f9ab51e55e

select A -> depends on A

0c51a4d8abd6

depends on A -> select A (3)

e98062ed6dc4

select A -> depends on A (3)

91e5d284a7f1

select A -> (null)

  1. 部分(或无)引用错误。

  2. 这似乎是该修复的要点。

  3. 相同的错误。

未来的 kconfig 工作

欢迎在澄清语义和评估使用完整 SAT 求解器方面进行 kconfig 工作。为了支持更复杂的依赖关系映射和/或查询,可以使用完整 SAT 求解器,例如,SAT 求解器的一个可能的用例可能是处理当前已知的递归依赖关系问题。尚不清楚这是否会解决此类问题,但有必要进行此类评估。如果对完整 SAT 求解器的支持过于复杂,或者它无法解决递归依赖关系问题,则 Kconfig 至少应具有清晰且定义明确的语义,其中还应解决并记录诸如处理递归依赖关系之类的限制或要求。

欢迎在 Kconfig 的这两个领域开展进一步的工作。我们将在接下来的两个小节中详细阐述这两者。

Kconfig 的语义

Kconfig 的使用范围很广,Linux 现在只是 Kconfig 的用户之一:一项研究已经完成了对 12 个项目中使用 Kconfig 的广泛分析 [0]。尽管 Kconfig 应用广泛,并且尽管本文档在记录基本的 Kconfig 语法方面做得不错,但我们仍然欢迎对 Kconfig 语义进行更精确的定义。一个项目通过使用 xconfig 配置器推导出了 Kconfig 语义 [1]。应该进行工作以确认推导出的语义是否与我们预期的 Kconfig 设计目标相符。另一个项目形式化了 Kconfig 语言核心子集的指称语义 [10]

拥有定义良好的语义对于工具进行依赖关系的实际评估非常有用。例如,一个这样的案例是将 Kconfig 推断的语义表达为布尔抽象,将 Kconfig 逻辑转换为布尔公式,并在此基础上运行 SAT 求解器,以查找死代码/功能(始终处于非活动状态)。使用这种方法在 Linux 中发现了 114 个死功能 [1] (第 8 节:对有效性的威胁)。基于 [10] 中语义的 kismet 工具可以发现对反向依赖关系的滥用,并导致了对 Linux Kconfig 文件的数十次提交修复 [11]

证实这一点可能很有用,因为 Kconfig 是主要的工业可变性建模语言之一 [1] [2]。 对它的研究将有助于评估此类语言的实际用途,此前其应用仅限于理论层面,而对现实世界的需求了解不足。目前,只有反向工程技术被用来推断 Kconfig 等可变性建模语言的语义 [3]

Kconfig 的完整 SAT 求解器

虽然 SAT 求解器 [4] 尚未被 Kconfig 直接使用,正如上一小节中提到的,但是已经有人将 Kconfig 推断的语义表达为布尔抽象,将 Kconfig 逻辑转换为布尔公式,并在其上运行 SAT 求解器 [5]。另一个相关的已知项目是 CADOS [6](前身为 VAMOS [7])以及相关的工具,主要是 undertaker [8],最初在 [9] 中被介绍。undertaker 的基本概念是从 Kconfig 中提取可变性模型,并将它们与从 CPP #ifdef 和构建规则中提取的命题公式一起放入 SAT 求解器中,以便查找死代码、死文件和死符号。如果希望在 Kconfig 上使用 SAT 求解器,一种方法是评估如何将这些工作重新利用到 Kconfig 上。现有项目的指导者有足够的兴趣,不仅可以帮助指导如何将这项工作集成到上游,还可以帮助长期维护它。有兴趣的开发人员应该访问

https://kernelnewbies.org/KernelProjects/kconfig-sat