内核启动时怎么处理启动参数

转自:

http://blog.csdn.net/zhenwenxian/article/details/5988729

Linux允许用户通过bootloader传递内核配置选项给内核,例如

console=ttyS1,115200n8 androidboot.hardware=eee_701 vga=788

内核在初始化过程中调用parse_args函数对这些选项进行解析,并调用相应的处理函数。

 

内核启动时怎么处理启动参数的了:通过__setup宏定义obs_kernel_param结构变量都被放入.init.setup段中,这样一来实际是使.init.setup段变成一张表,Kernel在处理每一个启动参数时,都会来查找这张表,与每一个数据项中的成员str进行比较,如果完全相同,就会调用该数据项的函数指针成员setup_func所指向的函数(该函数是在使用__setup宏定义该变量时传入的函数参数),并将启动参数如root=后面的内容传给该处理函数。

精髓是利用__setup宏建立了字符和对应处理函数集合的表,parse_args函数依次将命令行参数与这个表比较,找到匹配了就执行对应的处理函数。

 

__setup宏的来源及使用

内核组件用__setup宏来注册关键字及相关联的任何文本都会作为输入传给

 

function_handler。

 

不同的内核选项可以关联相同的处理函数,比如内核选项netdev和ether都关联了netdev_boot_setup函数。

理。

两次解析

相应于__setup宏和early_param宏两种注册形式,内核在初始化时,调用了两次parse_args函数进行解析。

parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param,
    __stop___param - __start___param,
           &unknown_bootoption);

parse_args的第一次调用就在parse_early_param函数里面,为什么会出现两次调用parse_args的情况?这是因为内核选项又分成了两种,就像现实世界中的我们,一种是普普通通的,一种是有特权的,有特权的需要在普通选项之前进行处理。

使用early_param声明的那些选项就会首先由parse_early_param去解析。

 

  下面的例子来自于init/do_mounts.c,其中root_dev_setup作为处理程序被注册给“root=”关键字:
            __setup("root=", root_dev_setup);

比如我们在启动向参数终有

  noinitrd root=/dev/mtdblock2 console=/linuxrc

 setup_arch解释时会发现root=/dev/mtdblock2,然后它就会调用root_dev_setup

static int __init root_dev_setup(char *line)

{

       strlcpy(saved_root_name, line, sizeof(saved_root_name));

       return 1;

}

我们容易看出,这个函数就是给saved_root_name赋值,saved_root_name这个全局变量非常重要,

比如printk。cstatic int __init console_setup(char *str)
{
 char buf[sizeof(console_cmdline[0].name) + 4]; /* 4 for index */
 char *s, *options, *brl_options = NULL;
 int idx;

#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
 if (!memcmp(str, "brl,", 4)) {
  brl_options = "";
  str += 4;
 } else if (!memcmp(str, "brl=", 4)) {
  brl_options = str + 4;
  str = strchr(brl_options, ',');
  if (!str) {
   printk(KERN_ERR "need port name after brl=/n");
   return 1;
  }
  *(str++) = 0;
 }
#endif

 /*
  * Decode str into name, index, options.
  */
 if (str[0] >= '0' && str[0] <= '9') {
  strcpy(buf, "ttyS");
  strncpy(buf + 4, str, sizeof(buf) - 5);
 } else {
  strncpy(buf, str, sizeof(buf) - 1);
 }
 buf[sizeof(buf) - 1] = 0;
 if ((options = strchr(str, ',')) != NULL)
  *(options++) = 0;
#ifdef __sparc__
 if (!strcmp(str, "ttya"))
  strcpy(buf, "ttyS0");
 if (!strcmp(str, "ttyb"))
  strcpy(buf, "ttyS1");
#endif
 for (s = buf; *s; s++)
  if ((*s >= '0' && *s <= '9') || *s == ',')
   break;
 idx = simple_strtoul(s, NULL, 10);
 *s = 0;

 __add_preferred_console(buf, idx, options, brl_options);
 console_set_on_cmdline = 1;
 return 1;
}
__setup("console=", console_setup);


如何增加命令行参数处理


ARM linux 系统通过bootable通过命令行参数传给内核kernel,内核启动时读出命令行参数cmdline,通过start_kernel对命令行处理。

如果为了实现自己的特殊功能,通过bootloader传参数给内核,对内核进行特殊的操作,就要增加代码。首先在bootloader要在命令行增加对应的参数项。另外还要改内核增加对这个参数的处理函数。比如增加一个参数,用来设置开机模式。在bootloader的命令行增加boot_mode=test。

在内核增加

 int saved_boot_mode;

static int __init boot_mode_setup(char *str)
{
 char buf[sizeof(console_cmdline[0].name) + 4]; /* 4 for index */
 char *s, *options, *brl_options = NULL;
 if (!strcmp(str, "test"))

saved_boot_mode=str;

    
   return 1;
}
__setup("boot_mode=", boot_mode_setup);

怎样通过__setup宏就将参数boot_mode和对应处理函数加入到了init。tab 区,执行start_kernel的时候对bootloader的命令行进行解析,解析到字符boot_mode就执行boot_mode_setup函数。

 

这样内核就可以知道命令行参数boot_mode的内容了。


你可能感兴趣的:(kernel)