您的位置:首页 > 运维架构 > Linux

Linux系统引导和启动的一些思考与总结

2014-12-30 17:46 246 查看
Grub的主要作用是引导操作系统。但是,为何要使用Grub,以及Grub为何要分成多个阶段常常让人困惑。为了解开这个谜团,我阅读了How it works系列的几个文章,终于明白了一个系统引导程序所面临的问题,以及Grub是如何去解决这些问题的。于是,豁然开朗。

为何要使用Grub?
要理解这一问题,首先要了解传统的boot loader所面临的问题。在Linux 0.11的代码中,Linux系统直接从软盘启动。软盘不需要分区,其文件布局也比较简单。因此,对于一个从软盘上启动的Linux系统来说,其启动过程是很简单的。





系统完成上电自检后,进入ROM BIOS程序。它将软盘的第一个扇区加载到内存的0000:7c00处开始执行。Boot扇区中的引导程序随后将紧随其后的4个扇区的setup模块加载到内存中执行。Setup模块又将其后的system模块加载到内存中执行,从而完成了系统的引导。

但是,当软盘逐渐淡出人们的视野(目前的Linux 2.6.39内核已经不再支持软盘),硬盘成为驱动器的主流时,传统的boot loader就面临了新的挑战。第一个挑战便是分区。一个硬盘可以有0~4个主分区,或者0~3个主分区加上若干个逻辑分区。MBR的分区表中,在某个时点只有一个分区可以被标记成活跃分区来引导系统。大部分传统的bootload都不能很好的处理分区,但是Grub可以。它不仅能够以一种一致的方式来处理各种各样的分区,而且还可以按需将某个分区设置成“活跃分区”。

第二个挑战来自于磁盘访问。在引导程序中,通常只能够使用BIOS的INT 13H接口来访问磁盘。因为这是唯一一种所有磁盘都支持的访问方式。INT 13H使用柱面号、磁头号和扇区号的CHS寻址方式来访问磁盘。但是这种传统的磁盘访问方式受INT 13接口的限制,仅可以访问的磁盘512M~8GB的空间。对于目前动辄几百G,甚至几个T的磁盘空间来说,传统的引导程序无法访问大部分的磁盘空间。而Grub支持LBA(Logical
Block Address)寻址方式,可以将CHS寻址转换成LBA寻址,也可以将LBA寻址转换成CHS寻址。这样,Grub就可以访问整个磁盘。

第三个挑战来自于文件系统。传统的bootloader不支持文件系统,因此在Linux 0.11中,其启动盘中文件的布局必须事先规划。但是Grub支持多种文件系统类型,比如AFFS,AtehOS fs,Linux ext2/ext3/ext4,DOS FAT12/FAT16/FAT32等多种不同的文件类型。这使得Grub引导程序能够理解并访问磁盘分区的文件系统中的文件。

第四个挑战来自于bootloader的大小限制。由于在磁盘中可以用来存放引导程序的空间有限,这包括MBR中的前446个字节、每个分区的引导记录中的512个字节和由于分区对齐在MBR和第一个分区之间的大约31KB空间。但是随着引导程序所支持的系统越来越多,其功能越来越复杂,体积也越来越大,很难将一个引导程序压缩在这些狭小的空间中,必须使用更加精巧的方式来引导系统。Grub采用分阶段的引导方式,巧妙的将必须的文件(stage1和stage1.5)放置于分区引导记录和31KB空间中,将其配置文件和较大的stage2放置于文件系统中。因此,虽然Grub功能复杂,但是其阶段划分清晰,能够完美的进行系统引导。

Grub引导阶段的划分
Grub引导通常分成3个阶段,分别叫做:stage 1,stage 1.5和stage 2。

Stage 1. 初始引导程序
当系统完成上电自检后,进入ROM BIOS程序。它将硬盘的第0个柱面、第0个磁头的第1个扇区(扇区编号从1开始)加载到内存的0000:7c00处,然后跳转到该地址执行程序。这样,BIOS将控制权转交给bootloader。这里的第一个扇区也就是所谓的MBR,主引导记录。它的前446个字节是一个可执行程序,之后的64字节是分区表,最后的2个字节是一个魔数。



