bios启动流程

bios启动流程

虽然我知道现在大多数的pc都已经不再使用bios而是使用更先进、灵活的uefi的方式,但是我们操作系统考试就是只考bios启动。

因此本文会首先用详细版本讲述bios启动的来龙去脉,然后再归纳出一个考试时会写的一个答案。

详细版本

一、第一阶段BIOS

上个世纪70年代初,“只读内存”(read-only memory,缩写为ROM)发明,开机程序被刷入ROM芯片,计算机通电后,第一件事就是读取它。

bios芯片

这块芯片里的程序叫做"基本输入输出系統"(Basic Input/Output System),简称为BIOS

在一个x86架构的CPU中(通常我们用的就是这个架构,所以我们这里只研究它,其它架构大差不不差),为了实现计算机通电以后第一件事就读取它,CPU通电后的程序计数器会被CPU自动设置为0xffff0。

为什么是这个地址呢?为了理解这个我们首先需要知道x86架构下cpu的模式。

CPU的模式

x86架构的CPU有几种不同的操作模式,每种模式都有其特定的用途和特性。

  1. 实模式(Real Mode):这是最基础的模式,也是CPU在启动时的默认模式。在实模式下,CPU能够直接访问物理内存的前1MB。这个模式没有内存保护、虚拟内存或多任务等高级特性。实模式是16位模式,最早在8086和8088微处理器中出现。
  2. 保护模式(Protected Mode):这是一个32位模式,提供了许多高级特性,如内存保护、虚拟内存和多任务等。在保护模式下,CPU可以访问多达4GB的内存。Windows 95/98/ME及之后的版本,以及Linux,都在保护模式下运行。
  3. 长模式(Long Mode):这是一个64位模式,允许CPU访问超过4GB的内存。长模式在AMD的AMD64架构(也被Intel采用,并称之为Intel 64)中首次出现。在长模式下,CPU可以同时运行32位和64位的应用程序。
  4. 虚拟8086模式(Virtual 8086 Mode):这是一个特殊的模式,允许在保护模式下运行实模式的程序。这个模式在Intel 80386及其后续的微处理器中出现。
为什么是0xfffff0地址呢?

正如上面CPU模式中介绍的一样,CPU刚上电的时候处于的正是实模式(这是为了保持向后兼容性,因为早期的PC机(如IBM PC)就是这样设计的。),该模式下可寻址的范围仅为1MB,并且此时的寻址采用偏移寻址方式,即内存地址是通过一个16位的段寄存器和一个16位的偏移寄存器组合来表示的。

在这种模式下,一个完整的物理地址(也叫线性地址)是由段地址(segment)和偏移地址(offset)组合得到的。具体的计算方式是:

PhysicalAddress=Segment×16+OffsetPhysical Address=Segment×16+Offset

因此0xFFFF0的地址实际上是通过0xF000的段地址和0xFFF0的偏移地址组合得到的,即:

0xF000×16+0xFFF0=0xFFFF00xF000×16+0xFFF0=0xFFFF0

这0xffff0刚好是实模式最大可寻址1MB范围的低16位偏移,实模式下的地址寻址刚好也是16位,所以可见其位置是很特殊的,刚好是内存最后一个部分,刚好够放一个地址。

1.1 硬件自检以及中断向量表的建立

BIOS程序首先检查,计算机硬件能否满足运行的基本条件,这叫做"硬件自检"(Power-On Self-Test),缩写为POST

如果硬件出现问题,主板会发出不同含义的蜂鸣,启动中止。如果没有问题,屏幕就会显示出CPU、内存、硬盘等信息。

硬件自检信息

在扫描的过程中,bios还会准备实模式下的中断向量表及中断服务程序,否则在操作系统加载好之前(操作系统内核在启动之后会加载自己的中断向量表来覆盖现在创建的),你的键盘、鼠标操作将不起任何作用。这看上去似乎没什么,但是如果你想用键盘完成下文提到的选择启动顺序这样简单的任务,计算机将收不到任何输入。

中断服务程序在内存中的位置

1.2 启动顺序

