linux kernel的cmdline引數解析原理分析
利用工作之便,今天研究了kernel下cmdline引數解析過程,記錄在此,與大家共享,轉載請註明出處,謝謝。
Kernel 版本號:3.4.55
Kernel啟動時會解析cmdline,然後根據這些引數如console root來進行配置執行。
Cmdline是由bootloader傳給kernel,如uboot,將需要傳給kernel的引數做成一個tags連結串列放在ram中,將首地址傳給kernel,kernel解析tags來獲取cmdline等資訊。
Uboot傳參給kernel以及kernel如何解析tags可以看我的另一篇博文,連結如下:
http://blog.csdn.net/skyflying2012/article/details/35787971
今天要分析的是kernel在獲取到cmdline之後如何對cmdline進行解析。依據我的思路(時間順序,如何開始,如何結束),首先看kernel下2種引數的註冊。 第一種是kernel通用引數,如console=ttyS0,115200 root=/rdinit/init等。這裡以console為例。
第二種是kernel下各個driver中需要的引數,在寫driver中,如果需要一些啟動時可變引數。可以在driver最後加入module_param()來註冊一個引數,kernel啟動時由cmdline指定該引數的值。
這裡以drivers/usb/gadget/serial.c中的use_acm引數為例(這個例子有點偏。。因為最近在除錯usb虛擬串列埠)
一 kernel通用引數
對於這類通用引數,kernel留出單獨一塊data段,叫.ini.setup段。在arch/arm/kernel/vmlinux.lds中:
.init.data : { *(.init.data) *(.cpuinit.data) *(.meminit.data) *(.init.rodata) *(.cpuinit.rodata) *(.meminit.rodata) . = ALIGN(32); __dtb_star . = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .; __initcall_start = .; *(.initcallearly.init) __initcall0_start = .; *(.initcall0.init) *(.initcall0s.init) __initcall1_start = __con_initcall_start = .; *(.con_initcall.init) __con_initcall_end = .; __security_initcall_start = .; *(.security_initcall.init) __security_initcall_end = .; . = ALIGN(4); __initramfs_start = .; *(.init.ramfs) . = ALIGN(8); *(.init.ramfs.info) }
可以看到init.setup段起始__setup_start和結束__setup_end。
.init.setup段中存放的就是kernel通用引數和對應處理函式的對映表。在include/linux/init.h中
struct obs_kernel_param {
const char *str;
int (*setup_func)(char *);
int early;
};
/*
* Only for really core code. See moduleparam.h for the normal way.
*
* Force the alignment so the compiler doesn't space elements of the
* obs_kernel_param "array" too far apart in .init.setup.
*/
#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)
/* 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)
可以看出巨集定義__setup以及early_param定義了obs_kernel_param結構體,該結構體存放參數和對應處理函式,存放在.init.setup段中。
可以想象,如果多個檔案中呼叫該巨集定義,在連結時就會根據連結順序將定義的obs_kernel_param放到.init.setup段中。
以console為例,在/kernel/printk.c中,如下:
static int __init console_setup(char *str)
{
.......
}
__setup("console=", console_setup);
__setup巨集定義展開,如下
Static struct obs_kernel_param __setup_console_setup
__used_section(.init.setup) __attribute__((aligned((sizeof(long)))) = {
.name = “console=”,
.setup_func = console_setup,
.early = 0
}
__setup_console_setup編譯時就會連結到.init.setup段中,kernel執行時就會根據cmdline中的引數名與.init.setup段中obs_kernel_param的name對比。
匹配則呼叫console-setup來解析該引數,console_setup的引數就是cmdline中console的值,這是後面引數解析的大體過程了。
--------------------- 本文來自 kerneler_ 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/skyflying2012/article/details/41142801?utm_source=copy
--------------------- 本文來自 kerneler_ 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/skyflying2012/article/details/41142801?utm_source=copy
--------------------- 本文來自 kerneler_ 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/skyflying2012/article/details/41142801?utm_source=copy