无论是Linux还是Windows,在加电后的第一步都是先运行BIOS(Basic Input/Output System)程序——不知道是不是所以的电脑系统都是如此。BIOS保存在主板上的一个non-volatile(即非易失)存储器,如PROM,EPROM,Flash等。——以前的BIOS一般都是只读的,现代的系统中,允许刷新BIOS程序。它的任务就是简单的初始化和识别系统硬件设备,如CPU,内存,输入/输出设备,外部存储设备等。然后找到bootloader的位置,并加载bootloader,将PC的控制权交给bootloader,完成后面的复杂的系统初始化任务。
但是在系统启动之前,系统如何启动BIOS呢?所以系统启动的过程,也被称为自举。虽然没有“先有鸡还是先有蛋”那么复杂,但是这里也有一个矛盾。PC是这样解决这个问题的。将CPU设计成加电以后,就从一个特殊的固定的地址开始执行指令,那么BIOS的位置就放在这里,也就是存储BIOS的ROM的起始地址就是这个固定的地址,用以保证BIOS程序可以在加电时被直接执行。

这里有两个问题:
1. BIOS的存储器地址如何决定的?
2. 现在多处理器的情况下,BIOS是如何执行的?

下面以Intel CPU为例,简单说明一下流程:
Intel在初始化的时候将CPU分为两类,即BSP(Bootstrap Processor)与APs(Application Processors)。从名字上既可看出两类CPU的作用。在启动的时候,首先由硬件动态选择一个总线上的CPU为BSP,那么剩下的CPU则都为AP。由BSP执行BIOS程序,初始化环境以及APs,然后还是有BSP执行操作系统的初始化代码。
Intel CPU的第一条语句的固定地址为0xFFFF FFF0,然后BIOS的存储器被hard-map到这个内存地址。这样当CPU开始执行时,实际上执行的就是BIOS程序。

由于BIOS的存储器不会太大,所以程序一般不会太复杂,那么不大可能实现加载操作系统的操作,只能完成简单的初始化工作。这时,只能借助于外部存储器了。可是外部存储器的读取是依赖于文件系统的。而BIOS程序既然比较简单,那么是不可能去支持文件系统的,更何况有各种各样的文件系统,不可能去一一支持。这时,还是只能依赖于硬编码,必须定义一个固定的外部存储器的地址——硬盘的第一个扇区的512字节——被称为MBR(Master Boot Record)——为什么是512字节呢?按照我的理解,一般情况下一个扇区都被设置为512字节,而硬盘操作的最小单元即为一个扇区。虽然可以设置更大的扇区,但是作为一个统一的程序来说,使用惯例512是一个不错的选择。

BIOS的最后一项任务就是将MBR读入到内存中,且起始地址固定为0x7c00,然后对MBR的最后两个字节进行验证,必须为0x55和0xAA,以保证这512字节为MBR。验证通过后,则跳转到0x7c00处开始执行。这样MBR就开始执行。——这里,我有两个问题,为什么是7c00和0x55和0xAA呢?目前没有找到当初选择这两个值的解释。我依稀记得选择0x55,0xaa是因为这个值比较特殊,利于校验,但是为什么利于校验却不记得了。

MBR保存了分区表(MBR并不存在于任何一个分区中,而是处于分区之上),以及一个用于装载操作系统启动程序的小程序。MBR首先会确定活动分区,然后使用BIOS将这个活动分区的启动扇区——仍然是第一个扇区512字节,最后跳转到加载该启动扇区的内存地址处。这样就将PC的控制器转移到这个启动扇区的程序手中(即真正的bootloader)。一般来说,这个启动程序也要求被加载到0x7c00这个地址。可是这个地址之前已经加载了MBR,如果再加载这个启动程序,那么必然冲突。所以MBR实际上在开始的时候,先对自己做了relocate,将自己拷贝到另外一个地址,然后从那个地址开始执行,这样就避免了冲突。

下面就进入了真正的bootloader了,对于Linux来说,一般就是LILO和GRUB,下面以最常用的GRUB为例。

GRUB的启动分为三个阶段stage1,stage1.5和stage2,这三个阶段也被分为三个文件(在某些情况下,可以没有stage1和stage1.5)。其中stage1可以嵌入到MBR中,即MBR的头446个字节(后面为分区表64字节,0x55和0xAA两个校验字节),也可以存储在活动分区的第一个扇区512字节,
然后由MBR来加载。所以stage1最多为512字节,如果存储在MBR中,则只能最大为446字节。stage1中保存了stage1.5的地址,并负责加载stage1.5的前512字节。之所以stage1只能加载512字节,是为了遵循MBR的规则。

进入stage1.5,由于只加载了前512字节,所以stage1.5首先要负责把剩余部分代码,由自己加载到内存中。对于stage1.5来说,它可以识别和支持文件系统。可以查看/boot/grub目录下,有多个后缀为stage1.5文件,其前缀即为支持的文件系统,也就是说要支持一个文件系统,就有一个对应的stage1.5文件。至于加载哪个文件,已经硬编码在stage1中。这个文件系统为stage2所在的文件系统。stage2文件是真正保存在文件系统中的。这样通过对应的stage1.5文件,就可以正确加载stage2文件。为什么会有stage1.5这个阶段呢?主要是当stage2不连续或者需要在stage2前,对文件系统做些特殊处理。如果没有这样的需求,完全可以避免stage1.5。