硬件自检完成后,BIOS把控制权转交给下一阶段的启动程序。

这时,BIOS需要知道,“下一阶段的启动程序"具体存放在哪一个设备。也就是说,BIOS需要有一个外部储存设备的排序,排在前面的设备就是优先转交控制权的设备。这种排序叫做"启动顺序”(Boot Sequence)。

打开BIOS的操作界面,里面有一项就是"设定启动顺序"。

bios设置启动顺序

二、第二阶段:主引导记录

BIOS按照"启动顺序",把控制权转交给排在第一位的储存设备。

这时,计算机读取该设备的第一个扇区,也就是读取最前面的512个字节。如果这512个字节的最后两个字节是0x55和0xAA,表明这个设备可以用于启动;如果不是,表明设备不能用于启动,控制权于是被转交给"启动顺序"中的下一个设备。

这最前面的512个字节,就叫做"主引导记录"(Master boot record,缩写为MBR)。下面是示意图:

mbr在磁盘中的位置示意图

值得一提的是,现在大多数的磁盘已经是gpt分区了,MBR也是一种古老的技术了,毕竟它支持4个主分区,每个分区最大也只能2T

2.1 主引导记录的结构

"主引导记录"只有512个字节,放不了太多东西。它的主要作用是,告诉计算机到硬盘的哪一个位置去找操作系统。

主引导记录由三个部分组成:

(1) 第1-446字节:调用操作系统的机器码。

(2) 第447-510字节:分区表(Partition table)。

(3) 第511-512字节:主引导记录签名(0x55和0xAA)。

其中,第二部分"分区表"的作用,是将硬盘分成若干个区,就是在windows中常见的C盘、D盘,linux中的各个分区。

2.2 分区表

硬盘分区有很多好处。考虑到每个区可以安装不同的操作系统,"主引导记录"因此必须知道将控制权转交给哪个区。

分区表的长度只有64个字节,里面又分成四项,每项16个字节。所以,一个硬盘最多只能分四个一级分区,又叫做"主分区"。

每个主分区的16个字节,由6个部分组成:

(1) 第1个字节:如果为0x80,就表示该主分区是激活分区,控制权要转交给这个分区。四个主分区里面只能有一个是激活的。

(2) 第2-4个字节:主分区第一个扇区的物理位置(柱面、磁头、扇区号等等)。

(3) 第5个字节:主分区类型

(4) 第6-8个字节:主分区最后一个扇区的物理位置。

(5) 第9-12字节:该主分区第一个扇区的逻辑地址。

(6) 第13-16字节:主分区的扇区总数。

最后的四个字节(“主分区的扇区总数”),决定了这个主分区的长度。也就是说,一个主分区的扇区总数最多不超过2的32次方。

如果每个扇区为512个字节,就意味着单个分区最大不超过2TB。再考虑到扇区的逻辑地址也是32位,所以单个硬盘可利用的空间最大也不超过2TB。如果想使用更大的硬盘,只有2个方法:一是提高每个扇区的字节数,二是增加扇区总数

三、第三阶段:硬盘启动

这时,计算机的控制权就要转交给硬盘的某个分区了,这里又分成三种情况。

3.1 情况A:卷引导记录(考试考察的情况)

这里是考试中认为的标准下一步,即使实际的情况往往并非如此。

上一节提到的BIOS程序会加载启动磁盘的MBR到内存里运行,运行MBR的引导代码,这段代码会查找活动分区(BIOS不认识活动分区,但这段代码认识活动分区)的位置,加载并执行活动分区的“卷引导记录"(Partition boot record缩写为PBR,也称为Volume boot record,缩写为VBR)。与MBR类似,PBR在运行后加载操作系统的引导程序到内存运行,例如Windows的bootmgr或Linux的grub。当引导程序运行后,操作系统内核就被加载运行,完成从BIOS程序中接手的引导流程。

