早期时,启动一台计算机意味着要给计算机喂一条包含引导程序的纸带,或者手工使用前端面板地址/数据/控制开关来加载引导程序。尽管目前的计算机已经装备了很多工具来简化引导过程,但是这一切并没有对整个过程进行必要的简化。

让我们先从高级的视角来查看 Linux 引导过程,这样就可以看到整个过程的全貌了。然后将回顾一下在各个步骤到底发生了什么。在整个过程中,参考一下内核源代码可以帮助我们更好地了解内核源代码树,并在以后对其进行深入分析。

概述

图 1 是我们在 20,000 英尺的高度看到的视图。

图 1. Linux 引导过程在 20,000 英尺处的视图

当 系统首次引导时,或系统被重置时,处理器会执行一个位于已知位置处的代码。在个人计算机(PC)中,这个位置在基本输入/输出系统(BIOS)中,它保存 在主板上的闪存中。嵌入式系统中的中央处理单元(CPU)会调用这个重置向量来启动一个位于闪存/ROM 中的已知地址处的程序。在这两种情况下,结果都是相同的。因为 PC 提供了很多灵活性,BIOS 必须确定要使用哪个设备来引导系统。稍后我们将详细介绍这个过程。

当找到一个引导设备之后,第一阶段的引导加载程序就被装入 RAM 并执行。这个引导加载程序在大小上小于 512 字节(一个扇区),其作用是加载第二阶段的引导加载程序。

当第二阶段的引导加载程序被装入 RAM 并执行时,通常会显示一个动画屏幕,并将 Linux 和一个可选的初始 RAM 磁盘(临时根文件系统)加载到内存中。在加载映像时,第二阶段的引导加载程序就会将控制权交给内核映像,然后内核就可以进行解压和初始化了。在这个阶段 中,第二阶段的引导加载程序会检测系统硬件、枚举系统链接的硬件设备、挂载根设备,然后加载必要的内核模块。完成这些操作之后启动第一个用户空间程序(init),并执行高级系统初始化工作。

这就是 Linux 引导的整个过程。现在让我们深入挖掘一下这个过程,并深入研究一下 Linux 引导过程的一些详细信息。

回页首

系统启动

系 统启动阶段依赖于引导 Linux 系统上的硬件。在嵌入式平台中,当系统加电或重置时,会使用一个启动环境。这方面的例子包括 U-Boot、RedBoot 和 Lucent 的 MicroMonitor。嵌入式平台通常都是与引导监视器搭配销售的。这些程序位于目标硬件上的闪存中的某一段特殊区域,它们提供了将 Linux 内核映像下载到闪存并继续执行的方法。除了可以存储并引导 Linux 映像之外,这些引导监视器还执行一定级别的系统测试和硬件初始化过程。在嵌入式平台中,这些引导监视器通常会涉及第一阶段和第二阶段的引导加载程序。

提取 MBR 的信息

要查看 MBR 的内容,请使用下面的命令:

# dd if=/dev/hda of=mbr.bin bs=512 count=1 # od -xa mbr.bin

这个 dd 命令需要以 root 用户的身份运行,它从 /dev/hda(第一个 IDE 盘) 上读取前 512 个字节的内容,并将其写入 mbr.bin 文件中。od 命令会以十六进制和 ASCII 码格式打印这个二进制文件的内容。

在 PC 中,引导 Linux 是从 BIOS 中的地址 0xFFFF0 处开始的。BIOS 的第一个步骤是加电自检(POST)。POST 的工作是对硬件进行检测。BIOS 的第二个步骤是进行本地设备的枚举和初始化。

给定 BIOS 功能的不同用法之后,BIOS 由两部分组成:POST 代码和运行时服务。当 POST 完成之后,它被从内存中清理了出来,但是 BIOS 运行时服务依然保留在内存中,目标操作系统可以使用这些服务。

要引导一个操作系统,BIOS 运行时会按照 CMOS 的设置定义的顺序来搜索处于活动状态并且可以引导的设备。引导设备可以是软盘、CD-ROM、硬盘上的某个分区、网络上的某个设备,甚至是 USB 闪存。

通常,Linux 都是从硬盘上引导的,其中主引导记录(MBR)中包含主引导加载程序。MBR 是一个 512 字节大小的扇区,位于磁盘上的第一个扇区中(0 道 0 柱面 1 扇区)。当 MBR 被加载到 RAM 中之后,BIOS 就会将控制权交给 MBR。

回页首

第一阶段引导加载程序

