start kernel flow
来源:互联网 发布:软件系统评分表 编辑:程序博客网 时间:2024/06/05 18:57
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern const struct kernel_param __start___param[], __stop___param[];
/*
* Need to run as early as possible, to initialize the
* lockdep hash:
*/
lockdep_init();
smp_setup_processor_id();
debug_objects_early_init();
/*
* Set up the the initial canary ASAP:
*/
boot_init_stack_canary();
cgroup_init_early();
local_irq_disable();
early_boot_irqs_disabled = true;
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
boot_cpu_init();
page_address_init();
pr_notice("%s", linux_banner);
setup_arch(&command_line);
mm_init_owner(&init_mm, &init_task);
mm_init_cpumask(&init_mm);
setup_command_line(command_line);
setup_nr_cpu_ids();
setup_per_cpu_areas();
smp_prepare_boot_cpu();/* arch-specific boot-cpu hooks */
build_all_zonelists(NULL, NULL);
page_alloc_init();
pr_notice("Kernel command line: %s\n", boot_command_line);
parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
-1, -1, &unknown_bootoption);
jump_label_init();
/*
* These use large bootmem allocations and must precede
* kmem_cache_init()
*/
setup_log_buf(0);
pidhash_init();
vfs_caches_init_early();
sort_main_extable();
trap_init();
mm_init();
/*
* Set up the scheduler prior starting any interrupts (such as the
* timer interrupt). Full topology setup happens at smp_init()
* time - but meanwhile we still have a functioning scheduler.
*/
sched_init();
/*
* Disable preemption - early bootup scheduling is extremely
* fragile until we cpu_idle() for the first time.
*/
preempt_disable();
if (WARN(!irqs_disabled(), "Interrupts were enabled *very* early, fixing it\n"))
local_irq_disable();
idr_init_cache();
perf_event_init();
rcu_init();
tick_nohz_init();
radix_tree_init();
/* init some links before init_ISA_irqs() */
early_irq_init();
init_IRQ();
tick_init();
init_timers();
hrtimers_init();
softirq_init();
timekeeping_init();
time_init();
profile_init();
call_function_init();
WARN(!irqs_disabled(), "Interrupts were enabled early\n");
early_boot_irqs_disabled = false;
local_irq_enable();
kmem_cache_init_late();
/*
* HACK ALERT! This is early. We're enabling the console before
* we've done PCI setups etc, and console_init() must be aware of
* this. But we do want output early, in case something goes wrong.
*/
console_init();
if (panic_later)
panic(panic_later, panic_param);
lockdep_info();
/*
* Need to run this when irqs are enabled, because it wants
* to self-test [hard/soft]-irqs on/off lock inversion bugs
* too:
*/
locking_selftest();
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && !initrd_below_start_ok &&
page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",
page_to_pfn(virt_to_page((void *)initrd_start)),
min_low_pfn);
initrd_start = 0;
}
#endif
page_cgroup_init();
debug_objects_mem_init();
kmemleak_init();
setup_per_cpu_pageset();
numa_policy_init();
if (late_time_init)
late_time_init();
sched_clock_init();
calibrate_delay();
pidmap_init();
anon_vma_init();
#ifdef CONFIG_X86
if (efi_enabled(EFI_RUNTIME_SERVICES))
efi_enter_virtual_mode();
#endif
thread_info_cache_init();
cred_init();
fork_init(totalram_pages);
proc_caches_init();
buffer_init();
key_init();
security_init();
dbg_late_init();
vfs_caches_init(totalram_pages);
signals_init();
/* rootfs populating might need page-writeback */
page_writeback_init();
#ifdef CONFIG_PROC_FS
proc_root_init();
#endif
cgroup_init();
cpuset_init();
taskstats_init_early();
delayacct_init();
check_bugs();
acpi_early_init(); /* before LAPIC and SMP init */
sfi_init_late();
if (efi_enabled(EFI_RUNTIME_SERVICES)) {
efi_late_init();
efi_free_boot_services();
}
ftrace_init();
/* Do the rest non-__init'ed, we're now alive */
rest_init();
}
MACHINE_START主要是定义了"struct machine_desc"的类型,放在 section(".arch.info.init"),是初始化数据,Kernel 起来之后将被丢弃。
其余各个成员函数在setup_arch()中被赋值到内核结构体,在不同时期被调用:
start_kernel()--->setup_arch()--->do_initcalls()--->customize_machine()--->TESTPROJECT_stingray_init()
1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用。
2. .init_irq在start_kernel() --> init_IRQ() --> init_arch_irq()中被调用
3. .map_io 在 setup_arch() --> paging_init() --> devicemaps_init()中被调用
4. .timer是定义系统时钟,定义TIMER4为系统时钟,在arch/arm/plat-s3c/time.c中体现。在start_kernel() --> time_init()中被调用。
5. .boot_params是bootloader向内核传递的参数的位置,这要和bootloader中参数的定义要一致。
arcadia MACH_ARCADIAARCADIA 4423
arcadia_l MACH_ARCADIA_LARCADIA_L 4424
msm8930dt MACH_MSM8930DTMSM8930DT 4425
ktam3874 MACH_KTAM3874KTAM3874 4426
cec4 MACH_CEC4CEC4 4427
ape6evm MACH_APE6EVMAPE6EVM 4428
tx6 MACH_TX6 TX6 4429
cfa10037 MACH_CFA10037CFA10037 4431
ezp1000 MACH_EZP1000EZP1000 4433
wgr826v MACH_WGR826VWGR826V 4434
exuma MACH_EXUMAEXUMA 4435
fregate MACH_FREGATEFREGATE 4436
osirisimx508 MACH_OSIRISIMX508OSIRISIMX508 4437
st_exigo MACH_ST_EXIGOST_EXIGO 4438
pismo MACH_PISMOPISMO 4439
atc7 MACH_ATC7ATC7 4440
nspireclp MACH_NSPIRECLPNSPIRECLP 4441
nspiretp MACH_NSPIRETPNSPIRETP 4442
nspirecx MACH_NSPIRECXNSPIRECX 4443
maya MACH_MAYAMAYA 4444
wecct MACH_WECCTWECCT 4445
m2s MACH_M2S M2S 4446
MAYA这个宏在/arch/arm/tools/mach-types文件里定义:
MACHINE_START的定义在arch/arm/include/asm/mach/arch.h
#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END \
};
其实就是定义了一个struct machine_desc类型结构体变量,这个结构体还定义了其他一些成员,接下来着重关注.init_machine这个成员,
它是一个函数指针,值为 XXXXX_machine_init,
具体内容先不分析。现在最关心的是这个结构体变量在哪里被调用,从而调用它里面的成员和成员函数呢?先来看/arch/arm/kernel/setup.c 里面的setup_arch()函数:
_text, _etext, _edata, _end
这四个参数来自于文件:[ arch/arm/kernel/vmlinux.lds.S ]
arch/arm/include/asm/mach-types.h 内容
include/generated/mach-types.h
#define MACH_TYPE_XXXXX 2520
也就是说参数machine_arch_type的值为2520。在setup_machine()函数里主要调用了lookup_machine_type()函数来查找对应的type,应该是出于效率的原因,
这个函数是通过汇编实现的,在此就不给出具体代码了。到这里,知道了在/init/main.c的start_kernel()函数里调用了setup_arch(),在setup_arch()里找到了具体的struct machine_desc类型的变量,但是在哪里通过这个变量调用里面的成员或成员函数的呢?继续找。还是在setup.c里,看到了这样一个函数:
复制代码
1 static int __init customize_machine(void)
2 {
3 /* customizes platform devices, or adds new ones */
4 if (machine_desc->init_machine)
5 machine_desc->init_machine();
6 return 0;
7 }
8 arch_initcall(customize_machine);
终于看到了,成员函数init_machine就是在这里被调用的。但是它没有被显式调用,而是放在了arch_initcall这个宏里,去看看它怎么定义的:
#define arch_initcall(fn) __define_initcall("3",fn,3)
再看__define_initcall宏:
1 #define __define_initcall(level,fn,id) \
2 static initcall_t __initcall_##fn##id __used \
3 __attribute__((__section__(".initcall" level ".init"))) = fn
嗯,它被链接到了.initcall段里,现在简单看看/arch/arm/kernel/vmlinux.lds这个链接脚本里关于initcall的定义:
复制代码
1 __initcall_start = .;
2 *(.initcallearly.init) __early_initcall_end = .;
3 *(.initcall0.init) *(.initcall0s.init)
4 *(.initcall1.init) *(.initcall1s.init)
5 *(.initcall2.init) *(.initcall2s.init)
6 *(.initcall3.init) *(.initcall3s.init)
7 *(.initcall4.init) *(.initcall4s.init)
8 *(.initcall5.init) *(.initcall5s.init)
9 *(.initcallrootfs.init)
10 *(.initcall6.init) *(.initcall6s.init)
11 *(.initcall7.init) *(.initcall7s.init)
12 __initcall_end = .;
可以看到customize_machine()被放到了.initcall3.init里。说了那么多定义,究竟它在哪里被调用啊?好吧,它是在/init/main.c里一个叫do_initcalls()的函数里被调用,去看看呗:
复制代码
1 extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];
2
3 static void __init do_initcalls(void)
4 {
5 initcall_t *fn;
6
7 for (fn = __early_initcall_end; fn < __initcall_end; fn++)
8 do_one_initcall(*fn);
9 }
看到第1行,很熟悉吧。在for循环里依次调用了从__early_initcall_end开始到__initcall_end结束的所有函数。customize_machine()也是在其间被调用。
start_kernel()--->setup_arch()--->do_initcalls()--->customize_machine()--->mini6410_machine_init()
让我们看一下parameter 的parser
parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
-1, -1, &unknown_bootoption);
early_param和__setup宏
1.编译相关
在/include/asm-generic/Vmlinux.lds.h文件中定义了__setup_start.....__setup_end段
#define INIT_SETUP(initsetup_align) \
. = ALIGN(initsetup_align); \
VMLINUX_SYMBOL(__setup_start) = .; \
*(.init.setup) \
VMLINUX_SYMBOL(__setup_end) = .;
标记了.init.setup的函数会被编译进该段
2.内核启动的相关调用关系
在start_kernel中调用parse_early_param()
void __init parse_early_param(void)
{
static __initdata int done = 0;
static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];
if (done)
return;
strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE); //复制启动命令行数据
parse_early_options(tmp_cmdline); //调用parse_early_options函数
done = 1;
}
parse_early_options函数
void __init parse_early_options(char *cmdline)
{
parse_args("early options", cmdline, NULL, 0, do_early_param);
}
接着调用parse_args函数
int parse_args(const char *name,char *args,const struct kernel_param *params,unsigned num,int (*unknown)(char *param, char *val))
{
char *param, *val;
DEBUGP("Parsing ARGS: %s\n", args);
args = skip_spaces(args);
while (*args) { //遍历启动命令行
int ret;
int irq_was_disabled;
args = next_arg(args, ¶m, &val); //获取下一个参数,填充param和val参数(例如:param--console;val--tty2,115200n8)
irq_was_disabled = irqs_disabled();
ret = parse_one(param, val, params, num, unknown); //解析一个命令行参数
if (irq_was_disabled && !irqs_disabled()) {
printk(KERN_WARNING "parse_args(): option '%s' enabled ""irq's!\n", param);
}
switch (ret) {
case -ENOENT:
printk(KERN_ERR "%s: Unknown parameter `%s'\n",name, param);
return ret;
case -ENOSPC:
printk(KERN_ERR "%s: `%s' too large for parameter `%s'\n",name, val ?: "", param);
return ret;
case 0:
break;
default:
printk(KERN_ERR"%s: `%s' invalid for parameter `%s'\n",name, val ?: "", param);
return ret;
}
}
/* All parsed OK. */
return 0;
}
命令行参数的解析parse_one
static int parse_one(char *param,char *val,const struct kernel_param *params,unsigned num_params,int (*handle_unknown)(char *param, char *val))
{
unsigned int i;
int err;
/* Find parameter */
for (i = 0; i < num_params; i++) { //num_params=0
if (parameq(param, params[i].name)) {
if (!val && params[i].ops->set != param_set_bool)
return -EINVAL;
DEBUGP("They are equal! Calling %p\n",params[i].ops->set);
mutex_lock(¶m_lock);
err = params[i].ops->set(val, ¶ms[i]);
mutex_unlock(¶m_lock);
return err;
}
}
if (handle_unknown) { //若handle_unknown函数存在
DEBUGP("Unknown argument: calling %p\n", handle_unknown);
return handle_unknown(param, val); //则调用handle_unknown函数,参数为param,val
}
DEBUGP("Unknown argument `%s'\n", param);
return -ENOENT;
}
回溯回去handle_unknow函数就是do_early_param
[cpp]
static int __init do_early_param(char *param, char *val)
{
const struct obs_kernel_param *p;
for (p = __setup_start; p < __setup_end; p++) {
if ((p->early && strcmp(param, p->str) == 0) || (strcmp(param, "console") == 0 && strcmp(p->str, "earlycon") == 0)) {
if (p->setup_func(val) != 0)
printk(KERN_WARNING"Malformed early option '%s'\n", param);
}
}
/* We accept everything at this stage. */
return 0;
}
do_early_param函数从__setup_start遍历到__setup_end段,
判断参数,进入if函数体里面
if (p->setup_func(val) != 0)这句调用了对应setup_func或early_param成员的函数,并将val作为其参数,val其实便是__setup(str, fn)或__early_param中的str
其实就是调用了fn(str)
这里的第一条if会刷选掉__setup定义的情况(除了console和earlycon参数的),因为__setup定义的obs_kernel_param结构体p->early=0
__setup定义的fn会在start_kernel->parse_args("Booting kernel", static_command_line, __start___param,__stop___param - __start___param,&unknown_bootoption);
unknown_bootoption->obsolete_checksetup函数给调用
看start_kernel中调用顺序
parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param,&unknown_bootoption);
可见先调用__early_param定义的解析参数函数及__setup定义的(console及earlycon)的参数解析函数
接着再调用__setup定义的其他解析参数函数
{
char * command_line;
extern const struct kernel_param __start___param[], __stop___param[];
/*
* Need to run as early as possible, to initialize the
* lockdep hash:
*/
lockdep_init();
smp_setup_processor_id();
debug_objects_early_init();
/*
* Set up the the initial canary ASAP:
*/
boot_init_stack_canary();
cgroup_init_early();
local_irq_disable();
early_boot_irqs_disabled = true;
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
boot_cpu_init();
page_address_init();
pr_notice("%s", linux_banner);
setup_arch(&command_line);
mm_init_owner(&init_mm, &init_task);
mm_init_cpumask(&init_mm);
setup_command_line(command_line);
setup_nr_cpu_ids();
setup_per_cpu_areas();
smp_prepare_boot_cpu();/* arch-specific boot-cpu hooks */
build_all_zonelists(NULL, NULL);
page_alloc_init();
pr_notice("Kernel command line: %s\n", boot_command_line);
parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
-1, -1, &unknown_bootoption);
jump_label_init();
/*
* These use large bootmem allocations and must precede
* kmem_cache_init()
*/
setup_log_buf(0);
pidhash_init();
vfs_caches_init_early();
sort_main_extable();
trap_init();
mm_init();
/*
* Set up the scheduler prior starting any interrupts (such as the
* timer interrupt). Full topology setup happens at smp_init()
* time - but meanwhile we still have a functioning scheduler.
*/
sched_init();
/*
* Disable preemption - early bootup scheduling is extremely
* fragile until we cpu_idle() for the first time.
*/
preempt_disable();
if (WARN(!irqs_disabled(), "Interrupts were enabled *very* early, fixing it\n"))
local_irq_disable();
idr_init_cache();
perf_event_init();
rcu_init();
tick_nohz_init();
radix_tree_init();
/* init some links before init_ISA_irqs() */
early_irq_init();
init_IRQ();
tick_init();
init_timers();
hrtimers_init();
softirq_init();
timekeeping_init();
time_init();
profile_init();
call_function_init();
WARN(!irqs_disabled(), "Interrupts were enabled early\n");
early_boot_irqs_disabled = false;
local_irq_enable();
kmem_cache_init_late();
/*
* HACK ALERT! This is early. We're enabling the console before
* we've done PCI setups etc, and console_init() must be aware of
* this. But we do want output early, in case something goes wrong.
*/
console_init();
if (panic_later)
panic(panic_later, panic_param);
lockdep_info();
/*
* Need to run this when irqs are enabled, because it wants
* to self-test [hard/soft]-irqs on/off lock inversion bugs
* too:
*/
locking_selftest();
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && !initrd_below_start_ok &&
page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",
page_to_pfn(virt_to_page((void *)initrd_start)),
min_low_pfn);
initrd_start = 0;
}
#endif
page_cgroup_init();
debug_objects_mem_init();
kmemleak_init();
setup_per_cpu_pageset();
numa_policy_init();
if (late_time_init)
late_time_init();
sched_clock_init();
calibrate_delay();
pidmap_init();
anon_vma_init();
#ifdef CONFIG_X86
if (efi_enabled(EFI_RUNTIME_SERVICES))
efi_enter_virtual_mode();
#endif
thread_info_cache_init();
cred_init();
fork_init(totalram_pages);
proc_caches_init();
buffer_init();
key_init();
security_init();
dbg_late_init();
vfs_caches_init(totalram_pages);
signals_init();
/* rootfs populating might need page-writeback */
page_writeback_init();
#ifdef CONFIG_PROC_FS
proc_root_init();
#endif
cgroup_init();
cpuset_init();
taskstats_init_early();
delayacct_init();
check_bugs();
acpi_early_init(); /* before LAPIC and SMP init */
sfi_init_late();
if (efi_enabled(EFI_RUNTIME_SERVICES)) {
efi_late_init();
efi_free_boot_services();
}
ftrace_init();
/* Do the rest non-__init'ed, we're now alive */
rest_init();
}
MACHINE_START主要是定义了"struct machine_desc"的类型,放在 section(".arch.info.init"),是初始化数据,Kernel 起来之后将被丢弃。
其余各个成员函数在setup_arch()中被赋值到内核结构体,在不同时期被调用:
start_kernel()--->setup_arch()--->do_initcalls()--->customize_machine()--->TESTPROJECT_stingray_init()
1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用。
2. .init_irq在start_kernel() --> init_IRQ() --> init_arch_irq()中被调用
3. .map_io 在 setup_arch() --> paging_init() --> devicemaps_init()中被调用
4. .timer是定义系统时钟,定义TIMER4为系统时钟,在arch/arm/plat-s3c/time.c中体现。在start_kernel() --> time_init()中被调用。
5. .boot_params是bootloader向内核传递的参数的位置,这要和bootloader中参数的定义要一致。
arcadia MACH_ARCADIAARCADIA 4423
arcadia_l MACH_ARCADIA_LARCADIA_L 4424
msm8930dt MACH_MSM8930DTMSM8930DT 4425
ktam3874 MACH_KTAM3874KTAM3874 4426
cec4 MACH_CEC4CEC4 4427
ape6evm MACH_APE6EVMAPE6EVM 4428
tx6 MACH_TX6 TX6 4429
cfa10037 MACH_CFA10037CFA10037 4431
ezp1000 MACH_EZP1000EZP1000 4433
wgr826v MACH_WGR826VWGR826V 4434
exuma MACH_EXUMAEXUMA 4435
fregate MACH_FREGATEFREGATE 4436
osirisimx508 MACH_OSIRISIMX508OSIRISIMX508 4437
st_exigo MACH_ST_EXIGOST_EXIGO 4438
pismo MACH_PISMOPISMO 4439
atc7 MACH_ATC7ATC7 4440
nspireclp MACH_NSPIRECLPNSPIRECLP 4441
nspiretp MACH_NSPIRETPNSPIRETP 4442
nspirecx MACH_NSPIRECXNSPIRECX 4443
maya MACH_MAYAMAYA 4444
wecct MACH_WECCTWECCT 4445
m2s MACH_M2S M2S 4446
MAYA这个宏在/arch/arm/tools/mach-types文件里定义:
MACHINE_START的定义在arch/arm/include/asm/mach/arch.h
#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END \
};
其实就是定义了一个struct machine_desc类型结构体变量,这个结构体还定义了其他一些成员,接下来着重关注.init_machine这个成员,
它是一个函数指针,值为 XXXXX_machine_init,
具体内容先不分析。现在最关心的是这个结构体变量在哪里被调用,从而调用它里面的成员和成员函数呢?先来看/arch/arm/kernel/setup.c 里面的setup_arch()函数:
_text, _etext, _edata, _end
这四个参数来自于文件:[ arch/arm/kernel/vmlinux.lds.S ]
arch/arm/include/asm/mach-types.h 内容
include/generated/mach-types.h
#define MACH_TYPE_XXXXX 2520
也就是说参数machine_arch_type的值为2520。在setup_machine()函数里主要调用了lookup_machine_type()函数来查找对应的type,应该是出于效率的原因,
这个函数是通过汇编实现的,在此就不给出具体代码了。到这里,知道了在/init/main.c的start_kernel()函数里调用了setup_arch(),在setup_arch()里找到了具体的struct machine_desc类型的变量,但是在哪里通过这个变量调用里面的成员或成员函数的呢?继续找。还是在setup.c里,看到了这样一个函数:
复制代码
1 static int __init customize_machine(void)
2 {
3 /* customizes platform devices, or adds new ones */
4 if (machine_desc->init_machine)
5 machine_desc->init_machine();
6 return 0;
7 }
8 arch_initcall(customize_machine);
终于看到了,成员函数init_machine就是在这里被调用的。但是它没有被显式调用,而是放在了arch_initcall这个宏里,去看看它怎么定义的:
#define arch_initcall(fn) __define_initcall("3",fn,3)
再看__define_initcall宏:
1 #define __define_initcall(level,fn,id) \
2 static initcall_t __initcall_##fn##id __used \
3 __attribute__((__section__(".initcall" level ".init"))) = fn
嗯,它被链接到了.initcall段里,现在简单看看/arch/arm/kernel/vmlinux.lds这个链接脚本里关于initcall的定义:
复制代码
1 __initcall_start = .;
2 *(.initcallearly.init) __early_initcall_end = .;
3 *(.initcall0.init) *(.initcall0s.init)
4 *(.initcall1.init) *(.initcall1s.init)
5 *(.initcall2.init) *(.initcall2s.init)
6 *(.initcall3.init) *(.initcall3s.init)
7 *(.initcall4.init) *(.initcall4s.init)
8 *(.initcall5.init) *(.initcall5s.init)
9 *(.initcallrootfs.init)
10 *(.initcall6.init) *(.initcall6s.init)
11 *(.initcall7.init) *(.initcall7s.init)
12 __initcall_end = .;
可以看到customize_machine()被放到了.initcall3.init里。说了那么多定义,究竟它在哪里被调用啊?好吧,它是在/init/main.c里一个叫do_initcalls()的函数里被调用,去看看呗:
复制代码
1 extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];
2
3 static void __init do_initcalls(void)
4 {
5 initcall_t *fn;
6
7 for (fn = __early_initcall_end; fn < __initcall_end; fn++)
8 do_one_initcall(*fn);
9 }
看到第1行,很熟悉吧。在for循环里依次调用了从__early_initcall_end开始到__initcall_end结束的所有函数。customize_machine()也是在其间被调用。
start_kernel()--->setup_arch()--->do_initcalls()--->customize_machine()--->mini6410_machine_init()
让我们看一下parameter 的parser
parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
-1, -1, &unknown_bootoption);
early_param和__setup宏
1.编译相关
在/include/asm-generic/Vmlinux.lds.h文件中定义了__setup_start.....__setup_end段
#define INIT_SETUP(initsetup_align) \
. = ALIGN(initsetup_align); \
VMLINUX_SYMBOL(__setup_start) = .; \
*(.init.setup) \
VMLINUX_SYMBOL(__setup_end) = .;
标记了.init.setup的函数会被编译进该段
2.内核启动的相关调用关系
在start_kernel中调用parse_early_param()
void __init parse_early_param(void)
{
static __initdata int done = 0;
static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];
if (done)
return;
strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE); //复制启动命令行数据
parse_early_options(tmp_cmdline); //调用parse_early_options函数
done = 1;
}
parse_early_options函数
void __init parse_early_options(char *cmdline)
{
parse_args("early options", cmdline, NULL, 0, do_early_param);
}
接着调用parse_args函数
int parse_args(const char *name,char *args,const struct kernel_param *params,unsigned num,int (*unknown)(char *param, char *val))
{
char *param, *val;
DEBUGP("Parsing ARGS: %s\n", args);
args = skip_spaces(args);
while (*args) { //遍历启动命令行
int ret;
int irq_was_disabled;
args = next_arg(args, ¶m, &val); //获取下一个参数,填充param和val参数(例如:param--console;val--tty2,115200n8)
irq_was_disabled = irqs_disabled();
ret = parse_one(param, val, params, num, unknown); //解析一个命令行参数
if (irq_was_disabled && !irqs_disabled()) {
printk(KERN_WARNING "parse_args(): option '%s' enabled ""irq's!\n", param);
}
switch (ret) {
case -ENOENT:
printk(KERN_ERR "%s: Unknown parameter `%s'\n",name, param);
return ret;
case -ENOSPC:
printk(KERN_ERR "%s: `%s' too large for parameter `%s'\n",name, val ?: "", param);
return ret;
case 0:
break;
default:
printk(KERN_ERR"%s: `%s' invalid for parameter `%s'\n",name, val ?: "", param);
return ret;
}
}
/* All parsed OK. */
return 0;
}
命令行参数的解析parse_one
static int parse_one(char *param,char *val,const struct kernel_param *params,unsigned num_params,int (*handle_unknown)(char *param, char *val))
{
unsigned int i;
int err;
/* Find parameter */
for (i = 0; i < num_params; i++) { //num_params=0
if (parameq(param, params[i].name)) {
if (!val && params[i].ops->set != param_set_bool)
return -EINVAL;
DEBUGP("They are equal! Calling %p\n",params[i].ops->set);
mutex_lock(¶m_lock);
err = params[i].ops->set(val, ¶ms[i]);
mutex_unlock(¶m_lock);
return err;
}
}
if (handle_unknown) { //若handle_unknown函数存在
DEBUGP("Unknown argument: calling %p\n", handle_unknown);
return handle_unknown(param, val); //则调用handle_unknown函数,参数为param,val
}
DEBUGP("Unknown argument `%s'\n", param);
return -ENOENT;
}
回溯回去handle_unknow函数就是do_early_param
[cpp]
static int __init do_early_param(char *param, char *val)
{
const struct obs_kernel_param *p;
for (p = __setup_start; p < __setup_end; p++) {
if ((p->early && strcmp(param, p->str) == 0) || (strcmp(param, "console") == 0 && strcmp(p->str, "earlycon") == 0)) {
if (p->setup_func(val) != 0)
printk(KERN_WARNING"Malformed early option '%s'\n", param);
}
}
/* We accept everything at this stage. */
return 0;
}
do_early_param函数从__setup_start遍历到__setup_end段,
判断参数,进入if函数体里面
if (p->setup_func(val) != 0)这句调用了对应setup_func或early_param成员的函数,并将val作为其参数,val其实便是__setup(str, fn)或__early_param中的str
其实就是调用了fn(str)
这里的第一条if会刷选掉__setup定义的情况(除了console和earlycon参数的),因为__setup定义的obs_kernel_param结构体p->early=0
__setup定义的fn会在start_kernel->parse_args("Booting kernel", static_command_line, __start___param,__stop___param - __start___param,&unknown_bootoption);
unknown_bootoption->obsolete_checksetup函数给调用
看start_kernel中调用顺序
parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param,&unknown_bootoption);
可见先调用__early_param定义的解析参数函数及__setup定义的(console及earlycon)的参数解析函数
接着再调用__setup定义的其他解析参数函数
0 0
- start kernel flow
- android start up flow
- linux start flow
- uboot and kernel boot flow
- android Kernel Start
- ARM Kernel Start Process
- linux start kernel
- Network Data Flow through the Linux Kernel
- 启动内核出现:start kernel...
- 停在start kernel....问题分析
- tegra tx1 kernel start up
- HOWTO Change the Linux Kernel Start Address
- 让我们现在开始Start Kernel: i386_start_kernel
- Linux kernal map & Network data flow through kernel
- linux kernel packet receive flow(内核封包处理流程)
- Bluetooth Remote Controller Linux Kernel Key Report Flow
- flow
- flow
- 初学者Unity3D自学(第三篇:官方例子2DPlatformer学习之角色控制)
- 基于 libmad 的简单 MP3 流媒体播放器的实现
- 关于使用Excel2010导入 到DataGridView和Listview中时出现的预期格式不正确的问题
- OpenGL学习十九:纹理过滤
- ios 项目总结三
- start kernel flow
- jschart 柱状图 饼状图 折线图
- 二分查找算法
- 泛型数组
- 手机端ul空隙
- 各数据类型与“零”值的比较
- javaSE-集合-enumation
- Java基础3,安装jdk、jre及配置环境变量
- 每天两道oracle笔试题+第六天:1、临时表空间和永久表空间的区别是什么?2、解释Function、Procedure、Package的区别?