我们先来看源码,有代码才有说服力哦:
1.asmlinkage void __init start_kernel(void)
2. {
3. char * command_line;
4. extern const struct kernel_param __start___param[], __stop___param[];
5.
6. smp_setup_processor_id(); //确定SMP系统中每个CPU的id
7.
8. /*
9. * Need to run as early as possible, to initialize the
10. * lockdep hash:
11. */
12. lockdep_init(); //初始化互斥锁的dependency。
13. debug_objects_early_init(); //初始化debug kernel相关
14.
15. /*
16. * Set up the the initial canary ASAP:
17. */
18. boot_init_stack_canary(); //stack_canary的是带防止栈溢出攻击保护的堆栈。
19.
20. cgroup_init_early(); //cgroup是什么??有待查找
21.
22. local_irq_disable(); //这个太直白了。
23. early_boot_irqs_off(); //这个也很直白。
24.
25. /*
26. * Interrupts are still disabled. Do necessary setups, then
27. * enable them
28. */
29. tick_init(); //初始化time ticket,时钟
30. boot_cpu_init(); //用以启动的CPU进行初始化。也就是初始化CPU0
31. page_address_init();//初始化页面
32. printk(KERN_NOTICE "%s", linux_banner);
33. setup_arch(&command_line); //CPU架构相关的初始化
34. mm_init_owner(&init_mm, &init_task); //初始化内存管理
35. setup_command_line(command_line); //处理启动命令行
36. setup_nr_cpu_ids(); //nr_cpu_id是什么?
37. setup_per_cpu_areas(); //为每个CPU开辟一块区域?
38. smp_prepare_boot_cpu();//准备boot_cpu. /* arch-specific boot-cpu hooks */
39.
40. build_all_zonelists(NULL); //建立什么样的zone??
41. page_alloc_init(); //初始化page allocation相关结构
42.
43. printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
44. parse_early_param();
45. parse_args("Booting kernel", static_command_line, __start___param,
46. __stop___param - __start___param,
47. &unknown_bootoption);//解析启动参数
48. /*
49. * These use large bootmem allocations and must precede
50. * kmem_cache_init()
51. */
52. pidhash_init();//初始化process ID hash表
53. vfs_caches_init_early(); //文件系统caches预初始化
54. sort_main_extable(); //初始化exception table
55. trap_init(); //初始化trap,用以处理错误执行代码
56. mm_init(); //初始化内存管理
57. /*
58. * Set up the scheduler prior starting any interrupts (such as the
59. * timer interrupt). Full topology setup happens at smp_init()
60. * time - but meanwhile we still have a functioning scheduler.
61. */
62. sched_init(); //进程调度初始化
63. /*
64. * Disable preemption - early bootup scheduling is extremely
65. * fragile until we cpu_idle() for the first time.
66. */
67. preempt_disable(); //这个是什么东东?
68. if (!irqs_disabled()) {
69. printk(KERN_WARNING "start_kernel(): bug: interrupts were "
70. "enabled *very* early, fixing it\n");
71. local_irq_disable();
72. }
73. rcu_init(); //Read_Copy_Update机制初始
74. radix_tree_init(); //什么是radlx_tree
75. /* init some links before init_ISA_irqs() */
76. early_irq_init();
77. init_IRQ(); //初始化中断
78. prio_tree_init(); //这是啥?以后要弄懂
79. init_timers(); //初始化时钟
80. hrtimers_init();//初始化高精时钟
81. softirq_init();//初始化软中断
82. timekeeping_init();//初始化时钟源
83. time_init();//初始化时间例程
84. profile_init(); //Profile?这是什么profile?
85. if (!irqs_disabled())
86. printk(KERN_CRIT "start_kernel(): bug: interrupts were "
87. "enabled early\n");
88. early_boot_irqs_on();
89. local_irq_enable();
90.
91. /* Interrupts are enabled now so all GFP allocations are safe. */
92. gfp_allowed_mask = __GFP_BITS_MASK;
93.
94. kmem_cache_init_late();//初始化CPU Cache
95.
96. /*
97. * HACK ALERT! This is early. We're enabling the console before
98. * we've done PCI setups etc, and console_init() must be aware of
99. * this. But we do want output early, in case something goes wrong.
100. */
101. console_init(); //初始化console
102. if (panic_later)
103. panic(panic_later, panic_param);
104.
105. lockdep_info();
106.
107. /*
108. * Need to run this when irqs are enabled, because it wants
109. * to self-test [hard/soft]-irqs on/off lock inversion bugs
110. * too:
111. */
112. locking_selftest(); //自测试锁
113.
114. #ifdef CONFIG_BLK_DEV_INITRD
115. if (initrd_start && !initrd_below_start_ok &&
116. page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
117. printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
118. "disabling it.\n",
119. page_to_pfn(virt_to_page((void *)initrd_start)),
120. min_low_pfn);
121. initrd_start = 0;
122. }
123. #endif
124. page_cgroup_init(); //页面初始
125. enable_debug_pagealloc(); //页面分配debug启用
126. kmemleak_init(); //memory leak 侦测初始化
127. debug_objects_mem_init(); //debug object是什么?
128. idr_init_cache(); //idr是什么?
129. setup_per_cpu_pageset(); //设置每个CPU的页面集合
130. numa_policy_init();// NUMA (Non Uniform Memory Access) policy
131. if (late_time_init)
132. late_time_init();
133. sched_clock_init();//初始化调度时钟
134. calibrate_delay(); //协同不同CPU的时钟
135. pidmap_init();//pid是process id还是processor id?
136. anon_vma_init();//anonymous page?什么意思?
137. #ifdef CONFIG_X86
138. if (efi_enabled)
139. efi_enter_virtual_mode();
140. #endif
141. thread_info_cache_init(); //初始化thread info
142. cred_init(); //credential
143. fork_init(totalram_pages); //初始化fork
144. proc_caches_init(); //初始化/proc的cache?
145. buffer_init(); // buffer
146. key_init(); //key
147. security_init();//security
148. dbg_late_init();//debug
149. vfs_caches_init(totalram_pages);//文件系统cache初始化
150. signals_init();//signal
151. /* rootfs populating might need page-writeback */
152. page_writeback_init();page_writeback
153. #ifdef CONFIG_PROC_FS
154. proc_root_init();
155. #endif
156. cgroup_init(); //cgroup?
157. cpuset_init(); //cpuset
158. taskstats_init_early(); //task
159. delayacct_init();
160.
161. check_bugs(); //检查什么bug?
162.
163. acpi_early_init();//acpi /* before LAPIC and SMP init */
164. sfi_init_late(); //simple firmware interface
165.
166. ftrace_init();
167.
168. /* Do the rest non-__init'ed, we're now alive */
169. rest_init();
170. }
下面进行一下重要函数的解析:
start_kernel()函数位于init/main.c中,是引导过程中最重要的一个函数,就像它的名字一样,它初始化了内核所有的功能。
1,调用lock_kernel(),防止内核被意外抢断,定义在lib/kernel_lock.c中。在SMP或者抢断式调度环境中,内核可以被抢断。内核初始化时,功能还不完善,为防止此种情况发生,使用称为Big Kernel Lock的spinlock。spinlock是一种忙等待锁,如果等待周期不是很长,它比信号有效,因为信号会造成进程调度。Big Kernel Lock只在内核初始化时使用,当初始化结束后,该锁被释放。
2,page_address_init()函数初始化页管理,创建了页管理所需的数据结构,定义在mm/highmem.c中。
3,输出内核版本信息,执行了两个内核输出语句printk(KERN_NOTICE)和printk(linux_banner)。因为此时还没有初始化控制台,所以这些信息不能输出到屏幕上或者输出到串口上,而是输出到一个buffer中。printk()函数定义在kernel/printk.c 中,KERN_NOTICE宏定义在include/linux/kernel.h中,值为"<5>"。linux_banner定义在 init/version.c中,在我的实验环境中是这样的一个字符串:Linux version 2.6.28 (zctan@dbgkrnl) (gcc version 4.3.0 20080428 (Red Hat 4.3.0-8) (GCC) ) #1 SMP Sun Feb 8 20:56:17 CST 2009。
4,setup_arch(),位于arch/x86/kernel/setup.c,初始化了许多体系结构相关的子系统。
5,setup_per_cpu_area(),定义在arch/x86/kernel/setup_percpu.c中,如果是SMP环境,则为每个CPU创建数据结构,分配初始工作内存。
6,smp_prepare_boot_cpu(),定义在include/asm-x86/smp.h。如果是SMP环境,则设置boot CPU的一些数据。在引导过程中使用的CPU称为boot CPU。
7,sched_init(),定义在kernel/sched.c。初始化每个CPU的运行队列和超时队列。Linux使用多优先级队列的调度方法,就绪进程位于运行队列中。
8,build_all_zonelists(),定义在mm/page_alloc.c中,建立内存区域链表。Linux将所有物理内存分为三个区,ZONE_DMA, ZONE_NORMAM, ZONE_HIGHMEM。
9,trap_init(),定义在arch/x86/kernel/traps_32.c中,初始化IDT, 如除0错,缺页中断等。
10,rcu_init(),定义在kernel/rcupdate.c中,初始化Read-Copy-Update子系统。当使用spinlock会造成效率低下时,RCU被用来实现临界区的互斥。
11,init_IRQ(),定义在arch/x86/kernel/paravirt.c中,初始化中断控制器。
12,pidhash_init(),定义在kernel/pid.c中,Linux的进程描述符称为PID, 使用名称空间以及hash表来管理。
13,init_timers(),定义在kernel/timer.c中,初始化定时器。
14,softirq_init(),定义在kernel/softirq.c中,初始化中断子系统,如softirq, tasklet。
15,time_init(),定义在arch/x86/kernel/time_32.c中,初始化系统时间。
16,profile_init(),定义在kernel/profile.c中,为profiling data分配存储空间。Profiling data这个术语描述在程序运行过程中采集到的一些数据,用于性能的分析。
17,local_irq_enable(),定义在include/linux/irqflags.h中,开启引导CPU的中断。
18,console_init(),定义在drivers/char/tty_io.c中,初始化控制台,可以是显示器也可以是串口。此时屏幕上才会有输出,前面printk输出到buffer中的内容会在这里全部输出。
19,initrd检测。如果定义了Init Ram Disk,则检测其是否有效。
20,mem_init(),定义在arch/x86/mm/init_32.c,检测所有可用物理页。
21,pgtable_cache_init(),定义在include/asm-x86/pgtable_32.h,在slab存储管理子系统中创建页目录页表的cache。
22,fork_init(),定义在kernel/fork.c中,初始化多进程环境。此时,执行start_kernel的进程就是所谓的进程0。
23,buffer_init(),定义在fs/buffer.c中,初始化文件系统的缓冲区。
24,vfs_cache_init(),定义在fs/dcache.c中,创建虚拟文件系统的Slab Cache。
25,radix_tree_init(),定义在lib/radix-tree.c。Linux使用radix树来管理位于文件系统缓冲区中的磁盘块,radix树是trie树的一种。
26,signals_init(),定义在kernel/signal.c中,初始化信号队列。
27,page_writeback_init(),定义在mm/page-writeback.c中,初始化将脏页页同步到磁盘上的控制信息。
28,proc_root_init(),定义在fs/proc/root.c, 初始化proc文件系统
29,rest_init(),定义在init/main.c中,创建init内核线程(也就是进程1)。init进程创建成功后,进程0释放Big Kernel Lock,重新调度(因为现在只有两个进程,所以调度的是init进程)。进程0,就变成了idle进程,只负责调度。
注:start_kernel函数涉及到很多内容和硬件知识,比如SMP等,有很多是我不知道的,所以只能简要的从功能上说明一下,有些可能理解错了,也会略过一些函数,请见谅。
六,init进程
init进程执行定义在init/main.c中的kernel_init()函数,完成余下的初始化工作。
1,lock_kernel(),加上Big Kernel Lock。
2,初始化SMP环境。
3,do_basic_setup()。调用driver_init(),加载设备驱动程序。执行do_initcalls(),调用内建模块的初始化函数,比如kgdb。
4,init_post()函数会打开/dev/console做为标准输入文件,并复制出标准输出和标准错误输出。最后,按下列顺序偿试执行init程序,位于ramdisk的/init,以及磁盘上的/sbin/init, /etc/init, /bin/init和/bin/sh, 只要有一个能执行就可以。init进程会使用类exec()去调用其它进程,因而不会返回。
smp_setup_processor_id()boot_cpu_init()setup_arch(&command_line);setup_nr_cpu_ids()setup_per_cpu_areas()smp_prepare_boot_cpu()setup_per_cpu_pageset();calibrate_delay();cpuset_init();
boot_init_stack_canary()page_address_init();mm_init_owner();page_alloc_init();mm_init();rcu_init();kmem_cache_init_late();page_cgroup_init();kmemleak_init();numa_policy_init();anon_vma_init();page_writeback_init();
pidhash_init();sched_init();sched_clock_init()pidmap_init();fork_init(totalram_pages);taskstats_init_early();
vfs_caches_init_early();thread_info_cache_init();vfs_caches_init(totalram_pages);
early_irq_init();init_IRQ();softirq_init();
lockdep_init();lockdep_info();locking_selftest();
tick_init();init_timers();hrtimers_init();timekeeping_init();time_init();
debug_objects_early_init();console_init();enable_debug_pagealloc();debug_objects_mem_init();dbg_late_init();
sort_main_extable();trap_init();efi_enter_virtual_mode();cred_init();proc_caches_init();buffer_init();key_init();security_init();signals_init();proc_root_init();delayacct_init();check_bugs();acpi_early_init();sfi_init_late();
cgroup_init_early();build_all_zonelists(NULL);preempt_disable();radix_tree_init();prio_tree_init();profile_init();idr_init_cache();cgroup_init();ftrace_init();