我一直有一个疑惑——计算机到底是怎么启动的?
而且似乎有点矛盾——要想启动,就必须运行程序,但是计算机没开机怎么加载和运行程序呢?
为了解决这个矛盾,工程师终于想出将一小段程序固化进一块存储设备,电脑只要开机就会开始执行这段程序。
并且上节课中提到了BIOS、GRUB、BootLoader等概念,与之相类似的还有诸如UEFI、MBR、GPT等名词,这些名词都是跟计算机的启动过程有关的。它们都是在操作系统之前进行的,主要完成计算机的硬件检查、初始化、加载操作系统等,现在我们梳理一下它们的关系便于更好的理解操作系统。
按照时间线来说一共有两套启动流程,一种是早期IBM PC时代的BIOS+MBR的启动方式(也称为Legacy方式),比较典型的如Windows XP/7;另一种是如今普遍采用的UEFI+GPT的启动方式,从Windows8开始默认采用这种方式。
Legacy启动流程
Legacy方式(BIOS+MBR)的启动方式大致经历了这样的流程: 我们就顺着整个流程来讲解每个步骤所涉及的概念。BIOS
BIOS是Basic Input and Output System的简称,即基本输入输出系统。它是一段被固化在主板ROM中的程序,掉电不会丢失,其主要功能是为电脑提供最基本的输入输出功能、开机硬件自动检测、设置、初始化。电脑按下开机之后,首先要做的就是加载并运行BIOS。
- 自检和初始化
- 初始化中断相关服务
- 加载引导程序
MBR与PBR
前面提到BIOS加载引导程序时会依次检验存储设备前512Bytes的内容,如果以0x55 0xAA结尾,则这个512Bytes的内容就是MBR,当BIOS找到MBR之后,就会将计算机的控制权交给MBR,并运行其中的程序。 MBR全称为Master Boot Record,译为主引导记录,是存在磁盘的第一个逻辑扇区LBA中(512Byte)的内容,这512Bytes根据功能又分为三个部分:- 1-446Byte:用于加载操作系统的机器码;
- 447-510Byte:MBR磁盘分区表(后讲);
- 511-512Byte:即0x55和0xAA,魔数,用来表示其是MBR;
因此,当进入MBR代码后,通过查看MBR分区表中记录的活动分区是哪一个,就跳到对应的分区执行之后的操作系统加载。
等等,跳到对应的分区是什么意思?
操作系统引导程序是指最终去加载操作系统的一段代码,不同的操作系统所使用的引导程序不同,在WindowsXP之前是NTLDR,WIndows7之后就采用bootmgr,Linux是采用Grub(grldr)。
因此对于Windows系统加载来说,系统启动流程如下:GRUB
GRUB即是前文提到的“操作系统引导程序”的一种。其全称为Grand Unified Boot Loader,主要是用于类Unix操作系统的加载。它允许用户可以在计算机内同时拥有多个操作系统,并在计算机启动时选择希望运行的操作系统。 不过与bootmgr之类的系统不同的是,如果是采用GRUB引导程序,那么GRUB是安装在MBR及其之后的非分区空间中的,GRUB会去读取文件系统,找到其安装目录/boot/grub,并读取其中的grub.cfg,加载需要的模块,最后启动操作系统。下面是我们COMOS的grub.cfg:加载模块命令insmod主要加载了两个模块:part_msdos与ext2。并且我们将根目录设置为hd0,即第一块硬盘,而指明操作系统内核文件在/boot/COSMOS.eki,最后执行boot命令开始加载操作系统。 part_msdos与ext2分别是分区模块和文件系统模块,加载这两个模块主要是为了让引导程序知道文件该去哪找,如果不加载这两个模块,它根本不懂/boot/COSMOS.eki是什么意思! 如果电脑中装了多个系统,那么只需要在上述grub.cfg文件中加入多个选择即可:
//设置HelloOS是启动系统的第一个选项
menuentry 'COSMOS' {
#加载part_msdos、ext2模块
#这是grub的语法
insmod part_msdos
insmod ext2
set root='hd0' #只有一个硬盘,因此设置根目录为hd0(就是hd.vdi)
multiboot2 /boot/COSMOSOS.eki #加载boot目录下的.eki内核文件
boot #加载启动内核文件
}
#设置过时样式为菜单
set timeout_style=menu
#设置过时时间
if [ "${timeout}" = 0 ]; then
set timeout=10 #等待10秒钟自动启动
fi
下图是只有一个选项的GRUB启动界面:
menuentry 'OS1' {
......
}
menuentry 'OS2' {
......
}
UEFI启动流程
在介绍UEFI之前,先讲一下跟前文的GPT分区表。GPT
GPT,全名Globally Unique Identifier Partion Table,即全局唯一标识分区表,又称为GUID分区表。它是UEFI规范的一部分。在之前介绍MBR时可知,MBR最多只能拥有4个分区,并且只能处理最大2T(2^(16+16)*512=2048GB=2TB)的硬盘,超过2T的硬盘就需要使用扩展分区。而GPT最大硬盘容量可达EB,且分区数量几乎无限制(Windows限定为128),来看看它的结构:- 保护MBR即为PMBR,位于磁盘0号扇区,其主要作用是兼容MBR:GPT系统会自动跳过这个扇区,而只支持MBR的系统则会根据PMBR的内容,以Legacy的方式进行启动。
- GPT头即GPT HDR,位于磁盘1号扇区,该扇区在创建GPT磁盘时生成,主要定义分区表起始、结束位置、分区表大小、个数等信息,一句话,就是定义分区的个数和位置。
- 分区表即Partion Table,当前Windows预留了32个扇区来设置分区表,一共可以容纳128个分区表,每个分区表大小为128Byte,每个分区表定义该分区的其实、结束位置。
- 分区区域:即根据分区表划分的存储区域,也是用户直接操作的地方。
- 分区表备份与GPT头备份,主要是用作损坏恢复,当首部GPTHDRR破坏时,可以根据尾部进行恢复,提高可靠性。
UEFI
UEFI,统一可扩展固件接口(英语:Unified Extensible Firmware Interface,缩写UEFI)是由intel牵头制定一种个人电脑系统规格,用来定义操作系统与系统固件之间的软件界面,作为BIOS的替代方案。 UEFI的启动过程相比BIOS更为简单:- 上电;
- UEFI固件从ROM中被加载,初始化硬件;
- 固件读取NVRAM(一个存储设备)中的引导管理器(配置信息),确定下一步需要加载的UEFI应用;
- UEFI应用继续加载其他UEFI应用,最终通过读取.efi文件来加载操作系统;
“实际上PC的启动固件的引导流程从IBM PC机诞生第⼀天起,就没有本质改变过。”
如果我们透过SEC、PEI、DXE和BDS等等复杂的术语看幕后隐藏的本质,就会发现无论传统BIOS还是UEFI,阳光之下没有什么新鲜的东西,启动本身无外乎三个步骤:
- Rom Stage:在这个阶段没有内存,需要在ROM上运行代码。这时因为没有内存,没有C语⾔运⾏需要的栈空间,开始往往是汇编语⾔,直接在ROM空间上运⾏。在找到个临时空间(Cache空间⽤作RAM,Cache As Ram, CAR)后,C语言终于可以粉墨登场了,后期用C语言初始化内存和为这个⽬的需要做的⼀切服务。
- Ram Stage: 在经过 ROM阶段的困难情况后,我们终于有了可以大展拳脚的内存,很多额外需要大内存的东西可以开始运行了。在这时我们开始进行初始化芯片组、CPU、主板模块等等核心过程。
- Find something to boot Stage: 终于要进⼊正题了,需要启动,我们找到启动设备。就要枚举设备,发现启动设备,并把启动设备之前需要依赖的节点统统打通。然后开始移交⼯作,Windows或者Linux的时代开始。
这就是传统BIOS和UEFI的启动过程,在剥去了术语后,主干的三个步骤从来没有变化过。”
的确,如果说BIOS时期是各个硬件厂商闭门造车阻止创新,那么UEFI就提供给了大家开放、创新的平台,使得UEFI能够更好的完成它的使命——初始化硬件和提供硬件的软件抽象(即接口),剩下的,就交给操作系统吧。补充-文件系统
一直对硬盘的文件系统有点疑惑——为什么需要文件系统?硬盘存储原理
在这之前我们先来看看机械硬盘的物理结构示意图(来自我是一块硬盘):- 找到存储数据的扇区所在柱面,这一步主要是读写头伸缩;
- 找到柱面上磁道上指定的扇区,这一步依靠主轴旋转;
- 读写头读取数据到内存;
文件系统
前面说到,文件系统简单来说就是方便人们快捷使用硬盘存储所发明的东西,总不能我需要存储或者一些数据还得自己知道到底存在1号扇区还是9号扇区吧? 人生苦短,我不用知道,也不想知道,文件系统知道就可以了,我只需要知道如何使用文件系统新建、删除、访问一个文件就够了。 考虑到硬盘的随机、顺序读取速度以及硬盘空间的管理等问题,现在主流的文件系统采用索引式的方式来管理文件和扇区的映射关系。 索引式?啥玩意儿?看下图来自我是一块硬盘:- 将tmp从根目录磁盘块删除
- 将tmp的inode释放,这样其他文件才可用
- 将tmp磁盘块,这样其他文件才可以用