MBR 中的主引导加载程序是一个 512 字节大小的映像,其中包含程序代码和一个小分区表(参见图 2)。前 446 个字节是主引导加载程序,其中包含可执行代码和错误消息文本。接下来的 64 个字节是分区表,其中包含 4 个分区的记录(每个记录的大小是 16 个字节)。MBR 以两个特殊数字的字节(0xAA55)结束。这个数字会用来进行 MBR 的有效性检查。

图 2. MBR 剖析

主 引导加载程序的工作是查找并加载次引导加载程序(第二阶段)。它是通过在分区表中查找一个活动分区来实现这种功能的。当找到一个活动分区时,它会扫描分区 表中的其他分区,以确保它们都不是活动的。当这个过程验证完成之后,就将活动分区的引导记录从这个设备中读入 RAM 中并执行它。

回页首

第二阶段引导加载程序

次引导加载程序(第二阶段引导加载程序)可以更形象地称为内核加载程序。这个阶段的任务是加载 Linux 内核和可选的初始 RAM 磁盘。

GRUB 阶段引导加载程序

/boot/grub 目录中包含了 stage1stage1.5stage2 引导加载程序,以及很多其他加载程序(例如,CR-ROM 使用的是 iso9660_stage_1_5)。

在 x86 PC 环境中,第一阶段和第二阶段的引导加载程序一起称为 Linux Loader(LILO)或 GRand Unified Bootloader(GRUB)。由于 LILO 有一些缺点,而 GRUB 克服了这些缺点,因此下面让我们就来看一下 GRUB。(有关 GRUB、LILO 和相关主题的更多内容,请参阅本文后面的 参考资料 部分的内容。)

关 于 GRUB,很好的一件事情是它包含了有关 Linux 文件系统的知识。GRUB 不像 LILO 一样使用裸扇区,而是可以从 ext2 或 ext3 文件系统中加载 Linux 内核。它是通过将两阶段的引导加载程序转换成三阶段的引导加载程序来实现这项功能的。阶段 1 (MBR)引导了一个阶段 1.5 的引导加载程序,它可以理解包含 Linux 内核映像的特殊文件系统。这方面的例子包括 reiserfs_stage1_5(要从 Reiser 日志文件系统上进行加载)或 e2fs_stage1_5(要从 ext2 或 ext3 文件系统上进行加载)。当阶段 1.5 的引导加载程序被加载并运行时,阶段 2 的引导加载程序就可以进行加载了。

当阶段 2 加载之后,GRUB 就可以在请求时显示可用内核列表(在 /etc/grub.conf 中进行定义,同时还有几个软符号链接 /etc/grub/menu.lst/etc/grub.conf)。我们可以选择内核甚至修改附加内核参数。另外,我们也可以使用一个命令行的 shell 对引导过程进行高级手工控制。

将第二阶段的引导加载程序加载到内存中之后,就可以对文件系统进行查询了,并将默认的内核映像和 initrd 映像加载到内存中。当这些映像文件准备好之后,阶段 2 的引导加载程序就可以调用内核映像了。

回页首

内核

GRUB 中的手工引导

在 GRUB 命令行中,我们可以使用 initrd 映像引导一个特定的内核,方法如下:

grub> kernel /bzImage-2.6.14.2
[Linux-bzImage, setup=0x1400, size=0x29672e]

grub> initrd /initrd-2.6.14.2.img
[Linux-initrd @ 0x5f13000, 0xcc199 bytes]

grub> boot

Uncompressing Linux... Ok, booting the kernel.

如果您不知道要引导的内核的名称,只需使用斜线(/)然后按下 Tab 键即可。GRUB 会显示内核和 initrd 映像列表。


内核映像被加载到内存中,并且阶段 2
的引导加载程序释放控制权之后,内核阶段就开始了。内核映像并不是一个可执行的内核,而是一个压缩过的内核映像。通常它是一个
zImage(压缩映像,小于 512KB)或一个 bzImage(较大的压缩映像,大于 512KB),它是提前使用 zlib
进行压缩过的。在这个内核映像前面是一个例程,它实现少量硬件设置,并对内核映像中包含的内核进行解压,然后将其放入高端内存中,如果有初始 RAM
磁盘映像,就会将它移动到内存中,并标明以后使用。然后该例程会调用内核,并开始启动内核引导的过程。

当 bzImage(用于 i386 映像)被调用时,我们从 ./arch/i386/boot/head.Sstart 汇编例程开始执行(主要流程图请参看图 3)。这个例程会执行一些基本的硬件设置,并调用 ./arch/i386/boot/compressed/head.S 中的 startup_32 例程。此例程会设置一个基本的环境(堆栈等),并清除 Block Started by Symbol(BSS)。然后调用一个叫做 decompress_kernel 的 C 函数(在 ./arch/i386/boot/compressed/misc.c 中)来解压内核。当内核被解压到内存中之后,就可以调用它了。这是另外一个 startup_32 函数,但是这个函数在 ./arch/i386/kernel/head.S 中。

