分类: LINUX
2014-01-21 22:18:42
引导期间的内核选项
Linux运行用户将内核配置选项传递给引导记录,引导记录再把选项传给内核,进而通过引导配置参数微调内核。在系统引导期间,将调用parse_args两次解析引导期间输入的配置参数。
parse_args函数解析的输入字符串参数是"变量名称=值"的形式,根据解析出的关键字并启用适当的处理函数。加载模块时,也会用到parse_args解析命令列参数。
注册关键字
内核组件利用定义在include/linux/init.h中的__setup宏注册关键字和相关联的处理函数:
__setup(string, function_handler)
string是关键字,而function_handler是相关联的处理函数。内核初始化时parse_args函数解析到关键字string是,就执行相关联的function_handler函数,但string必须以=字符结束,而任何跟在=字符后面的字符串将被作为函数输入参数传递给function_handler函数处理。这些参数存放在内存区块.setup.init中。
如arch\arm\kernel\process.c文件中,实例只需要输入关键字即可:
static int __init nohlt_setup(char *__unused)
{
hlt_counter = 1;
return 1;
}
static int __init hlt_setup(char *__unused)
{
hlt_counter = 0;
return 1;
}
__setup("nohlt", nohlt_setup);
__setup("hlt", hlt_setup);
在网络子系统的net/core/dev.c文件中,注册了netdev=关键字和对应的处理函数netdev_boot_setup函数:
int __init netdev_boot_setup(char *str)
{
int ints[5];
struct ifmap map;
str = get_options(str, ARRAY_SIZE(ints), ints);
if (!str || !*str)
return 0;
/* Save settings */
memset(&map, 0, sizeof(map));
if (ints[0] > 0)
map.irq = ints[1];
if (ints[0] > 1)
map.base_addr = ints[2];
if (ints[0] > 2)
map.mem_start = ints[3];
if (ints[0] > 3)
map.mem_end = ints[4];
/* Add new entry to the list */
return netdev_boot_setup_add(str, &map);
}
__setup("netdev=", netdev_boot_setup);
而在net/ethernet/eth.c文件中,ether=关键字通用被注册到了同一个处理函数netdev_boot_setup。
__setup("ether=", netdev_boot_setup);
当一端代码被编译为模块时,__setup宏会被护理,即定义为空操作。
#ifndef MODULE
……
#define __setup(str, fn) \
__setup_param(str, fn, fn, 0)
……
#else
……
#define __setup(str, func) /* nothing */
#endif
在init\main.c文件中start_kernel函数中:
asmlinkage void __init start_kernel(void)
{
……
parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
……
}
start_kernel两次调用parse_args以解析引导配置参数的原因在于引导期间的参数实际是分为两类的,每一次调用分别针对其中的一类:
#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)
在start_kernel函数中第二次调用parse_args函数时,会检查有module_param宏填入的模块选项,这些选项会存储在kernel_param数据结构中,module_param宏会确保这些数据结构都会被存放在特定的内存区块中(__param),有指针__start___param
和__stop___param限定。
__setup_start……__setup_end:这个区域会在引导阶段结束时释放掉,用户在运行期间不能看到或修改这些选项;
__start___param……__stop___param:这个区域不会被释放,其内容会被输出到/sys文件系统,这些选项可以显示给用户。
有__setup()和early_param()宏定义的关键字会被放入__setup_start……__setup_end区域,只是early_param宏定义的关键字会被设置early标记。
struct obs_kernel_param {
const char *str;
int (*setup_func)(char *);
int early;
};
str是关键字,setup_func是关联的处理函数,early则是early标记。__setup_param宏回家所有的obs_kernel_params实例存储到专用的内存区域内,这样比较容易遍历,__setup_start和__setup_end指针分别指向这个区域的开端和尾端。而当这些区域不再使用时也便于快速释放内存。