在构架相关的汇编代码运行完之后,程序跳入了构架无关的内核C语言代码:init/main.c中的start_kernel函数,在这个函数中Linux内核开始真正进入初始化阶段。
下面就顺这代码逐个函数的解释,但是这里并不会过于深入每个函数,因为这样就会只见树木,不见森林。分析代码首先要从构架上宏观地理解,然后再去考虑细节问题
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern struct kernel_param __start___param[], __stop___param[];
- 声明结构标签为kernel_param 的2个变量__start___param[], __stop___param[];
- 这里关键是这两个变量的地址是如何确定的。
- 这两个变量为地址指针,指向内核启动参数处理相关结构体段在内存中的位置(虚拟地址)。
- 对于ARM平台,位于 kernel\include\asm-generic\vmlinux.lds.h
-
- kernel_param结构体的定义是:(标签为kernel_param的结构体)
- struct kernel_param {
- const char *name;
- unsigned int perm;
- param_set_fn set;
- param_get_fn get;
- union {
- void *arg;
- const struct kparam_string *str;
- const struct kparam_array *arr;
- };
smp_setup_processor_id();
- 这个函数是针对SMP处理器的,经查阅资料,其作用是获取当前CPU的的硬件ID。
- 如果不是多处理器构架,在其他文件中就不会定义这个函数,此时使用本文件定义的弱引用函数:
- void __init __weak smp_setup_processor_id(void)
- {
- }
/*
*必须尽早运行这个程序, 作用是初始化
* lockdep 模块的hash表:
*/
lockdep_init();
- lockdep是一个内核调试模块,用来检查内核互斥机制(尤其是自旋锁)潜在的死锁问题。
-
- 由于自旋锁以查询方式等待,不释放处理器,比一般互斥机制更容易死锁,故引入lockdep检查以下几种可能的死锁情况:
- 1.同一个进程递归地加锁同一把锁;
- 2.一把锁既在中断(或中断下半部)使能的情况下执行过加锁操作, 又在中断(或中断下半部)里执行过加锁操作。这样该锁有可能在锁定时由于中断发生又试图在同一处理器上加锁;
- 3.加锁后导致依赖图产生成闭环,这是典型的死锁现象。
debug_objects_early_init();
- 在启动早期初始化hash buckets 和链接静态的 pool objects对象到 poll 列表. 在这个调用完成后 object tracker 已经开始完全运作了.
/*
* Set up the the initial canary ASAP: 初始化栈canary值:
*/
boot_init_stack_canary();
- canary值的是用于防止栈溢出攻击的堆栈的保护字 。
-
- 参考资料: GCC 中的编译器堆栈保护技术
cgroup_init_early();
- cgroup: 它的全称为control group.即一组进程的行为控制.
- 该函数主要是做数据结构和其中链表的初始化
- 参考资料: Linux cgroup机制分析之框架分析
local_irq_disable(); //关闭系统总中断(底层调用汇编指令)
early_boot_irqs_off(); //设置系统中断的关闭标志(bool全局变量)
early_init_irq_lock_class();
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them 中断依然被禁用。做必要的设置后, 再使能它
*/
lock_kernel();
tick_init();
- 初始化内核时钟系统
- -->clockevents_register_notifier(&tick_notifier)
- 往内核通知链中注册内核时钟时间的通知函数
-
- 参考资料:《Linux 时钟处理机制》 《Linux 时钟管理》
boot_cpu_init();
- 激活当前CPU(在内核全局变量中将当前CPU的状态设为激活状态)
- 参考资料:《激活第一个CPU》
page_address_init(); //高端内存相关,未定义高端内存的话为空函数
printk(KERN_NOTICE "%s", linux_banner);
//打印内核版本信息,也就是平时我们在内核启动时在串口中看到的:Linux version 2.6.30.4mingjie (root@EmbedSky) (gcc version 4.3.3 (Sourcery G Lite 2009q1176) )
#2 Sun Oct 21 08:54:39 CST 2012
setup_arch(&command_line);
- 内核构架相关初始化函数,可以说是非常重要的一个初始化步骤。其中包含了处理器相关参数的初始化、内核启动参数(tagged list)的获取和前期处理、内存子系统的早期的初始化(bootmem分配器)。
- 对于ARM构架来说,这个函数位于:arch/arm/kernel/setup.c
- 以后会详细分析这个函数。
mm_init_owner(&init_mm, &init_task);
- 初始化代表内核本身内存使用的管理结构体init_mm。
- ps:每一个任务都有一个mm_struct结构以管理内存空间,init_mm是内核的mm_struct,其中:
- 设置成员变量* mmap指向自己,意味着内核只有一个内存管理结构;
- 设置* pgd=swapper_pg_dir,swapper_pg_dir是内核的页目录(在arm体系结构有16k, 所以init_mm定义了整个kernel的内存空间)。
- 这些内容涉及到内存管理子系统,以后再仔细分析。
setup_command_line(command_line);
- 对cmdline进行备份和保存:
-
- /* 为处理的command line备份 (例如eg. 用于 /proc) */
- char *saved_command_line;
- /* 用于参数处理的command line */
- static char *static_command_line;
setup_per_cpu_areas();
setup_nr_cpu_ids();
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
- 针对SMP处理器的内存初始化函数,如果不是SMP系统则都为空函数。 (arm为空)
- 他们的目的是给每个CPU分配内存,并拷贝.data.percpu段的数据。为系统中的每个CPU的per_cpu变量申请空间并为boot CPU设置一些数据。
- 在SMP系统中,在引导过程中使用的CPU称为boot CPU
/*
* 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();
build_all_zonelists();
page_alloc_init();
printk(KERN_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,
&unknown_bootoption);
if (!irqs_disabled()) {
printk(KERN_WARNING "start_kernel(): bug: interrupts were "
"enabled *very* early, fixing it\n");
local_irq_disable();
}
sort_main_extable();
trap_init();
rcu_init();
/* init some links before init_ISA_irqs() */
early_irq_init();
init_IRQ();
pidhash_init();
init_timers();
hrtimers_init();
softirq_init();
timekeeping_init();
time_init();
sched_clock_init();
profile_init();
if (!irqs_disabled())
printk(KERN_CRIT "start_kernel(): bug: interrupts were "
"enabled early\n");
early_boot_irqs_on();
local_irq_enable();
/*
* 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) {
printk(KERN_CRIT "initrd overwritten (0xlx < 0xlx) - "
"disabling it.\n",
page_to_pfn(virt_to_page((void *)initrd_start)),
min_low_pfn);
initrd_start = 0;
}
#endif
vmalloc_init();
vfs_caches_init_early();
cpuset_init_early();
page_cgroup_init();
mem_init();
enable_debug_pagealloc();
cpu_hotplug_init();
kmem_cache_init();
kmemtrace_init();
debug_objects_mem_init();
idr_init_cache();
setup_per_cpu_pageset();
numa_policy_init();
if (late_time_init)
late_time_init();
calibrate_delay(); // 计算BogoMIPS值,他是衡量一个CPU性能的标志。
pidmap_init(); //PID分配映射初始化。
pgtable_cache_init();
prio_tree_init();
anon_vma_init(); // 匿名虚拟内存域( anonymous VMA)初始化
#ifdef CONFIG_X86
if (efi_enabled)
efi_enter_virtual_mode();
#endif
thread_info_cache_init();//获取thread_info缓存空间,大部分构架为空函数(包括ARM)
cred_init(); //任务信用系统初始化。详见:Documentation/credentials.txt
fork_init(num_physpages);//进程创建机制初始化。为内核"task_struct"分配空间,计算最大任务数。
proc_caches_init(); //初始化进程创建机制所需的其他数据结构,为其申请空间。
buffer_init(); //缓存系统初始化,创建缓存头空间,并检查其大小限时。
key_init(); //内核密钥管理系统初始化
security_init(); //内核安全框架初始化
vfs_caches_init(num_physpages);//虚拟文件系统(VFS)缓存初始化
radix_tree_init();
signals_init(); //信号管理系统初始化
/* rootfs populating might need page-writeback */
page_writeback_init(); //页写回机制初始化
#ifdef CONFIG_PROC_FS
proc_root_init(); //proc文件系统初始化
#endif
cgroup_init(); //control group正式初始化
cpuset_init(); //CPUSET初始化。 参考资料:
taskstats_init_early(); //任务状态早期初始化函数:为结构体获取高速缓存,并初始化互斥机制。
delayacct_init(); //任务延迟初始化
check_bugs(); //检查CPU BUG的函数,通过软件规避BUG
acpi_early_init(); /* before LAPIC and SMP init 在 LAPIC
和 SMP 前初始化 */
- ACPI早期初始化函数。 ACPI - Advanced Configuration and Power Interface高级配置及电源接口
ftrace_init(); //功能跟踪调试机制初始化,ftrace 是 function trace 的简称
/* Do the rest non-__init'ed, we're now alive 所剩下的非-__init的初始化, 内核现在已经启了 */
rest_init();
// 虽然从名字上来说是剩余的初始化。但是这个函数中的初始化包含了很多的内容
}
在看完上面的代码之后,你会发现内容很多。但是归纳起来,我认为需要注意的有以下几点:
- 内核启动参数的获取和处理
- setup_arch(&command_line);函数
- 内存管理的初始化(从bootmem到slab)
- rest_init();函数
其他的部分都是对内核各个组件的数据结构申请内存,并初始化。
阅读(1992) | 评论(0) | 转发(0) |