分类:
2011-08-18 16:12:41
原文地址:Linux内核的引导和启动过程 作者:platinaluo
先给大家来个图来总体认识一下Linux内核的引导过程,然后详细介绍。
这个图是X86 PC上的Linux 内核的引导过程,在嵌入式系统上的Linux内核的引导过程基本类似。不同只是在X86 PC上有一个从BIOS转移到Bootlloader的过程,嵌入式系统往往是复位后就直接运行Bootloader。
从图上可以看出,在系统启动进入与Linux相关代码之前,会经历如下阶段:
1)当系统上电或复位时,CPU会将程序计数器指针赋值为一个特定地址0XFFF0,并执行该地址里存放的指令。在PC中,该地址是存放在BIOS中,它保存在主板上的ROM或Flash中。
2)BIOS运行时安装CMOS的设置定义的启动设备顺利来搜索处于活动状态且可以引导的设备。若从硬盘启动,BIOS会将硬盘MBR(主引导记录)中的内容加载到RAM。当MBR被加载到RAM中之后,BIOS就会将控制权交给MBR。
3)主引导加载程序查找并加载次引导加载程序。它在分区表中查找活动分区,当找到一个活动分区时,扫描分区表中的其他分区,一确保它们都不是活动的。当这个过程验证完成之后,就将活动分区的引导记录从这个设备中读入RAM并执行它。
4)次引导加载程序加载Linux内核和可选的初始RAM磁盘,将控制权交给Linux内核源代码。
5)运行被加载的内核,并启用用户空间应用程序。
下面都上边加到linux内核的步骤5进行更详细的分析,它主要是完成启动内核并运行用户空间的init进程的功能。
当内核映像被加载到RAM之后,Bootloader的控制权被释放。内核映像并不是可直接执行的目标代码,而是一个压缩过的zImage(小内核)或者bzImage(大内核)。当bzImage(用于i386映像)被调用时它从/arch/i386/boot/head.S的start汇编例程开始执行。这个例程进行一些基本的硬件配置,并调用/arch/i386/boot/compressed/head.S中的 startup_32例程。它设置一个基本的的运行环境(如堆栈)后清除BSS段,调用/arch/i386/boot/compressed/misc.c中的decompressed_kernel()解压缩内核。如下图:
内核被解压缩到内存中之后会再调用/arch/i386/kernel/head.S文件中的startup_32例程,这个新的例程会初始化页表,并启用内存分页机制,接着为任何可选的浮点单元(FPU)检测CPU的类型,并将其存储起来供以后使用。
这些都在做完以后,/init/main.c中的start_kernel()函数被调用,进入到与体系结构无关的Linux内核部分。
start_kernel()函数会调用一系列初始化函数来设置终端,执行进一步的内存配置。之后,/arch/i386/kernel/process.c中kernel_thread()被调用一启动第一个核心线程,该线程执行init()函数,而原执行序列会调用cpu_idle(),等待调度。
作为核心线程的init()函数完成外设及其驱动程序的加载和初始化,挂载根文件系统。init()打开/dev/console设备,重定向stdin,stdout和stderr到控制台。之后,它搜索文件系统中的init程序(当然也可以用"init="命令行参数制定init程序),并使用execve()系统调用执行init程序。其中搜索的顺序为/sbin/init,/etc/init,/bin/init和/bin/sh。在嵌入式系统中,多数情况下,可以给内核传入一个简单的shell脚本来启动必需的嵌入式应用程序。
走到这里,终于把linux内核的引导和启动过程给走完了,而init()对应的由start_kernel()创建的第一个线程也进入到用户模式。
注:本文参考了人民邮电出版社《Linux设备驱动开发详解》一书,再次表示感谢。