深入理解Linux網路技術內幕-組件初始化的核心基礎架構(一)

來源:互聯網
上載者:User

引導期間的核心選項

Linux運行使用者將核心配置選項傳遞給引導記錄,引導記錄再把選項傳給核心,進而通過引導配置參數微調核心。在系統引導期間,將調用parse_args兩次解析引導期間輸入的配置參數。

parse_args函數解析的輸入字串參數是"變數名稱=值"的形式,根據解析出的關鍵字並啟用適當的處理函數。載入模組時,也會用到parse_args解析命令列參數。

註冊關鍵字

核心組件利用定義在include/linux/init.h中的__setup宏註冊關鍵字和相關聯的處理函數:

__setup(string, function_handler)   

string是關鍵字,而function_handler是相關聯的處理函數。核心初始化時parse_args函數解析到關鍵字string是,就執行相關聯的function_handler函數,但string必須以=字元結束,而任何跟在=字元後面的字串將被作為函數輸入參數傳遞給function_handler函數處理。這些參數存放在記憶體區塊.setup.init中。

如arch\arm\kernel\process.c檔案中,執行個體只需要輸入關鍵字即可:

static int __init nohlt_setup(char *__unused) 



    hlt_counter = 1; 

    return 1; 

}

static int __init hlt_setup(char *__unused) 



    hlt_counter = 0; 

    return 1; 

}

__setup("nohlt", nohlt_setup); 

__setup("hlt", hlt_setup);

在網路子系統的net/core/dev.c檔案中,註冊了netdev=關鍵字和對應的處理函數netdev_boot_setup函數:

int __init netdev_boot_setup(char *str) 



    int ints[5]; 

    struct ifmap map;

    str = get_options(str, ARRAY_SIZE(ints), ints); 

    if (!str || !*str) 

        return 0;

    /* Save settings */ 

    memset(&map, 0, sizeof(map)); 

    if (ints[0] > 0) 

        map.irq = ints[1]; 

    if (ints[0] > 1) 

        map.base_addr = ints[2]; 

    if (ints[0] > 2) 

        map.mem_start = ints[3]; 

    if (ints[0] > 3) 

        map.mem_end = ints[4];

    /* Add new entry to the list */ 

    return netdev_boot_setup_add(str, &map); 

}

__setup("netdev=", netdev_boot_setup);

而在net/ethernet/eth.c檔案中,ether=關鍵字通用被註冊到了同一個處理函數netdev_boot_setup。

__setup("ether=", netdev_boot_setup);

當一端代碼被編譯為模組時,__setup宏會被護理,即定義為空白操作。

#ifndef MODULE

……

#define __setup(str, fn)                    \ 

    __setup_param(str, fn, fn, 0)

……

#else

……

#define __setup(str, func)             /* nothing */

#endif

在init\main.c檔案中start_kernel函數中:

asmlinkage void __init start_kernel(void) 

{

              ……

     parse_early_param(); 

     parse_args("Booting kernel", static_command_line, __start___param, 

            __stop___param - __start___param, 

           &unknown_bootoption);

            ……

}

start_kernel兩次調用parse_args以解析引導配置參數的原因在於引導期間的參數實際是分為兩類的,每一次調用分別針對其中的一類:

  • 預設選項:多數參數都屬於這一類,這些參數由__setup宏定義,有parse_args第二次被調用時處理
  • 早期選項:核心引導期間,有些選項需要更早處理。這些參數是由early_param宏代替__setup宏定義的,這些選項由parse_early_params負責。early_param和__setup宏的唯一區別是early_param會設定一個特殊標記,使核心能區分這兩種情況,而此標記是obs_kernel_param資料結構的一部分。

#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)

在start_kernel函數中第二次調用parse_args函數時,會檢查有module_param宏填入的模組選項,這些選項會儲存在kernel_param資料結構中,module_param宏會確保這些資料結構都會被存放在特定的記憶體區塊中(__param),有指標__start___param

和__stop___param限定。

__setup_start……__setup_end:這個地區會在引導階段結束時釋放掉,使用者在運行期間不能看到或修改這些選項;

__start___param……__stop___param:這個地區不會被釋放,其內容會被輸出到/sys檔案系統,這些選項可以顯示給使用者。

有__setup()和early_param()宏定義的關鍵字會被放入__setup_start……__setup_end地區,只是early_param宏定義的關鍵字會被設定early標記。

struct obs_kernel_param { 

    const char *str; 

    int (*setup_func)(char *); 

    int early; 

};

str是關鍵字,setup_func是關聯的處理函數,early則是early標記。__setup_param宏回家所有的obs_kernel_params執行個體儲存到專用的記憶體地區內,這樣比較容易遍曆,__setup_start和__setup_end指標分別指向這個地區的開端和尾端。而當這些地區不再使用時也便於快速釋放記憶體。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.