启动配置¶
- 作者:
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 # !ERROR! we can not re-define same key
如果要更新该值,则必须显式使用覆盖运算符 :=
。例如
foo = bar, baz
foo := qux
然后,qux
被分配给 foo
键。这对于通过添加(部分)自定义启动配置来覆盖默认值而不解析默认启动配置很有用。
如果要将该值作为数组成员附加到现有键,可以使用 +=
运算符。例如
foo = bar, baz
foo += qux
在这种情况下,键 foo
具有 bar
、baz
和 qux
。
此外,子键和一个值可以共存于父键下。例如,允许以下配置。
foo = value1
foo.bar = value2
foo := value3 # This will update foo's value.
请注意,由于没有语法可以将原始值直接放在结构化键下,因此必须在括号外定义它。例如
foo {
bar = value1
bar {
baz = value2
qux = value3
}
}
此外,键下的值节点的顺序是固定的。如果存在值和子键,则该值始终是键的第一个子节点。因此,如果用户首先指定子键,例如
foo.bar = value1
foo = value2
在程序(和 /proc/bootconfig)中,它将显示如下
foo = value2
foo.bar = value1
/proc/bootconfig¶
/proc/bootconfig 是启动配置的用户空间接口。与 /proc/cmdline 不同,此文件显示键值样式列表。每个键值对都以以下样式显示在每一行中
KEY[.WORDS...] = "[VALUE]"[,"VALUE2"...]
使用启动配置启动内核¶
有两种使用启动配置启动内核的选项:将启动配置附加到 initrd 镜像或将其嵌入到内核本身中。
将启动配置附加到 Initrd¶
由于默认情况下启动配置文件与 initrd 一起加载,因此它将添加到 initrd (initramfs) 镜像文件的末尾,并带有填充、大小、校验和和 12 字节的魔术字,如下所示。
[initrd][bootconfig][padding][size(le32)][checksum(le32)][#BOOTCONFIG\n]
大小和校验和字段是无符号 32 位小端值。
当启动配置添加到 initrd 镜像时,总文件大小将对齐到 4 字节。为了填充间隙,将添加空字符 (\0
)。因此,size
是启动配置文件 + 填充字节的长度。
Linux 内核解码内存中 initrd 镜像的最后一部分以获取启动配置数据。由于这种“piggyback”方法,只要引导加载程序传递正确的 initrd 文件大小,就无需更改或更新引导加载程序和内核镜像本身。如果万一引导加载程序传递了更大的大小,内核将无法找到启动配置数据。
为了执行此操作,Linux 内核提供了 tools/bootconfig 下的 bootconfig
命令,该命令允许管理员将配置文件应用或删除到/从 initrd 镜像。您可以使用以下命令构建它
# make -C tools/bootconfig
要将启动配置文件添加到 initrd 镜像,请如下运行 bootconfig(如果存在旧数据,则会自动删除)
# 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 文件的末尾查找启动配置。或者,构建内核时选择 CONFIG_BOOT_CONFIG_FORCE
Kconfig 选项。
将启动配置嵌入到内核中¶
如果不能使用 initrd,也可以通过 Kconfig 选项将启动配置文件嵌入到内核中。在这种情况下,您需要使用以下配置重新编译内核
CONFIG_BOOT_CONFIG_EMBED=y
CONFIG_BOOT_CONFIG_EMBED_FILE="/PATH/TO/BOOTCONFIG/FILE"
CONFIG_BOOT_CONFIG_EMBED_FILE
需要从源代码树或对象树到启动配置文件的绝对路径或相对路径。内核将它嵌入为默认启动配置。
就像将启动配置附加到 initrd 一样,您需要在内核命令行上使用 bootconfig
选项来启用嵌入的启动配置,或者,构建内核时选择 CONFIG_BOOT_CONFIG_FORCE
Kconfig 选项。
请注意,即使您设置了此选项,您也可以通过附加到 initrd 的另一个启动配置来覆盖嵌入的启动配置。
通过启动配置传递内核参数¶
除了内核命令行之外,启动配置还可用于传递内核参数。 kernel
键下的所有键值对将直接传递到内核 cmdline。此外,init
下的键值对将通过 cmdline 传递给 init 进程。这些参数与用户给定的内核 cmdline 字符串连接在一起,顺序如下,以便命令行参数可以覆盖启动配置参数(这取决于子系统如何处理参数,但通常,较早的参数将被后面的参数覆盖。)
[bootconfig params][cmdline params] -- [bootconfig init params][cmdline init params]
以下是内核/init 参数的启动配置文件示例。
kernel {
root = 01234567-89ab-cdef-0123-456789abcd
}
init {
splash
}
这将复制到内核 cmdline 字符串中,如下所示
root="01234567-89ab-cdef-0123-456789abcd" -- splash
如果用户给出一些其他命令行,例如,
ro bootconfig -- quiet
最终的内核 cmdline 将如下所示
root="01234567-89ab-cdef-0123-456789abcd" ro bootconfig -- splash quiet
配置文件限制¶
目前,最大配置大小为 32KB,总关键字(不是键值条目)必须小于 1024 个节点。注意:这不是条目的数量而是节点,一个条目必须消耗超过 2 个节点(一个关键字和一个值)。因此理论上,它最多可以有 512 个键值对。如果键平均包含 3 个单词,则它可以包含 256 个键值对。在大多数情况下,配置项的数量将少于 100 个条目并且小于 8KB,因此这将足够了。如果节点数超过 1024,即使文件大小小于 32KB,解析器也会返回错误。(请注意,此最大大小不包括填充空字符。)无论如何,由于 bootconfig 命令在将启动配置附加到 initrd 镜像时会验证它,因此用户可以在启动前注意到它。
Bootconfig APIs¶
用户可以查询或循环遍历键值对,也可以查找根(前缀)键节点并查找该节点下的键值对。
如果您有一个键字符串,您可以使用 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”的数组。
不需要锁定,因为初始化后,配置变为只读。如果您需要修改,则必须复制所有数据和键。
函数和结构¶
-
uint32_t xbc_calc_checksum(void *data, uint32_t size)¶
计算启动配置的校验和
参数
void *data
启动配置数据。
uint32_t size
启动配置数据的大小。
描述
计算启动配置数据的校验和值。该校验和将与 BOOTCONFIG_MAGIC 和大小一起用于将启动配置嵌入到 initrd 镜像中。
-
bool xbc_node_is_value(struct xbc_node *node)¶
测试节点是否为值节点
参数
struct xbc_node *node
一个 XBC 节点。
描述
测试 node 是否为值节点,如果是值节点则返回 true,否则返回 false。
-
bool xbc_node_is_key(struct xbc_node *node)¶
测试节点是否为键节点
参数
struct xbc_node *node
一个 XBC 节点。
描述
测试 node 是否为键节点,如果是键节点则返回 true,否则返回 false。
-
bool xbc_node_is_array(struct xbc_node *node)¶
测试节点是否为数组值节点
参数
struct xbc_node *node
一个 XBC 节点。
描述
测试 node 是否为数组值节点。
-
bool xbc_node_is_leaf(struct xbc_node *node)¶
测试节点是否为叶键节点
参数
struct xbc_node *node
一个 XBC 节点。
描述
测试 node 是否为叶键节点,即键节点并具有值节点或没有子节点。 如果是叶节点则返回 true,否则返回 false。 请注意,除了值节点之外,叶节点还可以具有子键节点。
-
const char *xbc_find_value(const char *key, struct xbc_node **vnode)¶
查找与键匹配的值
参数
const char *key
搜索键
struct xbc_node **vnode
XBC 值节点的容器指针。
描述
从整个 XBC 树中搜索键与 key 匹配的值,如果找到则返回该值。找到的值节点存储在 *vnode 中。请注意,对于仅键(非值)条目,这可以返回 0 长度的字符串并在 *vnode 中存储 NULL。
-
struct xbc_node *xbc_find_node(const char *key)¶
查找与键匹配的节点
参数
const char *key
搜索键
描述
从整个 XBC 树中搜索键与 key 匹配的(键)节点,如果找到则返回该节点。如果未找到,则返回 NULL。
-
struct xbc_node *xbc_node_get_subkey(struct xbc_node *node)¶
如果存在,则返回第一个子键节点
参数
struct xbc_node *node
父节点
描述
返回 node 的第一个子键节点。如果 node 没有子节点或只有值节点,则这将返回 NULL。
-
xbc_array_for_each_value¶
xbc_array_for_each_value (anode, value)
迭代数组上的值节点
参数
anode
一个 XBC 数组值节点
value
一个值
描述
迭代从 anode 开始的数组值节点和值。 这应与 xbc_find_value()
和 xbc_node_find_value()
一起使用,以便用户可以处理每个数组条目节点。
-
xbc_node_for_each_child¶
xbc_node_for_each_child (parent, child)
迭代子节点
参数
parent
一个 XBC 节点。
child
迭代的 XBC 节点。
描述
迭代 parent 的子节点。每个子节点都存储到 child。child 可以是值节点和子键节点的混合。
-
xbc_node_for_each_subkey¶
xbc_node_for_each_subkey (parent, child)
迭代子子键节点
参数
parent
一个 XBC 节点。
child
迭代的 XBC 节点。
描述
迭代 parent 的子键节点。每个子节点都存储到 child。child 仅是子键节点。
-
xbc_node_for_each_array_value¶
xbc_node_for_each_array_value (node, key, anode, value)
迭代给定的键的数组条目
参数
node
一个 XBC 节点。
key
在 node 下搜索的键字符串
anode
数组条目的迭代 XBC 节点。
value
数组条目的迭代值。
描述
迭代 node 下给定的 key 的数组条目。 每个数组条目节点都存储到 anode 和 value。 如果 node 没有 key 节点,则不执行任何操作。 请注意,即使找到的键节点只有一个值(不是数组),也会执行一次块。 但是,如果找到的键节点没有值(仅键节点),则不执行任何操作。 因此,请勿使用它来测试键值对是否存在。
-
xbc_node_for_each_key_value¶
xbc_node_for_each_key_value (node, knode, value)
迭代节点下的键值对
参数
node
一个 XBC 节点。
knode
迭代的键节点
value
迭代的值字符串
描述
迭代 node 下的键值对。 每个键节点和值字符串分别存储在 knode 和 value 中。
-
xbc_for_each_key_value¶
xbc_for_each_key_value (knode, value)
迭代键值对
参数
knode
迭代的键节点
value
迭代的值字符串
描述
迭代整个 XBC 树中的键值对。每个键节点和值字符串分别存储在 knode 和 value 中。
-
int xbc_node_compose_key(struct xbc_node *node, char *buf, size_t size)¶
组合 XBC 节点的完整键字符串
参数
struct xbc_node *node
一个 XBC 节点。
char *buf
用于存储键的缓冲区。
size_t size
buf 的大小。
描述
将 node 的完整键组合到 buf 中。返回存储在 buf 中的键的总长度。如果 node 为 NULL,则返回 -EINVAL;如果键深度大于最大深度,则返回 -ERANGE。
-
int xbc_get_info(int *node_size, size_t *data_size)¶
获取已加载的引导配置的信息
参数
int *node_size
用于存储节点数量的指针。
size_t *data_size
用于存储引导配置数据大小的指针。
描述
如果 node_size 不为 NULL,则将其中的已使用节点数量存储到 node_size 中;如果 data_size 不为 NULL,则将引导配置数据的大小存储到 data_size 中。如果引导配置已初始化,则返回 0;否则返回 -ENODEV。
-
struct xbc_node *xbc_root_node(void)¶
获取扩展引导配置的根节点
参数
void
无参数
描述
返回扩展引导配置的根节点地址。如果扩展引导配置未初始化,则返回 NULL。
-
int xbc_node_index(struct xbc_node *node)¶
获取 XBC 节点的索引
参数
struct xbc_node *node
用于获取索引的目标节点。
描述
返回 node 在 XBC 节点列表中的索引号。
-
struct xbc_node *xbc_node_get_parent(struct xbc_node *node)¶
获取父 XBC 节点
参数
struct xbc_node *node
一个 XBC 节点。
描述
返回 node 的父节点。如果该节点是树的顶层节点,则返回 NULL。
-
struct xbc_node *xbc_node_get_child(struct xbc_node *node)¶
获取子 XBC 节点
参数
struct xbc_node *node
一个 XBC 节点。
描述
返回 node 的第一个子节点。如果该节点没有子节点,则返回 NULL。
-
struct xbc_node *xbc_node_get_next(struct xbc_node *node)¶
获取下一个兄弟 XBC 节点
参数
struct xbc_node *node
一个 XBC 节点。
描述
返回 node 的下一个兄弟节点。如果该节点没有下一个兄弟节点,则返回 NULL。请注意,即使此函数返回 NULL,也并不意味着 node 没有兄弟节点。(您还需要检查父节点的子节点是否为 node。)
-
const char *xbc_node_get_data(struct xbc_node *node)¶
获取 XBC 节点的数据
参数
struct xbc_node *node
一个 XBC 节点。
描述
返回 node 的数据(始终是以 null 结尾的字符串)。如果该节点的数据无效,则发出警告并返回 NULL。
-
struct xbc_node *xbc_node_find_subkey(struct xbc_node *parent, const char *key)¶
查找与给定键匹配的子键节点
参数
struct xbc_node *parent
一个 XBC 节点。
const char *key
键字符串。
描述
在 parent 下搜索与 key 匹配的键节点。key 可以包含多个用“.”连接的单词。如果 parent 为 NULL,则从整棵树搜索节点。如果没有匹配的节点,则返回 NULL。
-
const char *xbc_node_find_value(struct xbc_node *parent, const char *key, struct xbc_node **vnode)¶
查找与给定键匹配的值节点
参数
struct xbc_node *parent
一个 XBC 节点。
const char *key
键字符串。
struct xbc_node **vnode
找到的 XBC 节点的容器指针。
描述
在 parent 下搜索其(父)键节点与 key 匹配的值节点,将其存储在 *vnode 中,并返回该值字符串。key 可以包含多个用“.”连接的单词。如果 parent 为 NULL,则从整棵树搜索节点。如果找到匹配的键,则返回该值字符串;如果没有匹配的节点,则返回 NULL。请注意,如果键没有值,则此函数返回长度为 0 的字符串,并在 *vnode 中存储 NULL。此外,如果该值是一个数组,则它将返回第一个条目的值。
-
int xbc_node_compose_key_after(struct xbc_node *root, struct xbc_node *node, char *buf, size_t size)¶
组合 XBC 节点的部分键字符串
参数
struct xbc_node *root
根 XBC 节点
struct xbc_node *node
目标 XBC 节点。
char *buf
用于存储键的缓冲区。
size_t size
buf 的大小。
描述
将 node 的部分键组合到 buf 中,该部分键从 root 之后开始(不包括 root)。如果 root 为 NULL,则此函数返回 node 的完整键字。返回存储在 buf 中的键的总长度。如果 node 为 NULL,或者 root 不是 node 的祖先,或者 root 是 node,则返回 -EINVAL;如果键深度大于最大深度,则返回 -ERANGE。此函数应与 xbc_find_node()
一起使用,以列出给定键下的所有(子)键。
-
struct xbc_node *xbc_node_find_next_leaf(struct xbc_node *root, struct xbc_node *node)¶
在给定节点下查找下一个叶节点
参数
struct xbc_node *root
XBC 根节点
struct xbc_node *node
开始查找的 XBC 节点。
描述
在 root 节点下搜索 node 的下一个叶节点(即终端键节点)(包括 root 节点本身)。如果找到下一个节点,则返回该节点;如果未找到下一个叶节点,则返回 NULL。
-
const char *xbc_node_find_next_key_value(struct xbc_node *root, struct xbc_node **leaf)¶
查找下一个键值对节点
参数
struct xbc_node *root
XBC 根节点
struct xbc_node **leaf
开始查找的 XBC 节点的容器指针。
描述
在 root 节点下搜索 *leaf 的下一个叶节点(即终端键节点)。如果找到下一个叶节点,则返回该值并更新 *leaf;如果未找到下一个叶节点,则返回 NULL。请注意,如果键没有值,或者该值是一个数组,则此函数返回长度为 0 的字符串,或第一个条目的值。
-
void _xbc_exit(bool early)¶
清除所有已解析的引导配置
参数
bool early
如果是在 budy 系统初始化之前调用此函数,则设置为 true。
描述
此函数清除内存中已解析的引导配置的所有数据结构。如果需要使用新的引导配置重用 xbc_init()
,则可以使用此函数。
-
int xbc_init(const char *data, size_t size, const char **emsg, int *epos)¶
解析给定的 XBC 文件并构建 XBC 内部树
参数
const char *data
引导配置文本原始数据
size_t size
data 的大小
const char **emsg
用于存储错误消息的 const char * 指针
int *epos
用于存储错误位置的 int 指针
描述
此函数解析 data 中的引导配置文本。size 必须小于 XBC_DATA_MAX。如果成功,则返回存储的节点数(>0);如果出现任何错误,则返回 -errno。在出错的情况下,emsg 将更新为错误消息,epos 将更新为错误位置,该错误位置是 buf 的字节偏移量。如果该错误不是解析器错误,则 epos 将为 -1。
注释¶
配置语法接受 shell 脚本样式的注释。以井号 (“#”) 开头直到换行符 (“\n”) 的注释将被忽略。
这将解析为如下
请注意,您不能在值和分隔符(
,
或;
)之间放置注释。这意味着以下配置存在语法错误