Linux Unix系统的引导过程Word下载.docx
《Linux Unix系统的引导过程Word下载.docx》由会员分享,可在线阅读,更多相关《Linux Unix系统的引导过程Word下载.docx(10页珍藏版)》请在冰豆网上搜索。
MBR的大小为512字节,其中的446字节作为GRUB的stage1引导程序的存放位置,接下来的64字节存放磁盘分区信息,最后的两个字节为MBR的终止标志位,为0xAA55,在上文中也有提及到。
在MBR中的446字节的引导程序属于GRUB的开始执行程序,通过这段程序,进一步执行stage1.5或是stage2的执行程序,将在下面详细介绍执行过程。
其中stage1.5或是stage2便属于阶段2引导的过程了,stage2过程也是作为GRUBkernel的核心代码出现。
Stage1.5过程(对于GRUB而言存在stage1.5,GRUB2则不存在)的功能很单一,主要就是为了引导stage2过程服务。
由于stage2过程的代码存放在文件系统下的boot分区目录中,因此stage1.5过程就是需要提供一个文件系统的环境,而该文件系统环境需要保证系统可以找到stage2过程的文件,那么stage1.5阶段提供的文件系统需要是boot文件系统所对应的,这个在执行grubinstall过程中就已经确定了。
stage2过程中,主要会把系统切换到保护模式,设置好C运行时环境,找到config文件(事实上就是menulist文件),如果没有找到就执行一个shell,等待用户的执行。
然后的工作就变成了输入命令->
解析命令->
执行命令的循环中。
当然该阶段引导的最终状态就是执行boot命令,将内核和initrd镜像加载进入内存中,进而将控制权转交给内核,并开始执行用户态程序——init进行一些外设初始化工作。
至此整个Linux/Unix内核得以引导可以使用了,上面的内容从整体轮廓上面对该过程进行了说明,过程中主要还是以目前最常用技术进行分析,下面将深入到具体的每个阶段的执行,而在其执行过程中会增加更多最新的技术。
2系统启动阶段
在系统启动阶段主要发生在机器加电或是触发reset事件时,机器的主要执行过程,在这个过程中属于系统最初始的状态。
机器要执行这样的过程主要通过两种方式:
BIOS(BasicInputOutputSystem的简称)和EFI(ExtensibleFirmwareInterface的简称)。
接下来将分两部分分别阐述。
2.1BIOS启动
BIOS的启动方式现在仍然是最广泛的启动方式,主要发生在机器加电或触发了reset事件。
上文也提及到了,其中主要包含了:
自诊断程序、CMOS设置程序、系统自举装载程序、主要I/O设备驱动程序和中断服务。
这些程序的具体执行过程如下:
a)按下电源开关,电源就开始向主板和其它设备供电;
当芯片组检测到电源已经开始稳定供电了(当然从不稳定到稳定的过程只是一瞬间的事情),它便撤去RESET信号(如果是手工按下计算机面板上的Reset按钮来重启机器,那么松开该按钮时芯片组就会撤去RESET信号);
CPU马上就从地址FFFF:
0000H处开始执行指令,放在这里的只是一条跳转指令,跳到系统BIOS中真正的启动代码处。
b)系统BIOS的启动代码首先进行POST(Power-OnSelfTest,加电后自检)。
POST的主要检测系统中一些关键设备是否存在和能否正常工作,例如内存和显卡等设备;
由于POST是最早进行的检测过程,此时显卡还没有初始化,如果系统BIOS在进行POST的过程中发现了一些致命错误,例如没有找到内存或者内存有问题(此时只会检查640K常规内存),那么系统BIOS就会直接控制喇叭发声来报告错误,声音的长短和次数代表了错误的类型;
在正常情况下,POST过程进行得非常快,几乎无法感觉到它的存在。
POST结束之后就会调用其它代码来进行更完整的硬件检测。
c)接下来系统BIOS将查找显卡的BIOS。
前面说过,存放显卡BIOS的ROM芯片的起始地址通常设在C0000H处,系统BIOS在这个地方找到显卡BIOS之后就调用它的初始化代码,由显卡BIOS来初始化显卡。
此时多数显卡都会在屏幕上显示出一些初始化信息,介绍生产厂商、图形芯片类型等内容,不过这个画面几乎是一闪而过。
系统BIOS接着会查找其它设备的BIOS程序,找到之后同样要调用这些BIOS内部的初始化代码来初始化相关的设备。
d)查找完所有其它设备的BIOS之后,系统BIOS将显示出它自己的启动画面,其中包括有系统BIOS的类型、序列号和版本号等内容。
e)接着系统BIOS将检测和显示CPU的类型和工作频率,测试所有的RAM,并同时在屏幕上显示内存测试的进度。
可以在CMOS设置中自行决定使用简单耗时少或者详细耗时多的测试方式。
f)内存测试通过之后,系统BIOS将开始检测系统中安装的一些标准硬件设备,包括硬盘、CD-ROM、串口、并口和软驱等设备,另外绝大多数较新版本的系统BIOS在这一过程中还要自动检测和设置内存的定时参数、硬盘参数和访问模式等。
g)标准设备检测完毕后,系统BIOS内部支持即插即用的代码将开始检测和配置系统中安装的即插即用设备。
每找到一个设备之后,系统BIOS都会在屏幕上显示出设备的名称和型号等信息,同时为该设备分配中断、DMA通道和I/O端口等资源。
h)到这一步为止,所有硬件都已经检测配置完毕了,多数系统BIOS会重新清屏并在屏幕上方显示出一个表格,其中概略地列出了系统中安装的各种标准硬件设备,以及它们使用的资源和一些相关工作参数。
i)接下来系统BIOS将更新ESCD(ExtendedSystemConfigurationData,扩展系统配置数据)。
ESCD是系统BIOS用来与操作系统交换硬件配置信息的一种手段,这些数据被存放在CMOS(一小块特殊的RAM,由主板上的电池来供电)之中。
通常ESCD数据只在系统硬件配置发生改变后才会更新,所以不是每次启动机器时都能够看到“UpdateESCD…Success”这样的信息。
不过,某些主板的系统BIOS在保存ESCD数据时使用了与Windows9x不相同的数据格式,于是Windows9x在它自己的启动过程中会把ESCD数据修改成自己的格式。
但在下一次启动机器时,即使硬件配置没有发生改变,系统BIOS也会把ESCD的数据格式改回来。
如此循环,将会导致在每次启动机器时,系统BIOS都要更新一遍ESCD,这就是为什么有些机器在每次启动时都会显示出相关信息的原因。
j)ESCD更新完毕后,系统BIOS的启动代码将进行它的最后一项工作:
即根据用户指定的启动顺序从软盘、硬盘或光驱启动MBR。
在这个过程中会按照启动顺序顺序比较其放置MBR的位置的结尾两位是否为0xAA55,通过这种方式判断从哪个引导设备进行引导。
在确定之后,将该引导设备的MBR内容读入到0x7C00[1]的位置,并再次判断其最后两位,当检测正确之后,进行阶段1的引导。
注[1]:
此处读入地址为什么为0x7C00?
此处读入地址为0x7C00,主要属于历史原因,以及本身硬件的考虑,起初该地址为0×
200,因为当时8086中断向量使用地址为0×
0-0x3FF,86-DOS从0×
400处被加载,而它不使用0×
200-0x3FF,因此将其存放在此处。
但是后来IBMPC5150BIOS开发团队决定使用0x7C00,主要原因包括:
他们想留下32kb内更多的空间给操作系统来加载自己;
8086/8088使用0×
0-0x3FF作为中断向量,然后BIOS数据紧随之后;
引导扇区是512字节,但是用于引导程序的栈或数据区域需要多于512字节;
因此0x7C00,32kb中的最后1kb被选中。
更详细的分析过程请访问这里。
2.2EFI启动
与传统MBR相比,GPT采用了不同的分区方式。
对于传统MBR,其结构主要如下:
上图即对上文中所述的很形象的说明,在图中看到MBR被分成三个部分,分别是:
Bootloader、分别表以及MagicNumber。
其中Bootloader部分为stage1中被执行的起始部分,程序在这里被作为GRUB程序执行,详细的关于GRUB的内容将再下面章节中进行详细阐述。
相反,对于EFI系统中所采用的GPT分区方式,则采用了不同于MBR分区方式的形式,从下图中可以发现:
如上图所示,GPT分区表主要包括:
保护MBR、首要GPT头、首要GPT、备用GPT、备用GPT头和磁盘数据区。
保护MBR与正常的MBR区别不大,主要是分区表上的不同,在保护MBR中只要一个表示为0xEE的分区,以此来表示这块硬盘使用GPT分区表。
首要GPT头包含了众多信息,具体内容如下:
偏移
字节长度
说明
0×
00
8
签名,固定为ASCII码“EFIPART”,16进制表示0×
5452415020494645。
08
4
版本号,目前的版本为V1.0,16进制表示0×
00010000。
0x0C
分区表头的大小(单位是字节,通常是92字节,即5C000000)
10
GPT头中字节的CRC32校验
14
固定值00000000
18
当前LBA(这个分区表头的位置)
20
备份LBA(另一个分区表头的位置)
28
第一个可用于分区的LBA(主分区表的最后一个LBA+1)
30
最后一个可用于分区的LBA(备份分区表的最后一个LBA−1)
38
16
磁盘GUID
48
分区表项的起始LBA
50
分区表项的数量
54
一个分区表项的大小(通常是128)
58
分区表CRC32校验
0x5C
420
保留,剩余的字节必须是0(对于512字节LBA的硬盘即是420个字节)
分区表头定义了硬盘的可用空间以及组成分区表的项的大小和数量。
分区表头还记录了这块硬盘的GUID,记录了分区表头本身的位置和大小(位置总是在LBA1)以及备份分区表头和分区表的位置和大小(在硬盘的最后)。
它还存储着它本身和分区表的CRC32校验。
固件、引导程序和操作系统在启动时可以根据这个校验值来判断分区表是否有错误,如果出错了,可以使用软件从硬盘最后的备份GPT分区表恢复整个分区表,如果备份GPT也校验错误,那么磁盘将不可用,系统拒绝启动。
接下来主要是128个分区表项,GPT分区表使用简单而直接的方式表示分区。
一个分区表项的前16字节是分区类型GUID。
例如,EFI系统分区的GUID类型是{C12A7328-F81F-11D2-BA4B-00A0C93EC93B}。
接下来的16字节是该分区的唯一的GUID(这个指的是该分区本身,而之前的GUID指的是该分区的类型)。
在接下来是分区其实和末尾的64位LBA编号,以及分区的名字和属性。
具体结构如下表:
分区类型GUID
唯一的分区GUID
开始LBA
结束LBA
分区属性
72
分区名称(Unicode码)
3阶段1的引导(GRUB中stage1过程)
接下来开始真正的引导过程了,主要说明GRUP的引导。
总体上GRUB更像是一个minios,只不过这个minios的作用只是加载其他的操作系统,在GRUB中包括stage1、stage1.5(可选)和stage2,其中stage1和stage1.5属于bootloader,stage2属于minios的内核部分。
GRUB中stage1过程主要位于MBR的前446字节中(对于支持GPT分区的磁盘,同样有最开始的512字节作为保护MBR,保护MBR与正常的MBR区别不大,主要是分区表上的不同,在保护MBR中只要一个表示为0xEE的分区,以此来表示这块硬盘使用GPT分区表,不能识别GPT硬盘的操作系统通常会识别出一个未知类型的分区,并且拒绝对硬盘进行操作),之后的64字节为硬盘的分区表,最后两个字节为MBR结束标志位(0xAA55)。
stage1部分占用了446字节,其代码文件是源码目录下stage1/stage1.S文件,汇编后生成一个512字节的boot.img,被写在硬盘的0面0道1扇区中,作为硬盘的MBR。
stage1的工作很简单,就是加载0面0道2扇区上的512字节到0×
8000,然后跳转到0×
8000执行。
在0面0道2扇区上的512字节内容为stage1/start.S文件汇编后生成。
该扇区上的内容的作用是加载stage1.5或是stage2过程,并将控制权转交。
至于代码,这里就不进行分析了,感兴趣的读者可以去查看源码,不过对于GRUB和GRUB2,其stage1的源码位置不尽相同。
4阶段2的引导(GRUB中stage1.5或stage2过程)
在start过程将控制权转交后,接下来就是GRUB的核心过程了。
该过程之所以区分stage1.5和stage2,主要原因是GRUB和GRUB2的区别。
在GRUB2中,将stage1.5过程集成到了stage2的过程中,所以stage1.5过程仅仅是针对GRUB的。
下面将会分别介绍两种GRUB版本的两种过程。
4.1GRUB中stage1.5过程
Stage1.5过程很无辜,它的作用很单一,但是非常关键。
它的主要功用就是构造一个boot分区系统对应的文件系统,这样可以通过文件系统的路径(/boot/grub/)寻找stage2过程需要的core.img,进而加载到内存中开始执行。
Stage1.5存在于0面0道3扇区开始的地方,并一直延续十几k字节的区域,具体的大小与相应的文件系统的大小有关(文中涉及到了0面0道1-3+x扇区,这部分扇区为保留扇区,BIOS不会放置任何数据。
正因为如此如果转换到GPT分区形式,系统将不能被正确引导,如上文所示,MBR后面的扇区都被其他内容所占据)。
Stage1.5过程被构建成多种不同类型,但是功能类似,下面简单介绍一下基本的stage1.5过程的文件系统。
e2fs_stage1_5(针对ext2fs,可引导ext2和ext3文件系统)、fat_stage1_5(针对fat文件系统,可引导fat32和fat16)、ffs_stage1_5、jfs_stage1_5、minix_stage1_5、reiserfs_stage1_5、vstafs_stage1_5和xfs_stage1_5,这些文件被称为stage1.5过程,这些文件每个至少都在11k以上。
除此之外还有两个比较特殊的文件,分别为nbgrub和pxegrub,这两个文件主要是在网络引导时使用,只是格式不同而已,他们很类似与stage2,只是需要建立网络来获取配置文件。
由于stage1.5过程中会涉及到多个文件系统对应的文件,因此本文中主要以ext2fs为例进行说明,其他文件系统与此类似,可以同样进行分析理解。
对于ext2fs文件系统,用于生成该文件系统的stage1.5过程文件(e2fs_stage1_5)的代码为stage2/fsys_ext2fs.c文件。
在stage2/filesys.h文件中定义了每个文件系统对外的接口,用于上层调用,作为stage2过程寻找核心代码使用,文件系统一般被定义的接口主要就是三个函数,分别是mount、read和dir函数。
对应ext2fs,其定义的函数为:
#ifdefFSYS_EXT2FS
#defineFSYS_EXT2FS_NUM1
intext2fs_mount(void);
intext2fs_read(char*buf,intlen);
intext2fs_dir(char*dirname);
#else
#defineFSYS_EXT2FS_NUM0
#endif
针对ext2fs有如上的函数名称,每个函数将具体在stage2/fsys_ext2fs.c文件中被定义,这里面没有包含任何的写的过程,对于bootloader而言仅仅读就可以完成任务了,没必要对其系统进行写操作。
其中ext2fs_mount函数用于检查文件系统类型,并将superblock读入到内存中;
ext2fs_read函数和ext2fs_dir函数用于对文件系统具体的操作。
在stage2/fsys_ext2fs.c文件中除了需要对这三个函数的定义之外,还需要文件系统的属性的数据结构(superblock、inode和group结构,这些结构最初被定义在include/linux/ext2_fs.h文件中),通过这些数据结构描述一个文件系统。
如果读者有兴趣可以试着创建新的文件系统的支持,可以参照目前存在的一些文件系统的模板(实例)编写。
4.2GRUB中stage2过程
GRUB中的核心过程也就是stage2过程了,该过程主要是在文件系统建立以后选择合适的操作系统进行加载并转交控制权,达到最后引导操作系统的目标。
由于GRUB属于multibootloader,因此在引导的时候要进行选择,选择哪种操作系统来运行。
在GRUB内部主要包括两种方式,首先是从menu.list中读取显示到屏幕让用户选择,其次是通过grub-shell中定义的命令手动进行启动。
本文将在后面介绍这两种方式如何运行,接下来先介绍一下stage2的具体的执行过程。
在上面一节中介绍过,stage1.5过程中将boot分区的文件系统加载了,之后又做了一件事情,就是将控制权转交给stage2,而stage2入口的地方就是stage2/asm.S文件。
Stage2/asm.S文件属于汇编代码,主要作用是初始化C语言的运行环境,为下面执行C语言的函数做好准备,在准备好之后,将执行init_bios_info(stage2/common.c)函数。
init_bios_info函数的作用是执行一些底层的函数,然后跳转到cmain执行,cmain函数位于stage2/stage2.c文件中。
cmain函数内部进行一个死循环,在循环内部首先加载配置文件,显示给用户,在这同时循环一个内层循环,在内层循环中,获取配置文件中的命令,并解析执行。
过程中如果没有可用的配置文件,那么进入命令行模式(enter_cmdline函数),如果找到可用的menu,那么开始执行menu的对应的内容(run_menu函数)。
对于enter_cmdline(stage2/stage2.c)函数,将调用find_command(stage2/cmdline.c),进而执行相应命令的函数。
对于run_menu(stage2/stage2.c)函数,将调用stage2/cmdline.c文件中的run_script函数,进而调用find_command,执行相应命令的函数。
这两种方式虽然经过了不同的过程,对用户输入的行为进行分析和处理,最终调用的函数为find_command,在该函数中顺序循环比较“输入”的命令是否与系统内部定义的相同,如果相同转到执行该函数。
在这个比较的过程中包含了一个全局的数据结构为structbuiltin(stage2/shared.h),由该数据结构组成了一个table类型(stage2/builtins.c),将命令与相对应的builtin结构对应一起并进行串联。
下面描述一下builtin结构的定义:
structbuiltin
{
/*命令名称,重要,是搜索命令时的依据*/
char*name;
/*命令函数,重要,是搜索匹配后调用的函数*/
int(*func)(char*,int);
/*功能标示,一般未用到.*/
intflags;
/*简短帮助信息*/
char*short_doc;
/*完整帮助信息*/
char*long_doc;
};
structbuiltin*builtin_table[];
有兴趣的读者可以对上面的内容进行扩展,形成自己的命令,主要在stage2/builtins.c文件中按照预定的格式更新,并添加到builtin_table中即可。
在上面打开配置文件的过程中,主要是通过一些文件操作函数(被定义在stage2/disk_io.c中)完成。
这些文件操作函数主要包括:
grub_open、grub_read、grub_seek和grub_close等,这些函数属于grub对外的上层接口,具体的函数内部将调用前文中提到的boot分区对应的文件系统的相应的函数完成,这个过程主要是通过回调函数来完成。
该过程整体思路类似于面向对象过程,通过对象操作具体的函数。
关于用C语言实现面向对象的编程思路,可以参考一本书——《Object-orientedProgrammingwithANSI-