1. 介绍
引导过程就是简单意义上的UNIX;通过它能看到各种不同的组件和设计模式(它们构成了UNIX的全部)。在这里我们打算看一下每次按下电源按钮之后OS是如何启动的。
2. 五部引导过程概述
可将引导(启动)过程划分为5个主要步骤:
1) 固件:硬件自识别(PROM/BIOS)
2) Bootloader(自举操作系统载入器)
3) 内核:初始化和控制转移
4) Init和初始化脚本
5) 交给管理员:其他杂项
3. 固件—硬件自识别
固件是一系列特殊的指令,介于硬件和软件层之间,负责这两者之间的相互传译。尽管听起来是一件微不足道的工作,但它实际的工作却举足轻重:固件传译的不仅是语言,它还要传译核心元素。硬件只理解电子脉冲;软件(在其最基本的级别)只理解1和0的数据流;固件是两者的一种合成,在硬件中永久地(或半永久地)嵌入了软件形式的例程。它代表了用户同计算机进行交互的最基本的级别(原始的硬件插拔动作除外)。
3.1 固件的一些实例
在同固件打交道的过程中,可能会遇到以下术语:
- BIOS
- PROM/EPPROM/EEPPROM
- NVRAM
- CMOS
它们的共同特点是:
- 所有例程都是非易失性的芯片
- 所有都采用与OS无关的技术。它们的设计和行动独立于其中存放的内容
- 都具有类似的功能:维持基本的、简单的软件和驱动程序,以及它们相关的一些参数,还提供对键盘、显示器和硬盘的基本访问
- 都采取了专门的设计,在出现磁盘/外设故障时,保证本身仍然可用,不会被破坏;即使不访问硬盘或移动式磁盘,它们存储的程序也能运行。
- 所有都容纳着ROM bootstrap routine(启动程序,用来读入存放于MBR处的bootloader)程序
3.2 固件的工作
固件负责硬件的自查(查找硬件的存在),并让系统知道自己有哪些组件可用。这意味着,固件必须有相应的例程来检查连接的设备以及内建的控制器。但要注意的是:在内核装载恰当的驱动程序和模块之前,有的硬件仍是看不见的。
标识出设备之后,会对它们的基本功能进行测试,通常用它们的内嵌例程来进行。这些步骤统称为POST—开机自检(Power-On Self-Test)。
一旦确认并验证了系统的硬件,固件便开始搜索可引导设备—要么一个内部列表,要么用一个环境变量。
找到一个可引导设备后,会检查它的引导扇区(或引导块),以获取更多的信息。这些信息会传给OS载入程序。
4. bootloader—载入OS
想象系统现在已被唤醒,并回答了第一个问题:“我是什么”,下一步是找到“大脑”,并回答:“我是谁?”。对于任何UNIX系统,基本身份信息都是封装在本地OS中的。
Bootloader这个术语是“bootstrap Operating System loader”的简称,即“自举操作系统的装入器”。
何谓“自举”
根据Merriam-Webster在线字典的说法,首次使用bootstrap(自举)这个术语是在1926年。它的来源确实是一个人试图通过拉自己的鞋带(bootstrap),从而将自己举高。这也是“自力更生”(pull yourself up by your bootstrap)这一谚语的来历。
通常,“自举”意味着利用尽可能少的外部介入和资源,从而让某种东西工作起来。对计算机来说,它的自举意味着使用少量的、独立的、内部的功能,从而初始化,并将控制权交给操作系统。
4.1 bootloader的工作
Bootloader是UNIX实际启动你的系统的地方。在这里,你将启动一系列软件过程,最终将系统控制权从硬件交给OS。
为了使bootloader能够保存到固件中,关键的一点是它尽可能小,它必须能装到磁盘的引导扇区。同时,这也意味着它必须相当简单,只需采用足够的逻辑,能够载入和运行OS引导序列的下一个程序即可。为了找到bootloader,要么对设备进行顺序搜索,要么检查由管理员提供的一个列表。注意bootloader的相关设置保存在系统固件中。
在UNIX的世界中,会经常激将复杂的工作分解成小的,分散的任务,分别由小的,专用的程序来执行。这是UNIX十分重要的一个特点。
5 内核——初始化和控制转移
在UNIX世界中,有两条恒定不变的真理。首先,UNIX中存在的任何东西都是一个文件;其次,UNIX中发生的任何事情都是一个进程。内核是一个编译好的可执行文件,在外观上同其他的可执行文件没什么区别。但只要在引导期间把它装入内存,它就开始控制其他所有系统进程和进程调度器——换句话说,它控制着发生的一切事情。
然而,“控制一切事情”并不意味着“做一切事情”——这是非常重要的区别。内核实际上能做的事情是非常有限的。
现在我们称内核是一个文件,但文件的位置却是不定的。事实上文件完全可能存在一个远程服务器上,根本不在你的系统中。但就实际用途来说,内核在哪里并不重要,只要能找到并载入它即可。
为简化讨论,我们认为在引导阶段最常见的下一个步骤是:载入存放在本地硬盘上的一个本地内核。
5.1 内核的工作
内核为UNIX系统中发生的其他一切事情提供了环境。它是一个基本框架,来自文件系统的任何其他命令都要布置其中。它构建了一个虚拟机,硬件和软件在此相会交互。内核负责处理所有系统资源的分配,包括内存、虚拟内存、CPU时间共享、交换和分页;它还负责系统内部的通信,不管是在软件进程之间还是在软件到硬件指令之间。为了对后者进行管理,内核可提供永久存储空间的访问渠道——包括硬盘及其文件。
文件(包括内核及其附属内容)保存在一种随机存储的介质上,由文件系统对其进行结构化和管理。文件系统必须逻辑地装入,以便系统能够进行文件存取。当然,和系统中的其他任何东西一样,最终都是由内核来控制文件系统。
这听起来似乎是一个先有鸡还是先有蛋的问题:你要先有文件系统才能装入内核;但除非你有一个内核,否则无法装入文件系统…这里的诀窍在于,bootloader会在适当的时候介入,由它装入恰当的文件系统,以便OS能够正确地载入和接管。
6 init进程和初始化脚本
现在,内核已经装入。系统已经准备好回答这样一个问题:“现在做什么?”随后,要由init进程来回答这一问题。创建init进程是在引导过程中内核直接进行的最后一项干预。
Init启动时,它会执行各个默认启动例程,从而继续引导过程(包括检查和装入文件系统、启动守护进程等)。在一个正常运行的UNIX系统中,init是第一个可以存在的进程。正因为如此,它的进程ID肯定是1。
6.1 init的工作
Init进程负责所有的核心系统功能的有序启动(和关闭),这些功能位于内核之外。为此,它必须有能力创建(或孵化spawn)大量进程。
6.2 init的机制和特点:inittab
当然,init仅仅孵化进程是不够的——它们还必须为进程赋予权限。这正是管理员真正能控制的第一个项目:取决于系统用什么“运行级别”(runlevel)或者说“运行模式”来工作。它们会表现出不同的行为。
在inittab中,所有条目采取如下格式:
id: run-level : action : process
第一个字段id是条目的标志符。
第二个字段指运行级别。指该进程能在哪一个运行级别上被启动。一般UNIX的运行级别有0~9个级别,在run-level字段上看可以用多个运行级别的组合,如023。
第三个字段描述了你想让系统做什么事情。一些比较常用的值有:
- Boot——只在系统引导时运行进程
- Once——只有进入指定的运行级别后才运行进程
- Respawn——一旦进程终止,便重新启动进程
- Wait——类似于once,但在继续前,会等待进程结束。
第四个字段指定了要运行的可执行程序的完整路径,以及应传递给它的任意参数(只能用空格分隔)。