本文共 4051 字,大约阅读时间需要 13 分钟。
处理 模型
Linux kernel 的启动包 括很多组件的初始化和相关配置,这些配置参数一般是通过command line 进 行配置的。在进行后续分析之前,先来理解一下command line 的处理 模型: 要处理的对象是一个字符串,其中包含了各种配置信息,通常各个配置之间通过空格进行分离,每个配置的表达形式是 如:param=value1,value2 或者很简单就是一个rw 。 那么kernel 就需要提供对这些 参数进行处理的处理函数列表。根据参数的作用以及执行期的先后不同,这些处理函数被定义到不同的段中。针对每一个参数,Kernel 都会到相应的段中查找相应的处理函数,最终进行各个组件的配置。 配置 格式
console=ttySAC0,115200 root=nfs nfsroot=192.168.1.9:/source/rootfs initrd=0x10800000,0x14af47 |
配置 方式
动 态配置
由bootloader 进行参数配 置,command line 将做为atag_list 的一个节点传递到Kernel 。 静态配置
通过make menuconfig 进 行配置:运行后配置boot options->Default kernel command string 。该配置将被静态编译到Kernel 中, 通过变量default_command_line 访问。 解 析配置
3.1 相关定义
根据执行的先后顺序,可以将处理函数分为三个大类,他们分别存在于下面三个段中(参考top/arch/arm/kernel/vmlinux.lds ): __setup_start = .; *(.init.setup) __setup_end = .; __early_begin = .; *(.early_param.init) __early_end = .; __start___param = .; *(__param) __stop___param = .; |
这 三个段内存储的不是参数,而是command line 参数所需要的处理函 数。 段
“.early_param.init ” 所定义的处理相对靠前一些,它所处理的参数例如:initrd= ,cachepolicy= ,nocache , nowb , ecc= , vmalloc= , mem= ,等等。 这些处理函数是通过__early_param 宏 来定义的,例如:
static void __init early_initrd(char **p) { …… } __early_param("initrd=", early_initrd); |
对于宏__early_param , 可以在top/arch/arm/include/asm/Setup.h 中 找到如下定义: struct early_params { const char *arg; void (*fn)(char **p); }; #define __early_param(name,fn) / static struct early_params __early_##fn __used / __attribute__((__section__(".early_param.init"))) = { name, fn } |
“.init.setup ”定义的 处理则要靠后一些,它所处理的参数例如:nfsroot= , ip= ,等等。 这些处理函数是通过__setup 宏 来定义的,例如: static int __init nfs_root_setup(char *line) { …… } __setup("nfsroot=", nfs_root_setup); |
对于宏__setup , 可以在top/include/linux/Init.h 中看到: #define __setup_param(str, unique_id, fn, early) / static char __setup_str_##unique_id[] __initdata __aligned(1) = str; / static struct obs_kernel_param __setup_##unique_id / __used __section(.init.setup) / __attribute__((aligned((sizeof(long))))) / = { __setup_str_##unique_id, fn, early } #define __setup(str, fn) / __setup_param(str, fn, fn, 0) /* NOTE: fn is as per module_param, not __setup! Emits warning if fn * returns non-zero. */ #define early_param(str, fn) / __setup_param(str, fn, fn, 1) |
注意看的话,可以看到还有一个宏 early_param , 它与宏__setup 的定义相似,只不过最后一个宏参数是1 而不是0 。1 表示需要提前处理的参数。 段
这个段中保存的是build-in 类 型module 的配置参数。该宏直接用来修饰需要的变量。
3.2.1 相关变量
保存memuconfig 配置的参 数,如果bootloader 传入了命令行参数,那么这个新的配置将被更新到 该变量中。 存在于.init.data 段。最 初是default_command_line 的拷贝。 存在于.init.data 段。在parse_cmdline() 中被赋值,数据来源是default_command_line 。 用于保存没有处理过的命令行参数,是boot_caommand_line 的 拷贝。 3.2.2 主要函数
操作数据:default_command_line 。 函数列表: .early_param.init 段 (在__early_begin 和__early_end 之间)。 函数功能: 依据函数列表对default_command_line 中 的参数进行处理。 函数列表: .init.setup 段 中(__setup_start 和__setup_end 之间),主要是通过宏early_param 定 义的部分。 函数功能: 依据函数列表对boot_command_line 中 的参数进行处理。 注意parse_one() 的第四 个入参是0 ,而且第五个参数是NULL 。这里没有给出参数队列,不会对boot_command_line 的 每个参数在参数队列中进行对比查找,而是直接在do_early_param() 中 进行条件判断,如果满足下面的条件,那么对该参数进行对应的操作: if ((p->early && strcmp(param, p->str) == 0) || (strcmp(param, "console") == 0 && strcmp(p->str, "earlycon") == 0) ) |
操作数据:static_command_line 。 函数列表: __param 段 (__start___param 和__stop___param 之间)。 函数功能: 该操作将依据函数列表,对static_command_line 中 的参数进行相应的操作。这个操作在parse_one() 的第一部分代码完 成: for (i = 0; i < num_params; i++) { if (parameq(param, params[i].name)) { DEBUGP("They are equal! Calling %p/n", params[i].set); return params[i].set(val, ¶ms[i]); } } |
接下来对于不被这个列表所支持的参数,将在unknown_bootoption() 中 进行处理。在unknown_bootoption() 中主要是obsolete_checksetup() 的操作。 函数名称:obsolete_checksetup() 操作数据:static_command_line 。 函数列表: .init.setup 段 中(__setup_start 和__setup_end 之间),主要是通过宏__setup 定 义的部分。 函数功能: 该操作将依据函数列表,对static_command_line 中 的参数进行相应的操作。如果是在parse_early_param() 中已 经处理的操作,那么这里不再处理;如果是查找到的条目中没有操作函数,那么这表示是过时的数据定义(有些早期的代码,没有定义这个函数);如果不是以上两 种情形,那么利用找到的函数对参数进行处理。 图示
转自:http://blog.chinaunix.net/u3/111928/showart_2184040.html
转载地址:http://uijhb.baihongyu.com/