我们先来看源码,有代码才有说服力哦:
- <span style="font-size:12px;">
- 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. }
- </span>
下面进行一下重要函数的解析:
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();