linux2.6内核,如何指定内核启动参数

来源:互联网 发布:商城类app源码 编辑:程序博客网 时间:2024/06/05 08:00

前两天调试加载文件系统时,由于cfe中参数写不进去,直接在内核中写死参数,碰到一个问题:
static int __init init_setup(char *str)
{

unsigned int i;


execute_command = str;

/*

* In case LILO is going to boot us with default command line,

* it prepends "auto" before the whole cmdline which makes

* the shell think it should execute a script with such name.

* So we ignore all arguments entered _before_ init=... [MJ]

*/

for (i = 1; i < MAX_INIT_ARGS; i++)

argv_init[i] = NULL;


return 1;

}
__setup("init=", init_setup);



对于如上的示例代码来说, __setup函数起什么作用? 
我们就先来看看吧,首先从执行函数看看:
init \ main.c :
static int __init obsolete_checksetup(char *line)
{

struct obs_kernel_param *p;

int had_early_param = 0;


p = __setup_start;

do {

int n = strlen(p->str);

if (!strncmp(line, p->str, n)) {

if (p->early) {

/* Already done in parse_early_param?

* (Needs exact match on param part).

* Keep iterating, as we can have early

* params and __setups of same names 8( */

if (line[n] == '\0' || line[n] == '=')

had_early_param = 1;

} else if (!p->setup_func) {

printk(KERN_WARNING "Parameter %s is obsolete

 ignored\n", p->str);

return 1;

} else if (p->setup_func(line + n))

return 1;

}

p++;

} while (p < __setup_end);


return had_early_param;

}


这里的意思是从__setup_start开始处到__setup_end处中查找一个数据结构,这个数据结构中有str与setup_func这两个数据成员变量. 只要与这里面的str与输入的参数字符串相匹配,就会调用个这个字符串后面所指的内容, 
比如
__setup("init=", init_setup);
表示在启动linux kernel 时如果有这么一个参数: "init=/bin/init",那么内核就会默认调用 init_setup 函数,以"/bin/init"作为参数进行执行。


那么我们有必要了解一下整个过程:
__setup() 是一个宏定义,在include/linux/init.h这个文件中,__setup 定义如下:
struct obs_kernel_param {
const char *str;
int (*setup_func)(char *);
int early;
};



#define __setup_param(str, unique_id, fn, early) \
static const char __setup_str_##unique_id[] __initconst\
__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)



上面有一个非常重要的字段:__section(.init.setup) 这个是与elf格式相关的一个内容,那么有必要看下 ld 链接器的脚本文件:
arch\mips\kernel\vmlinux.lds 片段:


 .init.setup : {
  __setup_start = .;
  *(.init.setup)
  __setup_end = .;
 }


__setup_start是一个节的开始,而__setup_end是一个节的结束,这个节的名称是.init.setup
其中的内容就是一个数据结构,即一个str 及 setup_func ,2.6版本又增加了一个 early


那么就来看看生成的elf文件:
readelf -a vmlinux >>/home/vmlinux_elf生成一个描述elf结构的文件
[Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
...
[15] .init.data        PROGBITS        804eba8c 4eba8c 0082ba 00  WA  0   0  4
[16] .init.setup       PROGBITS        804f3d50 4f3d50 000378 00  WA  0   0  4
[17] .initcall.init    PROGBITS        804f40c8 4f40c8 000354 00  WA  0   0  4
表明.init.setup 数据空间范围:0x804f3d50 - 0x804f40c8加载的的虚拟地址范围
0x4f3d50 - 0x4f40c8 在vmlinux实现数据空间地址范围


init_setup 映射的内存地址:
34: 804c903c    48 FUNC    LOCAL  DEFAULT   14 init_setup
前面的地址 804c903c 是其虚拟映射地址


使用命令:
od --address-radix=x -t x4 vmlinux |grep -A 20 4f3d50 |head -20 | grep 804c903c
4f3d60 804c903c 00000000 804f397e 804c90ac
表明其处理子符串内存地址是 804f397e
与前面得到的.init.data节在内存映射中的位置0x804eba8c 相减就是 0x7EF2,与.data.init在文件中的偏移量0x4eba8c相加就得到4F397E ,这样用 
od --address-radix=x -a vmlinux-2.4.20-8 |grep -A 2 4F3970 


其vmlinux内容如下所示:


ok, init= 的值就在这里,不过这里是个 null 表明没有传递参数值而已。


=======================================================================
其实上面的一些分析都太无聊了,那么就列举下具体的解决问题方案吧:
文件系统mount失败


VFS: Cannot open root device "<NULL>" or unknown-block(8,1)
Please append a correct "root=" boot option; here are the available partitions:
1f01          261120 mtdblock1 (driver?)
1f02            4096 mtdblock2 (driver?)
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(8,1)
根文件系统没有挂载成功,找找原因看看

解决方案:
由于cfe的参数无法传给kernel,导致启动参数为空。
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 65024
Kernel command line: 
PID hash table entries: 1024 (order: 10, 4096 bytes)


从这里也可以看出来,ok, 那么就将这些参数也写死好了,修改如下: arch/mips/kernel/setup.c (具体修改平台下的文件)

static void __init arch_mem_init(char **cmdline_p)
{

...

strlcpy(command_line,"root=/dev/nfs nfsroot=192.168.44.103:/home/game/nfsdir ip=192.168.44.81::::frv:eth0:on rw ,nolock init=/init",512);

//动态获取ip地址

//strlcpy(command_line,"root=/dev/nfs nfsroot=192.168.44.103:/home/game/nfsdir ip=dhcp rw ,nolock init=/init",512);

*cmdline_p = command_line;

}


以上参数设定可参考linux文档:\Documentation\frv\booting.txt


重点指定 command_line 这个参数
如此即可指定网络文件系统nfs参数,从而成功启动文件系统。


[cpp] view plaincopyprint?
  1. <PRE></PRE>  
  2. <PRE></PRE>  
  3. <PRE></PRE>  
  4. <PRE></PRE>  
  5. <PRE></PRE>  
  6. <PRE></PRE>  
0 0
原创粉丝点击