前兩天調試加載文件系統時,由於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) {
ignored\n", p->str);printk(KERN_WARNING "Parameter %s is obsolete
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參數,從而成功啓動文件系統。
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>