在这个新的 startup_32 函数(也称为清除程序或进程 0)中,会对页表进行初始化,并启用内存分页功能。然后会为任何可选的浮点单元(FPU)检测 CPU 的类型,并将其存储起来供以后使用。然后调用 start_kernel 函数(在 init/main.c 中),它会将您带入与体系结构无关的 Linux 内核部分。实际上,这就是 Linux 内核的 main 函数。

图 3. Linux 内核 i386 引导的主要函数流程

通过调用 start_kernel,会调用一系列初始化函数来设置中断,执行进一步的内存配置,并加载初始 RAM 磁盘。最后,要调用 kernel_thread(在 arch/i386/kernel/process.c 中)来启动 init 函数,这是第一个用户空间进程(user-space process)。最后,启动空任务,现在调度器就可以接管控制权了(在调用 cpu_idle 之后)。通过启用中断,抢占式的调度器就可以周期性地接管控制权,从而提供多任务处理能力。

在内核引导过程中,初始 RAM 磁盘(initrd)是由阶段 2 引导加载程序加载到内存中的,它会被复制到 RAM 中并挂载到系统上。这个 initrd 会作为 RAM 中的临时根文件系统使用,并允许内核在没有挂载任何物理磁盘的情况下完整地实现引导。由于与外围设备进行交互所需要的模块可能是 initrd 的一部分,因此内核可以非常小,但是仍然需要支持大量可能的硬件配置。在内核引导之后,就可以正式装备根文件系统了(通过 pivot_root):此时会将 initrd 根文件系统卸载掉,并挂载真正的根文件系统。

decompress_kernel 输出

函数 decompress_kernel 就是显示我们通常看到的解压消息的地方:

Uncompressing Linux... Ok, booting the kernel.

initrd 函数让我们可以创建一个小型的 Linux 内核,其中包括作为可加载模块编译的驱动程序。这些可加载的模块为内核提供了访问磁盘和磁盘上的文件系统的方法,并为其他硬件提供了驱动程序。由于根文件系统是磁盘上的一个文件系统,因此 initrd 函数会提供一种启动方法来获得对磁盘的访问,并挂载真正的根文件系统。在一个没有硬盘的嵌入式环境中,initrd 可以是最终的根文件系统,或者也可以通过网络文件系统(NFS)来挂载最终的根文件系统。

回页首

Init

当内核被引导并进行初始化之后,内核就可以启动自己的第一个用户空间应用程序了。这是第一个调用的使用标准 C 库编译的程序。在此之前,还没有执行任何标准的 C 应用程序。

在桌面 Linux 系统上,第一个启动的程序通常是 /sbin/init。但是这不是一定的。很少有嵌入式系统会需要使用 init 所提供的丰富初始化功能(这是通过 /etc/inittab 进行配置的)。在很多情况下,我们可以调用一个简单的 shell 脚本来启动必需的嵌入式应用程序。

回页首

结束语

与 Linux 本身非常类似,Linux
的引导过程也非常灵活,可以支持众多的处理器和硬件平台。最初,加载引导加载程序提供了一种简单的方法,不用任何花架子就可以引导 Linux。LILO
引导加载程序对引导能力进行了扩充,但是它却缺少文件系统的感知能力。最新一代的引导加载程序,例如 GRUB,允许 Linux 从一些文件系统(从
Minix 到 Reise)上进行引导。

参考资料

学习

获得产品和技术

  • GNU GRUB 是一个具有众多选项和灵活性的引导 shell。
  • LinuxBIOS 是 BIOS 的一个替代品。LinuxBIOS 不但可以引导 Linux,而且它本身就是一个压缩的 Linux 内核。
  • OpenBIOS 是另一个可移植的 BIOS 项目,可以在很多体系结构上进行操作,例如 x86、Alpha 和 AMD64。
  • kernel.org 上可以找到最新的内核树。
  • 使用 IBM 试用版软件 改进您的下一个开发项目,从 developerWorks 上可以直接下载这些软件。

讨论