stage2文件为最主要的加载代码,这时由于已经stage1.5已经支持文件系统了,所以stage2可以比较大。stage2来实现GRUB的各种功能,这里就不列举了,感兴趣的同学可以自己查看GRUB的手册。stage2首先需要找到GRUB的配置文件,来决定如何加载操作系统。对于GRUB的配置与本文的主题联系并不紧密,我个人也对其兴趣不大。

GRUB不仅要复杂加载kernel,还要负责加载Initial Ram Disk,又被成为initrd。其目的主要是为了保证一个小体积的内核。initrd为一个简单的文件系统,它包含了一些内核必要的文件和模块。这样,首先将initrd挂载为一个根系统,然后kernel利用这个基本的系统,来检测环境,加载更多的必要的模块。在完成所有的加载后,这时kernel已经完全准备就绪。那么initrd对于kernel来说,已经不需要了。这时,kernel会将initrd从根/上卸载,并挂载上真正的根系统,并执行正常的启动程序。

Linux内核加载全流程的更多相关文章

  1. 修改linux内核加载顺序

    修改内核启动顺序:1.查看当前系统所有的内核# awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/gr ...

  2. 内核加载与linux的grub

    计算机系统的启动是一个复杂的过程,启动过程大致可以分为以下几个阶段: +------计算机系统启动流程----------------------------- ------------------- ...

  3. bootrom/spl/uboot/linux逐级加载是如何实现的?

    关键词:bootrom.spl.uboot.linux.mksheader.sb_header.mkimage.image_header_t等等. 首先看一个典型的bootrom->spl-&g ...

  4. bootm命令中地址参数,内核加载地址以及内核入口地址

    bootm命令只能用来引导经过mkimage构建了镜像头的内核镜像文件以及根文件镜像,对于没有用mkimage对内核进行处理的话,那直接把内核下载到连接脚本中指定的加载地址0x30008000再运行就 ...

  5. Android引入高速缓存的异步加载全分辨率

    Android引进高速缓存的异步加载全分辨率 为什么要缓存 通过图像缩放,我们这样做是对的异步加载优化的大图,但现在的App这不仅是一款高清大图.图.动不动就是图文混排.以图代文,假设这些图片都载入到 ...

  6. Linux内核网络报文简单流程

    转:http://blog.csdn.net/adamska0104/article/details/45397177 Linux内核网络报文简单流程2014-08-12 10:05:09 分类: L ...

  7. Linux驱动之内核加载模块过程分析

    Linux内核支持动态的加载模块运行:比如insmod first_drv.ko,这样就可以将模块加载到内核所在空间供应用程序调用.现在简单描述下insmod first_drv.ko的过程 1.in ...

  8. Linux开机加载过程

    2015-01-06 10:29:13   目录 1 开机加载简介 2 常规加载流程 2.1 加载BIOS 2.2 读取MBR 2.3 boot loader 2.4 加载内核 2.5 init依据i ...

  9. Linux0.11内核--加载可执行二进制文件之3.exec

    最后剩下最核心的函数do_execve了,由于这里为了简单起见我不分析shell命令的情况, /* * 'do_execve()'函数执行一个新程序. */ //// execve()系统中断调用函数 ...

随机推荐

  1. Allegro从.brd文件中导出器件封装

    打开.brd文件,File→Export→Libraries,除了No libraries dependencies之外,所有选项都勾选上,设定好存放路径之后,Export. 注意事项: 1. 一般的 ...

  2. 【数据结构和算法】 O(1)时间取得栈中的最大 / 最小元素值

    常数时间取得栈中的元素最大值和最小值,我们可以想到当push的时候比较一下,如果待push元素值小于栈顶元素,则更新min值,最大值亦然. 这样有个问题就是当pop的时候,就没了最大最小值. 于是上网 ...

  3. bzoj 2959 长跑(LCT+BCC+并查集)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2959 [题意] n个点,提供操作:连边,修改点权,查询自定义边的方向后起点a终点b能经 ...

  4. Some_problem_with_octopress

    今天我总算是使用上了高大上的octopress了,不容易啊,现在我把之前的博客全部搬到了octopress上了,在github上办博客让我不用再担心流量和广告了!---爽啊 我使用octopress时 ...

  5. PowerMock简介

    PowerMock 写单元测试可能有些难.有时仅仅为了可测试性的目标而不得不牺牲好的设计.通常可测试性和好的测试之间是一致的,但是并不总是如此.比如,由于现存框架的限制,final classes.m ...

  6. Codevs No.1052 地鼠游戏

    2016-05-31 18:22:32 题目链接: 地鼠游戏 Codevs No.1245 题目大意: 打地鼠,一开始所有地鼠都出现,但是维持的时间(s)和击中所得的积分各不同,求出采用最优策略(1s ...

  7. SPOJ 375 (树链剖分 - 边权剖分 - 修改单边权)

    题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28982#problem/I 给你一棵有边权的树,有两个操作:一个操作是输出l到 ...

  8. 在Linux下部署activemq

    今天的任务就是在一台新的服务器上继续部署activemq.其实都蛮简单的.首先先下载包:115U盘下载 2 上传到linux下的某个文件夹下.解压缩 tar -zxvf apache-activemq ...

  9. ASP.NET MVC- 使用PageList.Mvc分页

    ASP.NET MVC中进行分页的方式有多种,在NuGet上有提供使用PagedList.PagedList.Mvc进行分页. 1. 通过NuGet引用PagedList.Mvc 在安装引用Paged ...

  10. .net版本之间的关系

    net framework 2.0,3.0与3.5三个版本之间关系如下: .net framework 2.0 = CLR 2.0 + FCL(framework class library) .ne ...