在Linux的启动中除了可以通过命令行传递键值数据外还可以通过引导配置文件传递信息
引导配置文件
引导配置文件及基本介绍可以参考如下:
英文版本 Documentation\admin-guide\bootconfig.rst
中文版本 Documentation\translations\zh_CN\admin-guide\bootconfig.rst
解析文件
通过对文件的扫描可以使用一个 xbc_node对象的数组建立类似树的结构描述配置文件,同时遵循以下规则:
- 单个对象描述可以向上追溯父节点
- 单个对象可以向下搜素子节点
- 单个对象可以搜索兄弟节点
- 单个对象代表一个键或者值
- 值对象永远为键对象的第一个子节点
- 多值通过多个对象的父子关系串联起来
数据结构
struct xbc_node {
uint16_t next; #用此指向下一个兄弟的索引
uint16_t child; #用此指向一个子对象的索引
uint16_t parent; #用此指向父对象的索引
uint16_t data; #代表键/值对象的位置,区分该对象为键还是值需要参考最高bit的值
} __attribute__ ((__packed__));
#define XBC_KEY 0
#define XBC_VALUE (1 << 15)
解析程流
/* Need to setup xbc_data and xbc_nodes before call this. */
static int __init xbc_parse_tree(void)
{
char *p, *q;
int ret = 0, c;
last_parent = NULL;
p = xbc_data;
do {
q = strpbrk(p, "{}=+;:\n#");
if (!q) {
p = skip_spaces(p);
if (*p != '\0')
ret = xbc_parse_error("No delimiter", p);
break;
}
c = *q;
*q++ = '\0';
switch (c) {
case ':':
case '+':
if (*q++ != '=') {
ret = xbc_parse_error(c == '+' ?
"Wrong '+' operator" :
"Wrong ':' operator",
q - 2);
break;
}
fallthrough;
case '=':
ret = xbc_parse_kv(&p, q, c); #创建键值对
break;
case '{':
ret = xbc_open_brace(&p, q);
break;
case '#':
q = skip_comment(q);
fallthrough;
case ';':
case '\n':
ret = xbc_parse_key(&p, q); #插入一个键对象
break;
case '}':
ret = xbc_close_brace(&p, q);
break;
}
} while (!ret);
return ret;
}
解析过程主要思路为通过循环匹配引导配置文件的关键字做不同的逻辑处理
寻找父节点
/**
* xbc_node_get_parent() - Get the parent XBC node
* @node: An XBC node.
*
* Return the parent node of @node. If the node is top node of the tree,
* return NULL.
*/
struct xbc_node * __init xbc_node_get_parent(struct xbc_node *node)
{
return node->parent == XBC_NODE_MAX ? NULL : &xbc_nodes[node->parent];
}
搜索子节点
/**
* xbc_node_get_child() - Get the child XBC node
* @node: An XBC node.
*
* Return the first child node of @node. If the node has no child, return
* NULL.
*/
struct xbc_node * __init xbc_node_get_child(struct xbc_node *node)
{
return node->child ? &xbc_nodes[node->child] : NULL;
}
搜索兄弟节点
/**
* xbc_node_get_next() - Get the next sibling XBC node
* @node: An XBC node.
*
* Return the NEXT sibling node of @node. If the node has no next sibling,
* return NULL. Note that even if this returns NULL, it doesn't mean @node
* has no siblings. (You also has to check whether the parent's child node
* is @node or not.)
*/
struct xbc_node * __init xbc_node_get_next(struct xbc_node *node)
{
return node->next ? &xbc_nodes[node->next] : NULL;
}
判断对象数据类型
/**
* xbc_node_is_value() - Test the node is a value node
* @node: An XBC node.
*
* Test the @node is a value node and return true if a value node, false if not.
*/
static inline __init bool xbc_node_is_value(struct xbc_node *node)
{
return node->data & XBC_VALUE;
}
/**
* xbc_node_is_key() - Test the node is a key node
* @node: An XBC node.
*
* Test the @node is a key node and return true if a key node, false if not.
*/
static inline __init bool xbc_node_is_key(struct xbc_node *node)
{
return !xbc_node_is_value(node);
}
插入值对象
static inline __init struct xbc_node *xbc_add_child(char *data, uint32_t flag)
{
struct xbc_node *node = xbc_add_sibling(data, flag);
if (node)
last_parent = node;
return node;
}
static int __init xbc_parse_array(char **__v)
{
......................
do {
c = __xbc_parse_value(__v, &next);
if (c < 0)
return c;
node = xbc_add_child(*__v, XBC_VALUE);
if (!node)
return -ENOMEM;
*__v = next;
} while (c == ',');
node->child = 0;
return c;
}
通过last_parent 的帮助可以将值数组中下一个对象在树结构中不断的往下添加。