From http://blog.chinaunix.net/u3/111928/showart_2184040.html
參考文章:非常經典的文章
http://book.opensourceproject.org.cn/embedded/embeddedprime/index.html?page=opensource/0136130550/ch05lev1sec3.html
本文原創
,轉貼請註明出處,謝謝!
0
處理
模型
Linux kernel
的啓動包
括很多組件的初始化和相關配置,這些配置參數一般是通過command line
進
行配置的。在進行後續分析之前,先來理解一下command line
的處理
模型:
要處理的對象是一個字符串,其中包含了各種配置信息,通常各個配置之間通過空格進行分離,每個配置的表達形式是
如:param=value1,value2
或者很簡單就是一個rw
。
那麼kernel
就需要提供對這些
參數進行處理的處理函數列表。根據參數的作用以及執行期的先後不同,這些處理函數被定義到不同的段中。針對每一個參數,Kernel
都會到相應的段中查找相應的處理函數,最終進行各個組件的配置。
1
配置
格式
常見的配置格式如:
console=ttySAC0,115200 root=nfs nfsroot=192.168.1.9:/source/rootfs
initrd=0x10800000,0x14af47
|
2
配置
方式
由bootloader
進行參數配
置,command line
將做爲atag_list
的一個節點傳遞到Kernel
。
通過make menuconfig
進
行配置:運行後配置boot options->Default kernel
command string
。該配置將被靜態編譯到Kernel
中,
通過變量default_command_line
訪問。
3
解
析配置
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
相關變量
相關的變量包括:
default_command_line
:
保存memuconfig
配置的參
數,如果bootloader
傳入了命令行參數,那麼這個新的配置將被更新到
該變量中。
boot_command_line
:
存在於.init.data
段。最
初是default_command_line
的拷貝。
command_line
:
存在於.init.data
段。在parse_cmdline()
中被賦值,數據來源是default_command_line
。
saved_command_line
:
用於保存沒有處理過的命令行參數,是boot_caommand_line
的
拷貝。
static_command_line
:
是command_line
的拷
貝。
3.2.2
主要函數
函數名稱:parse_cmdline()
操作數據:default_command_line
。
函數列表:
.early_param.init
段
(在__early_begin
和__early_end
之間)。
函數功能:
依據函數列表對default_command_line
中
的參數進行處理。
函數名稱:parse_early_param()
操作數據:boot_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)
)
|
函數名稱:parse_args()
操作數據: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()
中已
經處理的操作,那麼這裏不再處理;如果是查找到的條目中沒有操作函數,那麼這表示是過時的數據定義(有些早期的代碼,沒有定義這個函數);如果不是以上兩
種情形,那麼利用找到的函數對參數進行處理。