操作系统设计中的加电引导
作者:丁宋涛
系统启动过程概述
在掀下电脑开机按钮后,电源就会开始向主板和其他外围设备供电。初始状态下的电压还不太稳定,因此并不会立即开始指令的执行。此时,主板上的控制芯片组会发出重置信号,然后等待内部初始化工作的完成。等到控制芯片组检测到电源己经开始稳定供电后,它撤去信号,跳转到合适的内存地址处,读取并执行第一条机器指令。系统需要完成一系列的准备工作,以确保后续系统及程序的正确执行。这些准备工作包括检测基础的外围设备是否存在、检测系统实际物理内存及程序可用内存大小、建立内存空间的映射图、对硬件设备发出初始化信号等等。这些工作应当完成于操作系统内核运行之前,将软硬件调整到合适的状态下,为最终操作系统内核的调入架设好正确的外围环境。而执行该工作的程序就被称为引导加载程序。
当操作系统能够运行系统服务及用户程序时,或者说计算机可以与用户进行交互时,系统启动过程就被认为是完成了。典型的开机启动时间大约为一分钟,这包括了上电自检、引导时间和操作系统与其他相关软件的装载和初始化时间。嵌入式系统,如电视、通信设备等,通常要求更快速的,甚至是瞬时的启动时间。对于这些系统而言,相关功能可以被制造厂商直接烧在存储器中,从而略去了较复杂的引导加载程序的设计。
引导加载程序的作用
主板上的中央处理单元,即CPU,只能运行贮存在只读存储器上的程序代码。在启动阶段时,系统被认为处于一个“完全空白”的状态下,即在存储器上的数据要么是空白的,要么是由于前次的系统崩溃而残留下的不合法代码。而现代操作系统的设计日趋复杂,代码量日趋庞大,将其固化在造价昂贵的存储器中显然是不合理不科学的。操作系统与应用程序的镜像与数据一般存放在一些非易失性的存储介质上,典型如硬盘、光盘、U盘等设备中。因此,需要调用引导加载程序,将这部分数据和代码加载到存储器中,以备后续执行。相较于完整的操作系统镜像,引导加载程序的代码量要小得多,且访问的数据量是不多的。
此外,为了执行过程中操作系统与外界能够正常交互,系统需要提供给操作系统一份完备的系统环境信息,这就需要引导加载程序在启动阶段就完成对外设的探测工作。现在的硬件设备在设计与实现时,往往会板载一块ROM,保存自身的启动代码,用于在接收到系统发来的启动信号时能够正确响应,完成自身的初始化工作,并在完成后向系统发送回中断信号。引导加载程序将会按照硬件规范所提供的端口,向其发送正确的信号或硬件命令,提醒它们及时完成自身的启动。
引导加载程序的设计
在系统上电或复位后,几乎所有的都是从芯片制造商预先规定的地址上取出并执行第一条指令的。个人电脑往往会在计算机主板芯片上固化一个小程序,保存系统初始化设置信号、开机自检等信息。这段程序称为BIOS,即基本输入输出系统,它与硬盘中的操作系统引导加载程序协同工作,一起完成整个引导加载程序的功能。
个人pc机的操作系统引导介绍
首先,我们看一下操作系统启动部分的执行流程。当PC的电源打开以后,80X86结构的CPU将自动进入实模式,并从地址0XFFFF0开始自动执行程序代码,这个地址通常是BIOS中的地址。PC机的BIOS将执行某些系统的硬件检测,并在物理地址0处开始初始化中断向量。此后,计算机将可启动设备的第一个扇区(磁盘引导扇区,512字节)读入内存绝对地址0x7C00处,并跳转到这个地方。
现在假定机器在BIOS自检完成后,选择从软盘启动,那么计算机就会检查软盘的0面0磁道1扇区,如果发现磁盘以0xAA55结束,则计算机就会认为这是一个引导扇区,此时计算机将这512个字节的内容装载到内存地址0000:7c000处,然后跳转到0000:7c000处讲控制权彻底交给这段引导代码。从此,计算机不再由固化在主板上的BIOS的程序控制,而变成由操作系统的一部分来控制。
从计算机组成原理看操作系统引导
大约一个操作系统源代码中通常就会包含大约10%的起关键作用的汇编语言代码。为了便于大家理解,我们从最为熟悉的桌面PC平台,开始讲解。
首先让我们看一下,下面一段的汇编代码:
org 07c00h
mov ax, cs
mov ds, ax
mov es, ax
上述的汇编代码完成这样的功能,其中汇编指令org 07c00h就是告诉编译器程序,将这段代码加载到7c00处,这样在计算机自检完成后就可以访问我们的程序了。那么,我们考虑一下,我们如何验证,我们的代码被计算机读取并开始工作呢?如果,能让计算机显示诸如“hello world”的字样可以吗?这就要求我们实现字符加载的功能。
在开机的默认状态下,显示器处于80*25的文本模式。显存的范围是0xB8000到0xBFFFF,共计32KB。每两个字节代表一个字符,其中低字节表示字符的ASCII码,高字节表示字符属性。一个屏幕总共可以显示25行,每行80个字符。我们可以使用BIOS的显示中断完成显示。
INT 10H 是由 BIOS 对屏幕及显示器所提供的服务程序。使用 INT 10H 中断服务程序时,先指定 AH 寄存器为下表编号其中之一,该编号表示欲调用的功用,而其他寄存器的详细说明,参考表后文字,当一切设定好之后再调用 INT 10H。
如果要清楚屏幕,我们可以使用:
mov ah, 06h ; 屏幕初始化或上卷
mov aL, 00h ; AH = 6, AL = 0h
mov bx, 01111h ; 设置底色为蓝色
mov cx, 0 ; 左上角开始: (0, 0)
mov dl, 4fh ; 到第x列
mov dh, 1fh ; 到第x行
int 10h ; 显示中断
如果要显示一个字符,我们可以使用:
;显示字符
mov ah, 09h
mov al, 'O'
mov cx, 1
mov bx, 0014h ; 页号为0(BH = 0) 蓝底红字(BL = 14h)
int 10h ; 显示中断
附表是它们的说明
表1-1 BIOS int 10号说明表
AH |
功 能 |
调用参数 |
返回参数 / 注释 |
1 |
置光标类型 |
(CH)0―3 = 光标开始行 (CL)0―3 = 光标结束行 |
|
2 |
置光标位置 |
BH = 页号 DH = 行 DL = 列 |
|
3 |
读光标位置 |
BH = 页号 |
CH = 光标开始行 CL = 光标结束行 DH = 行 DL = 列 |
4 |
置显示页 |
AL = 显示页号 |
|
5 |
屏幕初始化或上卷 |
|
|
6 |
屏幕初始化或上卷 |
AL = 上卷行数 AL =0全屏幕为空白 BH = 卷入行属性 CH = 左上角行号 CL = 左上角列号 DH = 右下角行号 DL = 右下角列号 |
|
7 |
屏幕初始化或下卷 |
AL = 下卷行数 AL = 0全屏幕为空白 BH = 卷入行属性 CH = 左上角行号 CL = 左上角列号 DH = 右下角行号 DL = 右下角列号 |
|
8 |
读光标位置的属性和字符 |
BH = 显示页 |
AH = 属性 AL = 字符 |
9 |
在光标位置显示字符及其属性 |
BH = 显示页 AL = 字符 BL = 属性 CX = 字符重复次数 |
|
A |
在光标处只显示字符 |
BH = 显示页 AL = 字符 CX = 字符重复次数 |
|
E |
显示字符(光标前移) |
AL = 字符 BL = 前景色 |
光标跟随字符移动 |
13 |
显示字符串 |
ES:BP = 串地址 CX = 串长度 DH, DL = 起始行列 BH = 页号 AL = 0,BL = 属性 串:char,char,……,char AL = 1,BL = 属性 串:char,char,……,char AL = 2 串:char,attr,…… AL = 3 串:char,attr,…… |
光标返回起始位置 光标跟随移动 光标返回起始位置 光标跟随串移动 |
动手实践
在实现一个简单的操作系统时,我们是不可能拿一台真正的机器做实验的,一是很少有人有这个条件,还有就是那样做比较麻烦。所以我们使用虚拟机来模拟一台真实的电脑,这样我们就能直接用虚拟机加载软盘镜像来启动了。
在Windows下有很多虚拟机软件,例如Virtual PC、Vmware和Bochs等等。其中微软公司推出的VirtualPC2007已经提供了免费下载,同时Bochs系统调试功能非常强大,我们将这两个平台作为主要的实现平台。
Virtual PC做演示,而Bochs主要用作调试。今天的实验我们主要使用Bochs虚拟机,下面给出一些虚拟机设置的指导。
Bochs是一种开源且高度可移植的IA-32(x86)PC模拟器,用C++写成,能够在大部分常见的平台上运行。它包括了对Intel x86 CPU,通用I/O设备,和定制BIOS的模拟。通常情况下,Bochs能够被编译成模拟386,486或者Pentium CPU。Bochs能够模拟运行大部分的操作系统,包括Linux,DOS 和 Windows。
Bochs的下载可以通过" https://sourceforge.net/projects/bochs/ "访问,如图1-2所示:
图 1-2 Bochs下载链接
接下来,会导航到bochs的发行界面界面,如图1-3所示:
图 1-3 Bochs发行导航
最后,进入下载界面,如果是Windows平台,选择exe格式即可,如图1-4所示:
图 1-4 Windows版本的Bochs
下载完毕之后,一路next安装即可。(安装时建议将自带的“DLX Linux Demo”选中,这样可以有利于我们参考Bochs的安装文件)
其次,我们需要下载的工具是汇编编译器nasm。类似的,我们可以到NASM的官方网站下载,参考网址为
下载完毕之后,一路next安装即可。
最后,由于我们是在虚拟机上进行试验,我们还需要一个虚拟软盘读写工具,这里我们使用“dd for windows”,下载地址为
http://www.chrysocome.net/download/ dd-0.5.zip
直接解压即可。
引导扇区编写显示字符串的引导程序
ORG 7c00h ;告诉编译器,在7c00处加载程序
mov ax, cs
mov ds, ax
mov es, ax
; 清屏
mov ah, 06h ; 屏幕初始化或上卷
mov aL, 00h ; AH = 6, AL = 0h
mov cx, 0 ; 左上角开始: (0, 0)
mov dl, 4fh ; 到第x列
mov dh, 1fh ; 到第x行
int 10h ; 显示中断
; 显示字符串
mov ax,&Msg
mov bp,ax ; es:bp=串地址
mov ah, 13h ; AH:13显示字符串
mov al, 1h ; AH = 13, AL = 01h
mov bx, 000Bh ; 页号为0(BH = 0) 黑底青字(BL = 0Bh)
mov cx, MsgLngth ; CX = 串长度
mov dl, 00h ; 起始列
mov dh, 00h ; 起始行
int 10h ; 显示中断
Msg: db "Welcome To OS World ^-^";
首先,我们将上述代码使用记事本编写,并保存为boot.asm。接下来我们使用nasm对上述代码进行编译:
在开始菜单中,选中运行,在运行中键入cmd,打开命令行菜单。
图1-5进入命令行模式
在命令行状态下,切换到nasm的安装目录,在我的机器中nasm的安装路径为D:\Program_Files\nasm,我们使用如下的dos命令:
cd D:\Program_Files\nasm,如图1-6所示:
(提示安装路径中最好不要有空格或中文)
图1-6 切换工作目录演示
此时,我们的工作目录仍然是逻辑盘符C,我们键入D:,即可到达nasm目录。
图 1-7 进入nasm目录
这是因为我们需要启动nasm才能将我们boot.asm代码编译成为可执行代码,我们将我们刚刚编写好的boot.asm,拷贝到nasm目录下,键入如下命令:
nasm boot.asm –o boot.bin
在虚拟机中,我们是使用虚拟文件vhd来模拟硬盘的,我们可以直接在windows 10中使用diskpart命令完成,首先我们进入cmd命令行,如图1-8所示:
图1-8 使用diskpart生成虚拟硬盘
在接下来,我们在diskpart中输入命令:create vdisk file=你需要存放的文件名(我这里是f盘下的dingst.vhd)maximum=10 type=fixed,如同1-9 所示
图 1-9获得vhd文件
接下来,我们将得到dingst.vhd文件拷贝到“dd for windows”的安装目录。同样的,我们将编译成功得到的boot.bin拷贝至“dd for windows”的安装目录,在命令行状态下切换到该目录,键入如下命令:
dd if=boot.bin of=dingst.vhd bs=512 count=1
就将我们的可执行文件拷贝至a.img虚拟软盘中,我们启动Virtual PC。在首次进入Virtual box时,我们需要生成一个虚拟机:
首先,系统会启动“新建虚拟机向导(New Virtual Machine Wizard)”,询问是否使用向导一步步创建新的虚拟机。我们选择“Create a virtual machine”,点击“Next”,如图1-9所示:
图 1-10 设置Virtual box虚拟机
接下来系统会提问将虚拟机文件存放在哪个位置,我们选择虚拟机的存放路径。然后,类型选择other,如图1-10所示,其他均保持默认即可:
图1-11 设置虚拟机类型
同时,内存选择16MB为宜,最大不要超过64M(当年windows95,超过64MB的内存,OS反而会崩溃)。
图1-12 设置虚拟机内存
再接下来,核心是选择已经烧录了引导程序的vhd作为你的硬盘,如图1-13所示:
当虚拟设置完毕,我们点击start按钮,启动虚拟机,如图1-11所示:
图1-13选择虚拟机的硬盘
接下来直接启动我们的虚拟机,即可得到我们的实验结果,如图1-14所示:
图1-14 虚拟硬盘引导启动成功
相关视频请点击https://ke.qq.com/course/2736724?tuin=10eec75