Documentation / translations / zh_CN / admin-guide / bootconfig.rst


Based on kernel version 6.9. Page generated on 2024-05-14 10:02 EST.

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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/admin-guide/bootconfig.rst

:译者: 吴想成 Wu XiangCheng <bobwxc@email.cn>

========
引导配置
========

:作者: Masami Hiramatsu <mhiramat@kernel.org>

概述
====

引导配置扩展了现有的内核命令行,以一种更有效率的方式在引导内核时进一步支持
键值数据。这允许管理员传递一份结构化关键字的配置文件。

配置文件语法
============

引导配置文件的语法采用非常简单的键值结构。每个关键字由点连接的单词组成,键
和值由 ``=`` 连接。值以分号( ``;`` )或换行符( ``\n`` )结尾。数组值中每
个元素由逗号( ``,`` )分隔。::

  KEY[.WORD[...]] = VALUE[, VALUE2[...]][;]

与内核命令行语法不同,逗号和 ``=`` 周围允许有空格。

关键字只允许包含字母、数字、连字符( ``-`` )和下划线( ``_`` )。值可包含
可打印字符和空格,但分号( ``;`` )、换行符( ``\n`` )、逗号( ``,`` )、
井号( ``#`` )和右大括号( ``}`` )等分隔符除外。

如果你需要在值中使用这些分隔符,可以用双引号( ``"VALUE"`` )或单引号
( ``'VALUE'`` )括起来。注意,引号无法转义。

键的值可以为空或不存在。这些键用于检查该键是否存在(类似布尔值)。

键值语法
--------

引导配置文件语法允许用户通过大括号合并键名部分相同的关键字。例如::

 foo.bar.baz = value1
 foo.bar.qux.quux = value2

也可以写成::

 foo.bar {
    baz = value1
    qux.quux = value2
 }

或者更紧凑一些,写成::

 foo.bar { baz = value1; qux.quux = value2 }

在这两种样式中,引导解析时相同的关键字都会自动合并。因此可以追加类似的树或
键值。

相同关键字的值
--------------

禁止两个或多个值或数组共享同一个关键字。例如::

 foo = bar, baz
 foo = qux  # !错误! 我们不可以重定义相同的关键字

如果你想要更新值,必须显式使用覆盖操作符 ``:=`` 。例如::

 foo = bar, baz
 foo := qux

这样 ``foo`` 关键字的值就变成了 ``qux`` 。这对于通过添加(部分)自定义引导
配置来覆盖默认值非常有用,免于解析默认引导配置。

如果你想对现有关键字追加值作为数组成员,可以使用 ``+=`` 操作符。例如::

 foo = bar, baz
 foo += qux

这样, ``foo`` 关键字就同时拥有了 ``bar`` , ``baz`` 和 ``qux`` 。

此外,父关键字下可同时存在值和子关键字。
例如,下列配置是可行的。::

 foo = value1
 foo.bar = value2
 foo := value3 # 这会更新foo的值。

注意,裸值不能直接放进结构化关键字中,必须在大括号外定义它。例如::

 foo {
     bar = value1
     bar {
         baz = value2
         qux = value3
     }
 }

同时,关键字下值节点的顺序是固定的。如果值和子关键字同时存在,值永远是该关
键字的第一个子节点。因此如果用户先指定子关键字,如::

 foo.bar = value1
 foo = value2

则在程序(和/proc/bootconfig)中,它会按如下显示::

 foo = value2
 foo.bar = value1

注释
----

配置语法接受shell脚本风格的注释。注释以井号( ``#`` )开始,到换行符
( ``\n`` )结束。

::

 # comment line
 foo = value # value is set to foo.
 bar = 1, # 1st element
       2, # 2nd element
       3  # 3rd element

会被解析为::

 foo = value
 bar = 1, 2, 3

注意你不能把注释放在值和分隔符( ``,`` 或 ``;`` )之间。如下配置语法是错误的::

 key = 1 # comment
       ,2


/proc/bootconfig
================

/proc/bootconfig是引导配置的用户空间接口。与/proc/cmdline不同,此文件内容以
键值列表样式显示。
每个键值对一行,样式如下::

 KEY[.WORDS...] = "[VALUE]"[,"VALUE2"...]


用引导配置引导内核
==================

用引导配置引导内核有两种方法:将引导配置附加到initrd镜像或直接嵌入内核中。

*initrd: initial RAM disk,初始内存磁盘*

将引导配置附加到initrd
----------------------

由于默认情况下引导配置文件是用initrd加载的,因此它将被添加到initrd(initramfs)
镜像文件的末尾,其中包含填充、大小、校验值和12字节幻数,如下所示::

 [initrd][bootconfig][padding][size(le32)][checksum(le32)][#BOOTCONFIG\n]

大小和校验值为小端序存放的32位无符号值。

当引导配置被加到initrd镜像时,整个文件大小会对齐到4字节。空字符( ``\0`` )
会填补对齐空隙。因此 ``size`` 就是引导配置文件的长度+填充的字节。

Linux内核在内存中解码initrd镜像的最后部分以获取引导配置数据。由于这种“背负式”
的方法,只要引导加载器传递了正确的initrd文件大小,就无需更改或更新引导加载器
和内核镜像本身。如果引导加载器意外传递了更长的大小,内核将无法找到引导配置数
据。

Linux内核在tools/bootconfig下提供了 ``bootconfig`` 命令来完成此操作,管理员
可以用它从initrd镜像中删除或追加配置文件。你可以用以下命令来构建它::

 # make -C tools/bootconfig

要向initrd镜像添加你的引导配置文件,请按如下命令操作(旧数据会自动移除)::

 # tools/bootconfig/bootconfig -a your-config /boot/initrd.img-X.Y.Z

要从镜像中移除配置,可以使用-d选项::

 # tools/bootconfig/bootconfig -d /boot/initrd.img-X.Y.Z

然后在内核命令行上添加 ``bootconfig`` 告诉内核去initrd文件末尾寻找内核配置。

将引导配置嵌入内核
------------------

如果你不能使用initrd,也可以通过Kconfig选项将引导配置文件嵌入内核中。在此情
况下,你需要用以下选项重新编译内核::

 CONFIG_BOOT_CONFIG_EMBED=y
 CONFIG_BOOT_CONFIG_EMBED_FILE="/引导配置/文件/的/路径"

``CONFIG_BOOT_CONFIG_EMBED_FILE`` 需要从源码树或对象树开始的引导配置文件的
绝对/相对路径。内核会将其嵌入作为默认引导配置。

与将引导配置附加到initrd一样,你也需要在内核命令行上添加 ``bootconfig`` 告诉
内核去启用内嵌的引导配置。

注意,即使你已经设置了此选项,仍可用附加到initrd的其他引导配置覆盖内嵌的引导
配置。

通过引导配置传递内核参数
========================

除了内核命令行,引导配置也可以用于传递内核参数。所有 ``kernel`` 关键字下的键
值对都将直接传递给内核命令行。此外, ``init`` 下的键值对将通过命令行传递给
init进程。参数按以下顺序与用户给定的内核命令行字符串相连,因此命令行参数可以
覆盖引导配置参数(这取决于子系统如何处理参数,但通常前面的参数将被后面的参数
覆盖)::

 [bootconfig params][cmdline params] -- [bootconfig init params][cmdline init params]

如果引导配置文件给出的kernel/init参数是::

 kernel {
   root = 01234567-89ab-cdef-0123-456789abcd
 }
 init {
  splash
 }

这将被复制到内核命令行字符串中,如下所示::

 root="01234567-89ab-cdef-0123-456789abcd" -- splash

如果用户给出的其他命令行是::

 ro bootconfig -- quiet

则最后的内核命令行如下::

 root="01234567-89ab-cdef-0123-456789abcd" ro bootconfig -- splash quiet


配置文件的限制
==============

当前最大的配置大小是32KB,关键字总数(不是键值条目)必须少于1024个节点。
注意:这不是条目数而是节点数,条目必须消耗超过2个节点(一个关键字和一个值)。
所以从理论上讲最多512个键值对。如果关键字平均包含3个单词,则可有256个键值对。
在大多数情况下,配置项的数量将少于100个条目,小于8KB,因此这应该足够了。如果
节点数超过1024,解析器将返回错误,即使文件大小小于32KB。(请注意,此最大尺寸
不包括填充的空字符。)
无论如何,因为 ``bootconfig`` 命令在附加启动配置到initrd映像时会验证它,用户
可以在引导之前注意到它。


引导配置API
===========

用户可以查询或遍历键值对,也可以查找(前缀)根关键字节点,并在查找该节点下的
键值。

如果您有一个关键字字符串,则可以直接使用 xbc_find_value() 查询该键的值。如果
你想知道引导配置里有哪些关键字,可以使用 xbc_for_each_key_value() 迭代键值对。
请注意,您需要使用 xbc_array_for_each_value() 访问数组的值,例如::

 vnode = NULL;
 xbc_find_value("key.word", &vnode);
 if (vnode && xbc_node_is_array(vnode))
    xbc_array_for_each_value(vnode, value) {
      printk("%s ", value);
    }

如果您想查找具有前缀字符串的键,可以使用 xbc_find_node() 通过前缀字符串查找
节点,然后用 xbc_node_for_each_key_value() 迭代前缀节点下的键。

但最典型的用法是获取前缀下的命名值或前缀下的命名数组,例如::

 root = xbc_find_node("key.prefix");
 value = xbc_node_find_value(root, "option", &vnode);
 ...
 xbc_node_for_each_array_value(root, "array-option", value, anode) {
    ...
 }

这将访问值“key.prefix.option”的值和“key.prefix.array-option”的数组。

锁是不需要的,因为在初始化之后配置只读。如果需要修改,必须复制所有数据和关键字。


函数与结构体
============

相关定义的kernel-doc参见:

 - include/linux/bootconfig.h
 - lib/bootconfig.c