1. 程式人生 > >linux kernel的cmdline引數解析原理分析

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