Linux Command Line 解析


 

處理模型

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 配置方式

2.1 Bootloader動態配置

bootloader 進行參數配置,command line 將做為atag_list 的一個節點傳遞到Kernel

2.2 Kernel 靜態配置

通過make menuconfig 進行配置:運行后配置boot options->Default kernel command string 。該配置將被靜態編譯到Kernel 中,通過變量default_command_line 訪問。

解析配置

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 參數所需要的處理函數。

3.1.1 .early_param.init

.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 而不是01 表示需要提前處理的參數。

3.1.3 __param

這個段中保存的是build-in 類型module 的配置參數。該宏直接用來修飾需要的變量。

3.2  解析

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, &params[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() 中已經處理的操作,那么這里不再處理;如果是查找到的條目中沒有操作函數,那么這表示是過時的數據定義(有些早期的代碼,沒有定義這個函數);如果不是以上兩種情形,那么利用找到的函數對參數進行處理。

3.2.3圖示



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM