过完年来,敲着键盘都有点生疏了,很多东西都有点忘记了,还有刚上班还有点不习惯,状态还没有转换过来,没有办法趁这个机会就复习一下Linux的启动过程吧。
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern const struct kernel_param __start___param[], __stop___param[];
/*
这两个变量为地址指针,指向内核启动参数处理相关结构体段在内存中的位置(虚拟地址)。
声明传入参数的外部参数对于ARM平台,位于 include\asm-generic\vmlinux.lds.h
*/
smp_setup_processor_id();
/*
* Need to run as early as possible, to initialize the
* lockdep hash:
*/
lockdep_init(); /* 初始化 lockdep hash 表,用于检测内核互斥机制所存在的潜在死锁问题 */
debug_objects_early_init();//对调试对象进行早期的初始化,其实就是HASH锁和静态对象池进行初始化
/*
* Set up the the initial canary ASAP:
*/
boot_init_stack_canary();//防止栈溢出攻击的保护
cgroup_init_early();
local_irq_disable();//关闭CPU当前中断
early_boot_irqs_off();
early_init_irq_lock_class();
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
tick_init(); //初始化tick功能,注册clockevent框架通知
boot_cpu_init();//软件上设置CPU为活跃、在线、存在、可用状态,注:多核CPU为第一个核
page_address_init();//初始化页地址,然而S3C6410中内存不可能使用高端地址即大于896M的地址空间,故这个函数为空。
printk(KERN_NOTICE "%s", linux_banner);
setup_arch(&command_line);
/*每种体系结构都有自己的setup_arch()函数,是体系结构相关的,具体编译哪个 体系结构的setup_arch()函数,由源码树顶层目录下的Makefile中的ARCH变量决定
例如: ARM体系结构的。
SUBARCH :=arm
ARCH ?= $(SUBARCH)
该函数在所在的路劲为 /arm/kernel/setup.c,其中那个command_line就是有bootloader传过来的!这个函数是个重量级的函数,大家不可忽视!该函数完成体系结构相关的初始化,内核移植的过程一般也就到此函数为止了,其余的就只是一些相关的外设驱动。*/
mm_init_owner(&init_mm, &init_task);
setup_command_line(command_line); /*1.对cmdline进行备份和保存:保存未改变的comand_line到字符数组static_command_line[] 中。保存 boot_command_line到字符数组saved_command_line[]中
*/
setup_nr_cpu_ids();
setup_per_cpu_areas(); /* 每个CPU分配pre-cpu结构内存并复制.data.percpu段的数据 */
/*如果没有定义CONFIG_SMP宏,则这个函数为空函数。如果定义了CONFIG_SMP宏,则这个setup_per_cpu_areas()函数给每个CPU分配内存,并拷贝.data.percpu段的数据。为系统中的每个CPU的per_cpu变量申请空间。
*/
/*下面三段1.针对SMP处理器的内存初始化函数,如果不是SMP系统则都为空函数。 (arm为空)
2.他们的目的是给每个CPU分配内存,并拷贝.data.percpu段的数据。为系统中的每个CPU的per_cpu变量申请空间并为boot CPU设置一些数据。
3.在SMP系统中,在引导过程中使用的CPU称为boot CPU*/
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
build_all_zonelists(NULL);// 建立系统内存页区(zone)链表
page_alloc_init();//初始化内存页
printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
parse_early_param();// 解析早期格式的内核参数
/*函数对Linux启动命令行参数进行在分析和处理,
当不能够识别前面的命令时,所调用的函数。*/
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
/*
* These use large bootmem allocations and must precede
* kmem_cache_init()
*/
pidhash_init(); /*初始化hash表,以便于从进程的PID获得对应的进程描述指针,按照开发办上的物理内存初始化pid hash表
*/
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 (!irqs_disabled()) {
printk(KERN_WARNING "start_kernel(): bug: interrupts were "
"enabled *very* early, fixing it\n");
local_irq_disable();
}
rcu_init();//互斥访问初始化
radix_tree_init();//创建idr缓冲区
/* init some links before init_ISA_irqs() */
early_irq_init();//早起中断初始化,实质为空
init_IRQ();/*
该函数主要对内核数据结构irq_desc[]和fiqdesc[]数据结构进行了初始化,同时对fiq的堆栈指针进行了设置。另外对DMA进行了初始化,源码如下

其中的那个init_arch_irq是在什么地方定义的呢?
其实在上面的setup_arch()函数中已经进行定义了:arch/arm/kernel/setup.c: init_arch_irq = mdesc->init_irq;,而这个mdesc->init_irq则是在
arch\arm\mach-s3c6410\mach-mini6410.c中定义
*/
prio_tree_init();
init_timers();//定时器初始化
hrtimers_init();
softirq_init();
timekeeping_init();
time_init();
profile_init();/* 对内核的 profile 功能
进行初始化,这是一个内核调式工具,通过这个可以发现内核在内核态的什么地方花费时间最多,
即发现内核的“hot spot”——执行最频繁的内核代码。
这个 profile_init() 分配一段内存,用来存放 profile 信息,每条指令都有一个计数器。 */
if (!irqs_disabled())
printk(KERN_CRIT "start_kernel(): bug: interrupts were "
"enabled early\n");
early_boot_irqs_on();
local_irq_enable();
/* Interrupts are enabled now so all GFP allocations are safe. */
gfp_allowed_mask = __GFP_BITS_MASK;
kmem_cache_init_late();//内存管理的slab机制
/*
* 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();//用来对控制台初始化,这个函数执行完成后,串口才可以看到printk打印的调试信息。这里还有个问题就是,在console_ini()之前用printk()也能打印出来,这个因为,如果没有注册console,
printk只是将信息放到缓冲区中,console_init->con_init->register_console,在register_console时输
出缓冲区中暂存的信息。
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) {
printk(KERN_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();
enable_debug_pagealloc();
kmemleak_init();
debug_objects_mem_init();
idr_init_cache();
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_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();
ftrace_init();
/* Do the rest non-__init'ed, we're now alive */
rest_init();//这个函数留个接下来的文章学习。。。。
}
本文很多部分来源于以下文章,感谢原作者的无私分享。
linux内核启动第二阶段分析 http://blog.csdn.net/boarmy/article/details/8652338
理解start_kernel中函数语句的作用 http://blog.csdn.net/u013012494/article/details/39229843