Linux核心 __setup巨集分析
在解析cmdline時,我們經常會使用到__setup巨集,用來處理kernel的cmdline。
#define __setup(str, fn) \
__setup_param(str, fn, fn, 0)
#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 }
現在我是用到讀取屏id。
__setup("lcd_id=", lcd_id_get);
將上面巨集展開就是:
__setup_param(“lcd_id=”, lcd_id_get, lcd_id_get, 0)
static char __setup_str_lcd_id_get[] __initdata __aligned(1) = “lcd_id=”; static struct obs_kernel_param __setup_lcd_id_get __used __section(.init.setup) attribute((aligned((sizeof(long))))) = { __setup_str_lcd_id_get, lcd_id_get, 0 }
把巨集裡面的修飾拿掉就是:
static char __setup_str_lcd_id_get[] = "lcd_id=";
static struct obs_kernel_param __setup_lcd_id_get = { __setup_str_lcd_id_get, lcd_id_get, 0 }
這裡定義了兩個變數:一個字串__setup_str_lcd_id_get,一個obs_kernel_param結構,其結構的原型為:
@/kernel/include/linux/init.h struct obs_kernel_param { const char *str; int (*setup_func)(char *); int early; };
str元素為__setup_str_lcd_id_get, setup_func函式指標為lcd_id_get, early為0.
巨集裡面的__section(.init.setup)修飾,執行將__setup_lcd_id_get結構放到對應section。
#define INIT_SETUP(initsetup_align) \
. = ALIGN(initsetup_align); \
VMLINUX_SYMBOL(__setup_start) = .; \
*(.init.setup) \
VMLINUX_SYMBOL(__setup_end) = .;
INIT_SETUP巨集將被vmlinux連線指令碼使用。
/kernel/arch/arm/kernel/vmlinux.lds.S
.init.data : {
INIT_DATA
INIT_SETUP(16)
INIT_CALLS
CON_INITCALL
SECURITY_INITCALL
INIT_RAM_FS
}
在編譯是,__setup()巨集將編譯進vmlinux中的.init.setup段中,這個段從__setup_start符號開始,在__setup_end,在核心啟動是如何取出呢。
在start_kernel可以看到:
start_kernel()
setup_arch()
parse_early_param() // @/kernel/init
strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE); // 賦值cmdline
parse_early_options(tmp_cmdline);
parse_args("early options", cmdline, NULL, 0, 0, 0, do_early_param);
parse_one(param, val, doing, params, num, min_level, max_level,unknown);
其中parse_one用來解析指令,然後do_early_param
/* Check for early params. */
static int __init do_early_param(char *param, char *val, const char *unused)
{
const struct obs_kernel_param *p;
for (p = __setup_start; p < __setup_end; p++) {
if ((p->early && parameq(param, p->str)) ||
(strcmp(param, "console") == 0 &&
strcmp(p->str, "earlycon") == 0)
) {
if (p->setup_func(val) != 0)
pr_warn("Malformed early option '%s'\n", param);
}
}
/* We accept everything at this stage. */
return 0;
}
do_early_param將取出符號 __setup_start到__setup_end之間的obs_kernel_param結構的變數,呼叫obs_kernel_param中設定的函式指標。
總結:
__setup巨集用來指導建立obs_kernel_param結構,並編譯到核心特定段中。在核心啟動時,將取出obs_kernel_param結構並執行其中的函式。