这里这么划分的核心逻辑为,BIOS并不知道某个磁盘其内部的具体构造,它只知道这个计算机里面有几个磁盘。那么MBR其实相当于一个接口,BIOS不需要知道这个磁盘具体的分区情况,无脑访问第一个分区就对了,具体怎么处理是这个MBR的责任,MBR为什么会知道怎么处理呢,因为是你给这个磁盘分的区,MBR也被划分出来了,MBR对这个磁盘负责。

PBR也同理,一个磁盘里面可以有多种文件系统,包括FAT、EXT2和NTFS等等,MBR这么点代码,他是不可能知道每个分区的具体情况的,那么同样,我MBR只需要检测你这个分区有没有flag说明你是个启动分区(上文提到的0x80),有的话我就把接下来加载操作系统的活给你,你对这个分区里面的事情熟悉呀!

这样一个磁盘里面是不是就可以有多种操作系统了,因为操作系统的启动器在pbr里面,mbr是选择pbr的,与操作系统无关。

3.2 情况B:启动管理器

考试不考,但是这是实际情况。

现在很多的操作系统,喜欢在安装的时候直接霸占整个mbr,把自己的启动管理器装进mbr中而不是pbr中,常见的启动器包括Linux中的Grub和Windows的windows bootmanager。这些启动管理器很多时候并不按照上面的标准流程来。

比如对于grub来说,grub在mbr中放的是一个叫boot.img的汇编文件,这称之为stage1,然后boot.img并不会按照规范去扫描pbr,相反它直接去加载core.img(为什么不把boot.img和core.img合并?因为mbr太小了),称之为stage1.5,这个core.img是放在了mbr和第一个分区之间空闲的空间中,因为为了兼容,MBR是在磁盘的第0个块上,而第一个分区不是从第1个块开始的,而是第63个,这使得空余了62*512B的空间(一般一个块512B大)。这一步之后grub会直接去一个在安装时就已经硬编码的一个分区,去读取/boot/grub下面的文件,这些文件往往是grub的插件模块。然后才是让你选择启动哪一个分区,这个时候如果你选择启动windows(如果你计算机上有),它才会去读取windows bootmanager在的那个pbr,然而如果启动的是linux,它就直接读取你的硬盘,加载内核了,相当于它直接成为了pbr

image-20240103162808189

grub的选择页面

img

对于windows来说,那就更绝了,它设计的windows bootmanager就真是windows的启动器,只能识别windows,更别提扫描pbr的操作了。

不过上面的传统模型也不是没有意义的,grub其实是可以装到pbr之中的,安装的时候指明具体的分区而不是磁盘就行,如下面命令所示。

grub-install --force /dev/sdax

四、第四阶段:操作系统

在把控制权转交给操作系统之前,启动管理器们还会将CPU模式从实模式切换到保护模式,来让操作系统内核能获得更多的功能。对于Grub来说,它还要告诉Linux内核根目录是哪一个分区。接下来就是启动器们负责把内核二进制文件装入内存,并将CPU的程序计数器指向内核执行的入口。

在转交之后,以Linux系统为例,系统会首先挂载根目录。内核加载成功后,第一个运行的程序是/sbin/init。它根据配置文件(Debian系统是/etc/initab)产生init进程(现在大多数Linux发行版的init进程都是systemd了)。这是Linux启动后的第一个进程,pid进程编号为1,其他进程都是它的后代。流程图如下:

image-20240103164142247

然后,init线程加载系统的各个模块,比如窗口程序和网络程序,直至执行/bin/login程序,跳出登录界面,等待用户输入用户名和密码。

至此,全部启动过程完成

考试版本

考试版本就会显得简介明了了

  1. 启动BIOS,此时为实模式,并进行硬件自检,准备中断向量表和中断服务程序.

  2. BIOS将磁盘的第一个块-MBR读入内存,执行MBR磁盘引导程序,扫描分区表.

  3. 从活动分区中(0x80 flag)读入PBR,执行PBR中的程序,PBR启动的相关程序进行环境的准备,切换为保护模式,挂载分区等动作,为内核执行准备.

  4. 执行内核,内核进行初始化

  5. 挂载根文件系统

  6. 形成进程0的运行环境

  7. 执行init进程

  8. init进程进行系统初始化,启动完成.