arm Linux系统启动之----start_kernel函数

[cpp]  view plain copy
  1. head-common.S  
  2. ---具体做了哪些动作  
  3. ---跳转到init/main.c   
  4. ---b start_kernel  
  5. //关于start_kernel的强文深入理解linux内核,第八章  
  6. main.c  
  7. asmlinkage void __init start_kernel(void)  
  8. {  
  9. char * command_line;  
  10. extern struct kernel_param __start___param[], __stop___param[];  
  11. //来设置smp process id,当然目前看到的代码里面这里是空的  
  12. smp_setup_processor_id();  
  13. /* 
  14. * Need to run as early as possible, to initialize the 
  15. * lockdep hash: 
  16. */  
  17. //lockdep是linux内核的一个调试模块,用来检查内核互斥机制尤其是自旋锁潜在的死锁问题。  
  18. //自旋锁由于是查询方式等待,不释放处理器,比一般的互斥机制更容易死锁,  
  19. //故引入lockdep检查以下几种情况可能的死锁(lockdep将有专门的文章详细介绍,在此只是简单列举):  
  20. //  
  21. //·同一个进程递归地加锁同一把锁;  
  22. //  
  23. //·一把锁既在中断(或中断下半部)使能的情况下执行过加锁操作,  
  24. // 又在中断(或中断下半部)里执行过加锁操作。这样该锁有可能在锁定时由于中断发生又试图在同一处理器上加锁;  
  25. //  
  26. //·加锁后导致依赖图产生成闭环,这是典型的死锁现象。  
  27. lockdep_init();  
  28. debug_objects_early_init();  
  29. /* 
  30. * Set up the the initial canary ASAP: 
  31. */  
  32. //初始化stack_canary栈3  
  33. //stack_canary的是带防止栈溢出攻击保护的堆栈。  
  34. //  当user space的程序通过int 0x80进入内核空间的时候,CPU自动完成一次堆栈切换,   
  35. //从user space的stack切换到kernel space的stack。  
  36. //   在这个进程exit之前所发生的所有系统调用所使用的kernel stack都是同一个。  
  37. //kernel stack的大小一般为4096/8192,  
  38. //内核堆栈示意图帮助大家理解:  
  39. //  
  40. //   内存低址                                                              内存高址  
  41. //                      |                 |<-----------------------------esp|     
  42. //   +-----------------------------------4096-------------------------------+  
  43. //   |        72        |     4           |       x < 4016    |     4       |  
  44. //   +------------------+-----------------+---------------------------------+  
  45. //   |thread_info  |    | STACK_END_MAGIC |   var/call chain  |stack_canary |  
  46. //   +------------------+-----------------+---------------------------------+  
  47. //   |     28      | 44 |                 |                                 |  
  48. //                 V    |                                                   |  
  49. //             restart_block                                                V  
  50. //  
  51. //esp+0x0                                                                   +0x40  
  52. //    +---------------------------------------------------------------------------+  
  53. //    |ebx|ecx|edx|esi|edi|ebp|eax|ds|es|fs|gs|orig_eax|eip|cs|eflags|oldesp|oldss|  
  54. //    +---------------------------------------------------------------------------+  
  55. //    |             kernel完成                          |         cpu自动完成       |  
  56. //http://hi.baidu.com/wzt85/blog/item/112a37132f6116c2f6039e44.html  
  57. boot_init_stack_canary();   
  58. //  cgroup: 它的全称为control group.即一组进程的行为控制.  
  59. //  比如,我们限制进程/bin/sh的CPU使用为20%.我们就可以建一个cpu占用为20%的cgroup.  
  60. //  然后将/bin/sh进程添加到这个cgroup中.当然,一个cgroup可以有多个进程.  
  61. //http://blogold.chinaunix.net/u1/51562/showart_1736813.html  
  62. cgroup_init_early();  
  63. //更新kernel中的所有的立即数值,但是包括哪些需要再看?  
  64. core_imv_update();  
  65. //关闭当前CUP中断  
  66. local_irq_disable();  
  67. //修改标记early_boot_irqs_enabled;  
  68. //通过一个静态全局变量 early_boot_irqs_enabled来帮助我们调试代码,  
  69. //通过这个标记可以帮助我们知道是否在”early bootup code”,也可以通过这个标志警告是有无效的终端打开  
  70. early_boot_irqs_off();  
  71. //每一个中断都有一个IRQ描述符(struct irq_desc)来进行描述。  
  72. //这个函数的主要作用是设置所有的 IRQ描述符(struct irq_desc)的锁是统一的锁,  
  73. //还是每一个IRQ描述符(struct irq_desc)都有一个小锁。  
  74. early_init_irq_lock_class();  
  75. /* 
  76.  * Interrupts are still disabled. Do necessary setups, then 
  77.  * enable them 
  78.  */  
  79. // 大内核锁(BKL--Big Kernel Lock)  
  80. //大内核锁本质上也是自旋锁,但是它又不同于自旋锁,自旋锁是不可以递归获得锁的,因为那样会导致死锁。  
  81. //但大内核锁可以递归获得锁。大内核锁用于保护整个内核,而自旋锁用于保护非常特定的某一共享资源。  
  82. //进程保持大内核锁时可以发生调度,具体实现是:  
  83. //在执行schedule时,schedule将检查进程是否拥有大内核锁,如果有,它将被释放,以致于其它的进程能够获得该锁,  
  84. //而当轮到该进程运行时,再让它重新获得大内核锁。注意在保持自旋锁期间是不运行发生调度的。  
  85. //需要特别指出,整个内核只有一个大内核锁,其实不难理解,内核只有一个,而大内核锁是保护整个内核的,当然有且只有一个就足够了。  
  86. //还需要特别指出的是,大内核锁是历史遗留,内核中用的非常少,一般保持该锁的时间较长,因此不提倡使用它。  
  87. //从2.6.11内核起,大内核锁可以通过配置内核使其变得可抢占(自旋锁是不可抢占的),这时它实质上是一个互斥锁,使用信号量实现。  
  88. //大内核锁的API包括:  
  89. //  
  90. //void lock_kernel(void);  
  91. //  
  92. //该函数用于得到大内核锁。它可以递归调用而不会导致死锁。  
  93. //  
  94. //void unlock_kernel(void);  
  95. //  
  96. //该函数用于释放大内核锁。当然必须与lock_kernel配对使用,调用了多少次lock_kernel,就需要调用多少次unlock_kernel。  
  97. //大内核锁的API使用非常简单,按照以下方式使用就可以了:  
  98. //lock_kernel(); //对被保护的共享资源的访问 … unlock_kernel();  
  99. //http://blog.csdn.net/universus/archive/2010/05/25/5623971.aspx  
  100. lock_kernel();  
  101. //初始化time ticket,时钟  
  102. tick_init();  
  103. //函数 tick_init() 很简单,调用 clockevents_register_notifier 函数向 clockevents_chain 通知链注册元素:  
  104. // tick_notifier。这个元素的回调函数指明了当时钟事件设备信息发生变化(例如新加入一个时钟事件设备等等)时,  
  105. //应该执行的操作,该回调函数为 tick_notify   
  106. //http://blogold.chinaunix.net/u3/97642/showart_2050200.html  
  107. boot_cpu_init();  
  108. //初始化页地址,当然对于arm这里是个空函数  
  109. //http://book.chinaunix.net/special/ebook/PrenticeHall/PrenticeHallPTRTheLinuxKernelPrimer/0131181637/ch08lev1sec5.html  
  110. page_address_init();  
  111. printk(KERN_NOTICE "%s", linux_banner);  
  112. //系结构相关的内核初始化过程  
  113. //http://www.cublog.cn/u3/94690/showart_2238008.html  
  114. setup_arch(&command_line);  
  115. //初始化内存管理  
  116. mm_init_owner(&init_mm, &init_task);  
  117. //处理启动命令,这里就是设置的cmd_line  
  118. setup_command_line(command_line);  
  119. //这个在定义了SMP的时候有作用,现在这里为空函数;对于smp的使用,后面在看。。。  
  120. setup_nr_cpu_ids();  
  121. //如果没有定义CONFIG_SMP宏,则这个函数为空函数。  
  122. //如果定义了CONFIG_SMP宏,则这个setup_per_cpu_areas()函数给每个CPU分配内存,  
  123. //并拷贝.data.percpu段的数据。为系统中的每个CPU的per_cpu变量申请空间。  
  124. setup_per_cpu_areas();  
  125. //定义在include/asm-x86/smp.h。  
  126. //如果是SMP环境,则设置boot CPU的一些数据。在引导过程中使用的CPU称为boot CPU  
  127. smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */  
  128. //设置node 和 zone 数据结构  
  129. //内存管理的讲解:http://blog.chinaunix.net/space.php?uid=361890&do=blog&cuid=2146541  
  130. build_all_zonelists(NULL);  
  131. //初始化page allocation相关结构  
  132. page_alloc_init();  
  133. printk(KERN_NOTICE "Kernel command line: %s/n", boot_command_line);  
  134. //解析内核参数  
  135. //对内核参数的解析:http://hi.baidu.com/yuhuntero/blog/item/654a7411e45ce519b8127ba9.html  
  136. parse_early_param();  
  137. parse_args("Booting kernel", static_command_line, __start___param,  
  138.   __stop___param - __start___param,  
  139.   &unknown_bootoption);  
  140. /* 
  141. * These use large bootmem allocations and must precede 
  142. * kmem_cache_init() 
  143. */  
  144. //初始化hash表,以便于从进程的PID获得对应的进程描述指针,按照实际的物理内存初始化pid hash表  
  145. //这里涉及到进程管理http://blog.csdn.net/satanwxd/archive/2010/03/27/5422053.aspx  
  146. pidhash_init();  
  147. //初始化VFS的两个重要数据结构dcache和inode的缓存。  
  148. //http://blog.csdn.net/yunsongice/archive/2011/02/01/6171324.aspx  
  149. vfs_caches_init_early();  
  150. //把编译期间,kbuild设置的异常表,也就是__start___ex_table和__stop___ex_table之中的所有元素进行排序  
  151. sort_main_extable();  
  152. //初始化中断向量表  
  153. //http://blog.csdn.net/yunsongice/archive/2011/02/01/6171325.aspx  
  154. trap_init();  
  155. //memory map初始化  
  156. //http://blog.csdn.net/huyugv_830913/archive/2010/09/15/5886970.aspx  
  157. mm_init();  
  158. /* 
  159. * Set up the scheduler prior starting any interrupts (such as the 
  160. * timer interrupt). Full topology setup happens at smp_init() 
  161. * time - but meanwhile we still have a functioning scheduler. 
  162. */  
  163. //核心进程调度器初始化,调度器的初始化的优先级要高于任何中断的建立,  
  164. //并且初始化进程0,即idle进程,但是并没有设置idle进程的NEED_RESCHED标志,  
  165. //所以还会继续完成内核初始化剩下的事情。  
  166. //这里仅仅为进程调度程序的执行做准备。  
  167. //它所做的具体工作是调用init_bh函数(kernel/softirq.c)把timer,tqueue,immediate三个人物队列加入下半部分的数组  
  168. sched_init();  
  169. /* 
  170. * Disable preemption - early bootup scheduling is extremely 
  171. * fragile until we cpu_idle() for the first time. 
  172. */  
  173. //抢占计数器加1   
  174. preempt_disable();  
  175. //检查中断是否打开  
  176. if (!irqs_disabled()) {  
  177. printk(KERN_WARNING "start_kernel(): bug: interrupts were "  
  178. "enabled *very* early, fixing it/n");  
  179. local_irq_disable();  
  180. }  
  181. //Read-Copy-Update的初始化  
  182. //RCU机制是Linux2.6之后提供的一种数据一致性访问的机制,  
  183. //从RCU(read-copy-update)的名称上看,我们就能对他的实现机制有一个大概的了解,  
  184. //在修改数据的时候,首先需要读取数据,然后生成一个副本,对副本进行修改,  
  185. //修改完成之后再将老数据update成新的数据,此所谓RCU。  
  186. //http://blog.ednchina.com/tiloog/193361/message.aspx  
  187. //http://blogold.chinaunix.net/u1/51562/showart_1341707.html  
  188. rcu_init();  
  189. //定义在lib/radix-tree.c。  
  190. //Linux使用radix树来管理位于文件系统缓冲区中的磁盘块,  
  191. //radix树是trie树的一种  
  192. //http://blog.csdn.net/walkland/archive/2009/03/19/4006121.aspx  
  193. radix_tree_init();  
  194. /* init some links before init_ISA_irqs() */  
  195. //early_irq_init 则对数组中每个成员结构进行初始化,   
  196. //例如, 初始每个中断源的中断号.其他的函数基本为空.   
  197. early_irq_init();  
  198. //初始化IRQ中断和终端描述符。  
  199. //初始化系统中支持的最大可能的中断描述结构struct irqdesc变量数组irq_desc[NR_IRQS],  
  200. //把每个结构变量irq_desc[n]都初始化为预先定义好的坏中断描述结构变量bad_irq_desc,  
  201. //并初始化该中断的链表表头成员结构变量pend  
  202. init_IRQ();  
  203. //prio-tree是一棵查找树,管理的是什么?  
  204. //http://blog.csdn.net/dog250/archive/2010/06/28/5700317.aspx  
  205. prio_tree_init();  
  206. //初始化定时器Timer相关的数据结构  
  207. //http://www.ibm.com/developerworks/cn/linux/l-cn-clocks/index.html  
  208. init_timers();  
  209. //对高精度时钟进行初始化  
  210. hrtimers_init();  
  211. //软中断初始化  
  212. //http://blogold.chinaunix.net/u1/51562/showart_494363.html  
  213. softirq_init();  
  214. //初始化时钟源  
  215. timekeeping_init();  
  216. //初始化系统时间,  
  217. //检查系统定时器描述结构struct sys_timer全局变量system_timer是否为空,  
  218. //如果为空将其指向dummy_gettimeoffset()函数。  
  219. //http://www.ibm.com/developerworks/cn/linux/l-cn-clocks/index.html  
  220. time_init();  
  221. //profile只是内核的一个调试性能的工具,  
  222. //这个可以通过menuconfig中的Instrumentation Support->profile打开。  
  223. //http://www.linuxdiyf.com/bbs//thread-71446-1-1.html  
  224. profile_init();  
  225. if (!irqs_disabled())  
  226. printk(KERN_CRIT "start_kernel(): bug: interrupts were "  
  227. "enabled early/n");  
  228. //与开始的early_boot_irqs_off相对应  
  229. early_boot_irqs_on();  
  230. //与local_irq_disbale相对应,开中断  
  231. local_irq_enable();  
  232. /* Interrupts are enabled now so all GFP allocations are safe. */  
  233. gfp_allowed_mask = __GFP_BITS_MASK;  
  234. //memory cache的初始化  
  235. //http://my.chinaunix.net/space.php?uid=7588746&do=blog&id=153184  
  236. kmem_cache_init_late();  
  237. /* 
  238. * HACK ALERT! This is early. We're enabling the console before 
  239. * we've done PCI setups etc, and console_init() must be aware of 
  240. * this. But we do want output early, in case something goes wrong. 
  241. */  
  242. //初始化控制台以显示printk的内容,在此之前调用的printk,只是把数据存到缓冲区里,  
  243. //只有在这个函数调用后,才会在控制台打印出内容  
  244. //该函数执行后可调用printk()函数将log_buf中符合打印级别要求的系统信息打印到控制台上。  
  245. console_init();  
  246. if (panic_later)  
  247. panic(panic_later, panic_param);  
  248. //如果定义了CONFIG_LOCKDEP宏,那么就打印锁依赖信息,否则什么也不做  
  249. lockdep_info();  
  250. /* 
  251. * Need to run this when irqs are enabled, because it wants 
  252. * to self-test [hard/soft]-irqs on/off lock inversion bugs 
  253. * too: 
  254. */  
  255. //如果定义CONFIG_DEBUG_LOCKING_API_SELFTESTS宏  
  256. //则locking_selftest()是一个空函数,否则执行锁自测  
  257. locking_selftest();  
  258. #ifdef CONFIG_BLK_DEV_INITRD  
  259. if (initrd_start && !initrd_below_start_ok &&  
  260.    page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {  
  261. printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "  
  262.    "disabling it./n",  
  263.    page_to_pfn(virt_to_page((void *)initrd_start)),  
  264.    min_low_pfn);  
  265. initrd_start = 0;  
  266. }  
  267. #endif  
  268. //页面初始化,可以参考上面的cgroup机制  
  269. page_cgroup_init();  
  270. //页面分配debug启用  
  271. enable_debug_pagealloc();  
  272. //此处函数为空  
  273. kmemtrace_init();  
  274. //memory lead侦测初始化,如何侦测???  
  275. kmemleak_init();  
  276. //  
  277. //Called after the kmem_caches are functional to setup a dedicated  
  278. //cache pool, which has the SLAB_DEBUG_OBJECTS flag set. This flag  
  279. //prevents that the debug code is called on kmem_cache_free() for the  
  280. //debug tracker objects to avoid recursive calls.  
  281. //在kmem_caches之后表示建立一个高速缓冲池,建立起SLAB_DEBUG_OBJECTS标志。???  
  282. debug_objects_mem_init();  
  283. //idr在linux内核中指的就是整数ID管理机制,  
  284. //从本质上来说,这就是一种将整数ID号和特定指针关联在一起的机制  
  285. //idr机制适用在那些需要把某个整数和特定指针关联在一起的地方。  
  286. //http://blogold.chinaunix.net/u3/93255/showart_2524027.html  
  287. idr_init_cache();  
  288. //是否是对SMP的支持,单核是否需要??这个要分析  
  289. setup_per_cpu_pageset();  
  290. //NUMA (Non Uniform Memory Access) policy   
  291. //具体是什么不懂  
  292. numa_policy_init();  
  293. if (late_time_init)  
  294. late_time_init();  
  295. //初始化调度时钟  
  296. sched_clock_init();  
  297. //calibrate_delay()函数可以计算出cpu在一秒钟内执行了多少次一个极短的循环,  
  298. //计算出来的值经过处理后得到BogoMIPS 值,  
  299. //Bogo是Bogus(伪)的意思,MIPS是millions of instructions per second(百万条指令每秒)的缩写。  
  300. //这样我们就知道了其实这个函数是linux内核中一个cpu性能测试函数。  
  301. //http://blogold.chinaunix.net/u2/86768/showart_2196664.html  
  302. calibrate_delay();  
  303. //PID是process id的缩写  
  304. //http://blog.csdn.net/satanwxd/archive/2010/03/27/5422053.aspx  
  305. pidmap_init();  
  306. //来自mm/rmap.c  
  307. //分配一个anon_vma_cachep作为anon_vma的slab缓存。  
  308. //这个技术是PFRA(页框回收算法)技术中的组成部分。  
  309. //这个技术为定位而生——快速的定位指向同一页框的所有页表项。  
  310. anon_vma_init();  
  311. #ifdef CONFIG_X86  
  312. if (efi_enabled)  
  313. efi_enter_virtual_mode();  
  314. #endif  
  315. //创建thread_info缓存  
  316. thread_info_cache_init();  
  317. //申请了一个slab来存放credentials??????如何理解?  
  318. cred_init();  
  319. //根据物理内存大小计算允许创建进程的数量  
  320. //http://www.jollen.org/blog/2006/11/jollen_linux_3_fork_init.html  
  321. fork_init(totalram_pages);  
  322. //给进程的各种资源管理结构分配了相应的对象缓存区  
  323. //http://www.shangshuwu.cn/index.php/Linux内核的进程创建  
  324. proc_caches_init();  
  325. //创建 buffer_head SLAB 缓存  
  326. buffer_init();  
  327. //初始化key的management stuff  
  328. key_init();  
  329. //关于系统安全的初始化,主要是访问控制  
  330. //http://blog.csdn.net/nhczp/archive/2008/04/29/2341194.aspx  
  331. security_init();  
  332. //与debug kernel相关  
  333. dbg_late_init();  
  334. //调用kmem_cache_create()函数来为VFS创建各种SLAB分配器缓存  
  335. //包括:names_cachep、filp_cachep、dquot_cachep和bh_cachep等四个SLAB分配器缓存  
  336. vfs_caches_init(totalram_pages);  
  337. //创建信号队列  
  338. signals_init();  
  339. /* rootfs populating might need page-writeback */  
  340. //回写相关的初始化  
  341. //http://blog.csdn.net/yangp01/archive/2010/04/06/5454822.aspx  
  342. page_writeback_init();  
  343. #ifdef CONFIG_PROC_FS  
  344. proc_root_init();  
  345. #endif  
  346. //它将剩余的subsys初始化.然后将init_css_set添加进哈希数组css_set_table[ ]中.  
  347. //在上面的代码中css_set_hash()是css_set_table的哈希函数.  
  348. //它是css_set->subsys为哈希键值,到css_set_table[ ]中找到对应项.然后调用hlist_add_head()将init_css_set添加到冲突项中.  
  349. //然后,注册了cgroup文件系统.这个文件系统也是我们在用户空间使用cgroup时必须挂载的.  
  350. //最后,在proc的根目录下创建了一个名为cgroups的文件.用来从用户空间观察cgroup的状态.  
  351. //http://blogold.chinaunix.net/u1/51562/showart_1736813.html  
  352. cgroup_init();  
  353. //http://blogold.chinaunix.net/u1/51562/showart_1777937.html  
  354. cpuset_init();  
  355. 进程状态初始化,实际上就是分配了一个存储线程状态的高速缓存  
  356. taskstats_init_early();  
  357. delayacct_init();  
  358. //此处为一空函数  
  359. imv_init_complete();  
  360. //测试CPU的各种缺陷,记录检测到的缺陷,以便于内核的其他部分以后可以使用他们工作。  
  361. check_bugs();  
  362. //电源相关的初始化  
  363. //http://blogold.chinaunix.net/u/548/showart.php?id=377952  
  364. acpi_early_init(); /* before LAPIC and SMP init */  
  365. //  
  366. sfi_init_late();  
  367. ftrace_init();  
  368. /* Do the rest non-__init'ed, we're now alive */  
  369. //创建1号进程,详细分析之  
  370. rest_init();  
  371. }  
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值