Linux引导过程的更多相关文章

  1. Linux 引导过程内幕

    转载:http://www.ibm.com/developerworks/cn/linux/l-linuxboot/index.html   从主引导记录到第一个用户空间应用程序的指导 引导 Linu ...

  2. 【转】Linux 高级的视角来查看Linux引导过程

    [原文]https://www.toutiao.com/i6594210975480545800/ 1.概述 图 1 是我们在20,000 英尺的高度看到的视图. 当系统首次引导时,或系统被重置时,处 ...

  3. Linux引导过程和服务过程

    目录 一.Linux操作系统引导过程 1.1.开机自检 1.2.MBR引导 1.3.GRUB菜单 1.4.加载Linux内核 1.5.init进程初始化 二.系统初始化进程 2.1.init进程 2. ...

  4. Linux引导过程与服务控制

    一:系统引导流程: 开机自检(BIOS)-->MBR引导-->GRUB菜单-->加载内核(kernel)-->init进程初始化  二:系统引导级别: 0 poweroff.t ...

  5. Linux引导过程!(重点)

    1.开机自检:服务器开机后,将根据主板BIOS (基本输入系统),中的设置对CPU .内存.显卡.键盘等设备进行初步检测,并初始化部分硬件. 2.MBR 引导:当从本机硬盘中启动系统时,首先根据硬盘中 ...

  6. Linux学习笔记之Linux启动引导过程

    早期时,启动一台计算机意味着要给计算机喂一条包含引导程序的纸带,或者手工使用前端面板地址/数据/控制开关来加载引导程序.尽管目前的计算机已经装备了很多工具来简化引导过程,但是这一切并没有对整个过程进行 ...

  7. Linux学习笔记:系统启动引导过程

    Linux系统启动引导过程 近期发现自己在仅仅是掌握上有几个比較硬的伤: 一.知识体系碎片,比方Linux,这学点那学点,结果没有成体系,串不起来: 二.记忆时间短暂,非常多的内容学了就忘,最后的结果 ...

  8. Linux引导启动程序 - boot

    主要描述 boot/目录中的三个汇编代码文件,见列表 3-1 所示.正如在前一章中提到的,这三个 文件虽然都是汇编程序,但却使用了两种语法格式.bootsect.s 和 setup.s 采用近似于 I ...

  9. [svc]linux启动过程及级别

    Unix目录结构的来历 Linux 的启动流程 Linux 引导过程内幕 嵌入式系统 Boot Loader 技术内幕 centos6使用chkconfig治理服务和其原理 centos7的服务治理- ...

随机推荐

  1. 4-oracle11g安装

    1.导入安装包,解压 [root@ocp Desktop]# unzip p10404530_112030_Linux-x86-64_1of7.zip [root@ocp Desktop]# unzi ...

  2. axios应用

    Skip to content     Features Business Explore Marketplace Pricing Sign in or Sign up     Watch929 St ...

  3. Django笔记 —— 基础部分总结

    最近在学习Django,打算玩玩网页后台方面的东西,因为一直很好奇但却没怎么接触过.Django对我来说是一个全新的内容,思路想来也是全新的,或许并不能写得很明白,所以大家就凑合着看吧- 本篇笔记(其 ...

  4. jmeter处理响应结果中文乱码

    1. 在线程下面添加后置处理器BeanShell PostProcessor,增加script:prev.setDataEncoding("UTF-8"); 2. 在jmeter. ...

  5. 【js笔记】数组那些事[0]

    js中数组是一个特殊的对象,索引是它的属性,整数索引在内部被转化为字符串类型. 1 数组的创建 new关键字方法:var arr=new Array() var arr=new Array(10); ...

  6. 『AngularJS』理解$Scope

    理解$Scope 执行概要 在AngularJS,一个子scope通常原型继承于它的父scope.应用于这个规则的表达式是一个使用scope:{...}的指令,这将创建一个『孤岛』scope(非原型继 ...

  7. 一个知乎日报pwa

    前几天写了一篇文章关于如何实现一个简单版的pwa应用,端午撸了一个简易版知乎日报pwa. 关于如何写一个pwa,这里就不多介绍了,请移步这里.应用使用vue+vuex+axios,API这里,这里做了 ...

  8. 【多校联合】(HDU6095)Rikka with Competition

    题意:给定$n$个数,代表$n$个选手的能量高低,现在再给一个$k$,任意在$n$个选手中挑取两个选手比赛,如果$|a_i−a_j|>K$,那么能量高的选手获胜,另一个将被淘汰,否则两个人都有机 ...

  9. jmeter无法启动的解决办法

    jmeter下载地址: 链接: https://pan.baidu.com/s/15YhiPH-kNVxISEZ4Mxf_WA     提取码: 25sv jdk 8.0 下载地址: 链接: http ...

  10. python进阶训练

    1.列表,字典,集合解析 from random import randint #列表解析,选出大于0的元素 data=[randint(-10,10)for i in range(10)] resu ...