这446个字节的引导程序会扫描分区表来找到活跃分区,然后将活跃分区的第一个扇区(分区引导记录,512字节)读入内存并执行。这里的512字节便是stage 1的程序。Stage 1程序是Grub引导的第一步,它主要用来加载stage 1.5。此时Grub还没有加载必要的驱动程序,无法识别文件系统,所以stage 1.5在磁盘上的位置是硬编码到stage 1的程序中的。这一块的设计比较精巧,Grub将这些可修改的硬编码数据叫做embedded
date。它其实是Grub stage 1的二进制文件中一些固定的字节。但是Grub安装程序可以在安装Grub时根据磁盘的情况来修改这些字节,从而将stage 1.5的位置硬编码到Grub stage 1中。由于大小的限制,stage 1的程序无法再做其他的事情。

Stage 1.5. 加载Grub的必要驱动
Grub stage 1.5通常存贮于MBR和第一分区之间的31KB“间隙”中。它包含了加载stage 2所必须的驱动程序。经过stage 1.5,Grub就能够识别和理解包含Linux内核映像的文件系统了。这正是Grub真正强大的地方。

Stage 2. 加载内核
Stage 1.5最后挂载其启动分区的文件系统,并加载文件系统中的/boot/grub/stage2这个程序。Stage 2根据/boot/grub/grub.conf来向用户显示一个Grub引导菜单,帮助用户选择要启动的操作系统。之后,根据用户的选择来加载内核映像和可选的RamDISK。

更多的信息可以参考“http://www.uruk.org/orig-grub/technical.html

内核启动和挂载文件系统
内核启动的过程在很多文章中都有描述,这里不再赘述,只想解释一下为什么需要RamDISK这个东西。在网上找了很久,没有太多的文章来解释为何需要initrd或者initramfs。最后,终于在kernel.org上有有一篇文章解释了initrd的来源 -- "https://www.kernel.org/doc/Documentation/initrd.txt"。其大意如下,需要更多细节的请仔细阅读链接的这篇文章!

Linux为了将内核做得尽可能小而简,采用了模块化的设计思想。将许多与操作系统根本职责无关的东西作为可以加载的内核模块,只有真正体现操作系统基本功能的代码才作为内核可执行文件的一部分。这样一来,许多的外设驱动就变成了内核模块,而与内核初始化、进程调度、信号处理相关的必要组件才成为内核的核心组成。因此,Linux的内核可以做得很小并运行在各种嵌入式系统中。但凡事具有两面性,保持内核可执行文件的精简就必然给内核的加载带来一定的障碍,其中一个关键的问题就是访问文件系统。Linux系统可以兼容许多不同的磁盘类型,比如IDE,SCSI,但是内核不可能支持所有的磁盘类型。否则,内核会变得很庞大。磁盘的驱动通常作为内核模块,在内核启动时按需加载。但是内核启动过程中常常需要执行一些命令(比如insmod)。这些命令的可执行程序又存储在文件系统中,这样就造成了矛盾。既要访问文件系统,但是又没有其驱动。这时,设计Linux系统的那些聪明人想到了一个方法:实现创建一个小型的文件系统,其中包含一些系统启动时必须的程序和文件。然后,将此文件系统和内核映像放在一起。当Grub加载内核映像时,也告诉Grub将此文件系统加载到内存。之后,在内核启动过程中挂载这个内存文件系统--这就是initrd。

多么精巧的设计!内核访问内存是需要任何驱动的!这样,内核利用init ram disk中的必要模块和可执行程序完成硬件的检测,加载必要的模块和驱动。等系统顺利启动,具备了访问磁盘的能力的时候再挂载磁盘上的根文件系统,并卸载和释放内存文件系统,操作系统得以顺利启动。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