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>

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

menuconfig

"menuconfig" <symbol>
<config options>

这与上面的简单 config 条目类似,但它也向前端提供提示,即所有子选项应显示为单独的选项列表。 为了确保所有子选项都将真正显示在 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_* 的 config 变量,该变量在常见的 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 ...

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

注意:使用内部 config 变量 HAVE_GENERIC_IOMAP,引入它是为了克服 select 的限制,无论依赖关系如何,它都会强制 config 选项为“y”。 依赖关系已移至符号 GENERIC_IOMAP,我们避免了 select 强制符号等于“y”的情况。

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

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

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

如果您需要公开编译器功能以进行 makefile 和/或 C 源文件,则 CC_HAS_ 是 config 选项的推荐前缀

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

仅作为模块构建

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

config FOO
      depends on BAR && m

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

编译测试

如果一个 config 符号具有依赖关系,但如果未满足依赖关系,则仍可以编译由 config 符号控制的代码,则鼓励通过将“|| 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 完全被禁用。 BAR 模块必须为 !BAR 情况提供所有存根。

如果存在具有相同依赖关系的多个驱动程序,则可以使用辅助符号来获得更正式的方法,例如

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

config BAR_OPTIONAL
      def_tristate BAR || !BAR

表达可选依赖性的一种不太受欢迎的方式是在模块代码中使用 IS_REACHABLE(),例如,当模块 BAR 不提供 !BAR 存根时很有用

foo_init()
{
        if (IS_REACHABLE(CONFIG_BAR))
                bar_register(&foo);
        ...
}

通常不鼓励使用 IS_REACHABLE(),因为当 CONFIG_BAR=m 并且此代码是内置时,代码将被静默丢弃。这不是用户在将 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”来使用示例 Kconfig 文件 Documentation/kbuild/Kconfig.recursion-issue-01 测试 a) 的解决方案,因为 CORE_BELL_A 依赖于 CORE,因此这已经是隐含的。有时可能无法删除某些依赖关系标准,对于这种情况,可以使用解决方案 b)。

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

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

commit

修复

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 工作

欢迎在 Kconfig 上进行工作,包括澄清语义和评估使用完整的 SAT 求解器。完整的 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 节:对有效性的威胁)。 kismet 工具基于 [10] 中的语义,发现了对反向依赖关系的滥用,并导致对 Linux Kconfig 文件进行了数十次提交修复[11]

确认这一点可能很有用,因为 Kconfig 是领先的工业可变性建模语言之一[1] [2]。对其的研究将有助于评估此类语言的实际用途,它们的用途仅是理论性的,并且对实际世界的要求的理解还不够。但是,只有反向工程技术被用来从可变性建模语言(如 Kconfig)中推断语义[3]

Kconfig 的完整 SAT 求解器

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

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