LINUX的启动总纲
POST
BIOS(boot sequence)
MBR(boot loader)
kernel
initrd(ramdisk)
bootfs
/sbin/init
POST和BIOS
在介绍计算机的启动过程之间我们要先明白COMS和BIOS是什么东西?当我们的电脑第一次使用U盘的启动的时我们一般都会按某个键进入到一个bios界面进行一些启动配置,在这里我们暂且认为BIOS是就是一个软件。BIOS是一种怎样的形式存在于我们的主板上的呢?当我们打开主板时候会看到一块长方形或者正方形的芯片(如下图),这个芯片是不是就保存有BIOS程序呢?
是的,这个芯片当中保存有BIOS程序,这个芯片就叫做“BIOS芯片”,属于只读存储器,不得更改里面的数据,是一个韧体,韧体就是写入硬件上的 软件程序。那么为什么要把软件程序写入到一个硬件芯片上呢?因为计算机启动是一个很矛盾的过程:必须先运行程序,然后计算机才能启动,但是计算机不启动就无法运行程序!早期真的是这样,必须想尽各种办法,把一小段程序装进一个芯片,让这个芯片一回电立马就可以执行程序,不让cpu参与,然后计算机才能正常运行(正文这会详细阐述)。想要运行程序必须先启动计算机,而想启动计算机必须需要程序的引导,这是一个十分尴尬的问题?生活这样的问题也有很多,比如找工作,有的公司只要两到三年经验的,但是没有经验就找不到工作,没有工作又怎样积累经验呢?因为没有资产才去银行贷款,但是银行却要求用资产抵押!越想要什么,生活就越不给,唯一的办法是有什么就用什么,用什么都用好,做什么都做好,不要常常觉得苦,一切都是靠积累。我们看一看计算机是怎样解决这个问题的?这对我们的人生有很好的借鉴意义,因为计算机是一个时代智慧结晶。
BIOS程序有多个子程序,其实最主要的四个子程序:
中断程序。中断就是键盘、鼠标、光驱与BIOS系统通信的基础,其实每当按下鼠标或者键盘的时候其实都会中断BIOS系统的运行,BIOS系统必须立马处理键盘和鼠标所传递的信息,注意这里的中断与操作系统启动起来的中断并不是一会事儿,但是目的是一样。这里的中断中断的是BIOS系统,当操作系统启动起来之后中断就是内核。如果在主机过于繁忙的时候进行中断会有一定的卡顿感,因为内核一旦收到中断信号就要放下手上的工作去完成中断传递的信息。
BIOS设置程序。这个才是我们经常使用的那个BIOS程序,当我们在使用BIOS程序的时候我们是在更改一些信息,比如我们把从硬盘启动变成U盘启动,那么这个更改是更改的哪里的信息呢?被更改的信息显然不能是BIOS芯片的,因为它是只读的不能进行更改,其实我们更改的是另一个芯片是信息:COMS 是一个具有RAM特性的的存储器。那么什么是RAM特性?像我们前面提到的BIOS芯片是具有ROM特性,COMS芯片具有RAM特性,还有内存条也具有RAM特性,RAM就是random access memory随机访问存储器,说白了所谓的RAM特性就是断电会丢失数据。ROM就是read only memory,说白了所谓的ROM特性就是断电不会丢失数据,不知道这样说你是否能够理解呢?可能你又要说的:“你不是说COMS具有RAM特性,断电 后 丢失,那么 我们使用BIOS程序更改的信息断电之后就应该丢失,那怎么我使用BIOS更改的某些信息在开关机之后 依然有效呢?”没错,COMS存储器的确会在断电后丢失,但是我们的主板上有一块电池,这块电池一直支持着COMS一直工作,如果这块电池没有电了,BIOS的设置自然就会失效,对了这块电池还供养着硬盘时钟的工作,以确保主机上的时间不会误差。
上电自检的程序。POST(power on self test)计算机要想启动起来的的话这个自检程序必不可少,这个自举的程序的作用就是程序来对内部各个设备进行检查。通常完整的POST自检将包括对 CPU、内存、主板、等,一旦在自检中发现问题,系统将给出提示信息或鸣笛警告。
自举的程序(bootstrapping))这个自举的程序其实就是计算机自己把自己举起来的过程,早期的时候计算机的启动是一个难题,我们想让计算机帮助我们进行工作话肯定要先让计算机加载到内核启动起来,加载内核就需要cpu,因为只有cpu才能执行指令,但是单单只有cpu也不行哟,cpu内部说白了就是一 些细小的 电路它自己并不能进行开关,加法器不能进行开关就不能进行运算,必须有特定电流的冲击才能进行电路的开关,那么cpu需要的电流冲击来自于哪里呢?这些电流冲击必须来自于内存,但是问题又来了,内存当中也没有数据,的确,此时内存当中没有数据的,我们在上文也提到过,内存有RAM属性,断电即丢失所有的数据,这就陷入一 个死循环,cpu因为内存当中没有内核所有不能加载内核计算机不能启动,但是如果内核不加载内核数据就不能通过硬盘调用到内存当中(数据的调用需要与硬盘打交道,只有内核可能 与硬盘进行打交道)。怎样才能打破 这个死循环呢?要想打破这个死循环的话就应该主机加电的时候把操作系放入到内存的固定位置,然后cpu一加电就去这个固定的内存位置取出指令并执行。那么问题就简单了怎样才能在主机一加电就把操作系统放入到内存当中呢?我们可以操作系统写入到一个芯片上,只要这个设备一加电立马就把操作系统映射到指定的内存位置,这个思路是可以的,但是可不可不再进一步优化呢?如果我们把操作系统写到一个芯片上的话那岂不是以后此主机都只能使用这个操作系统了吗?我们既然有了一个这样的思路:“把一个引导程序硬性写入到一个芯片上,让其一加电就自动映射到内存的固定位置”,我们可以再进一步:把一个引导通用性的小程序硬性写入到这个芯片上,一加电就自动映射到内存的固定位置,然后cpu去读取这个引导通用性的小程序中的指令,指令包括的内容就是去某个硬件当中取出操作系统放置到内存当中,这样可不可以呢?是可以的,可能你已经猜到了,这个小程序其实就是我们要讲的自举程序。那么去哪个硬件设备上读取操作系统呢?看到这是你可能会想到了我们在使用U盘启动的时候会调整的启动顺序,BIOS会去首先查找启动顺序中的第一个设备,如果该设备没有操作系统,就查找第二个、第三个、第四个设备,在这 时其实可以解释一个常见的现象:
有的时候当我们的计算机上插着启动U盘时开机就会进入到U盘当中的系统,其原因就是在BIOS程序把U盘启动的顺序调整的比硬盘高,想要解决这个问题也很简单,把U盘拿掉再开机,这样话自举程序首先去找第一启动设备发现没有,自然会去第二个、第三个、第四个设备去查找操作系统的。
在这里我说明一个关系就是BIOS与COMS的关系,BIOS扮演着程序当中指令的角色,而COMS里面的数据扮演着程序当中数据的角色,两者必须相互配合才可正常工作,我们打开BIOS程序的过程其实是BIOS芯片当中的BIOS设置程序去读取COMS当中的数据的过程。
值得一提的是:BIOS芯片当中有这四个主要的程序,前三个都是加电之后在BIOS当中运行的,没有CPU的参与,只有自举程序会映射到内存当中需要cpu的参与才能运行起来。其实基本上所有的计算机都是通过自举程序启动起来的,自举的目的是找到MBR,但是如果找到了操作系统之后的动作就由bootloader来决定了。
MBR和grub
主引导记录(MBR,Main Boot Record)是位于磁盘最前边的一段引导(Loader)代码,它通常是由安装操作系统时的引导程序安装在磁盘上的,注意,是安装在磁盘第一个扇区,也就是说MBR最大也就是512个字节,分三部分,446个的字节的bootloader(通过是grub),和64个字节的分区柱面记录,余下的部分就无关紧要了。
对于内核来说rootfs是访问所有分区的入口,而对于grub来说则不然中,grub不遵守内核遵守的那一套,grub更加的简单粗暴,grub并不把rootfs当做是访问文件的入口。它是一个程序,是安装安装操作系统时的安装程序提供的,MBR是全磁盘级别,不单单属性哪一个操作系统或者分区的,这是整个磁盘共有的。
如果你的c盘安装一个windows8以上的版本,当然用MBR里面的bootloader引导没问题,但是如果你的D盘又安装了一个linux呢?没有办法了,因为windows8以上的版本就会锁定MBR(曰:防止病毒***MBR),不允许有其他的操作系统给MBR做任何操作,而微软给MBR安装的bootloder只能引导微软自己的系统,同时微软的操作系统也不让别的bootloder进行引导。如果一个人不接受任何的建议,只听从自己的本能,这是有多可怕。这像“金三胖”不允许国人出去,也不许另人进入自己的国家,不准上网,不准拍照,更可笑的是朝鲜官方还发布了一个视频,这个视频说美国人民都生活在野外,我们要去美国拯救他们,但是朝鲜一年要饿死的人数最多,这是“愚民政策”。
而linux的引导很开放的,谁都能引导。
lilo也是bootloader,但是有个缺点,就是不支持1024柱面以后的硬盘,也就是说引用的柱面有限,但在嵌入式应用比较广。
不管是lilo和grub能能发挥的空间都是446个字节,可为什么grub更加优秀呢?要想用户能用更好的用户体验,仅使用446字节是无法满足需示的的,grub采取了两个阶段的设计,也就是说grub当中存在的是仅仅是第一个阶段,第一个阶段存在的意义就是引导第二个阶段,所以第一阶段并不完成什么有意义的工作,并不负责引导操作系统。第二个阶段位与内核在同一个分区/BOOT/下的grub目录,但是要仔细的想一想,在MBR程序启动的时候,这时候并没有挂载根,而第二阶段所需要的文件都在根文件系统之下,那么第一个阶段要怎样才能访问第二个阶段呢?那么就有第1.5阶段,所谓的1.5阶段就是识别第二阶段所需要的文件所在的文件系统,我们只有识别了其文件系统才可以调用里面的数据。我们可以打开/boot/grub目录看一下:
第二阶段的功能就相当强大了,这个stage2的足足有125K,突破了446字节的限制,大家注意了没有,里面还有一个splash.xpm.gz的文件,这个就是brub的背景文件,当然我们可以替换他。此外还有一个配置文件“grub.conf”
[[email protected] grub]# rpm -q grub #查询grub的版本
grub-0.97-94.el6_7.1.x86_64
解释一个上面grub的配置文件:
default=0就是默认选择择第一个title(一个title就是一个内核或系统)
timeout=5等待用户选择的超时时间,5秒。
splashimage=这是用来指定背景图片路径 的。
hiddenumenu隐藏菜单,开机的时候是没有这个菜单,你一用键盘就显示,如果去掉这一行的话就不隐藏菜单了。
title CentOS 6 (2.6.32-642.el6.x86_64)这是每个tite后面跟的字符串(操作系统提示),可以随意的更改。
root (hd0,0) 内核文件所在的设备,对grub而言,所有类型硬盘一律为hd,第一个0表示第几个磁盘,最后的0表示对应磁盘的分区。
kernel /vmlinuz-2.6.32-642.el6.x86_64……这一行是内核的文件路径,及传递给内核的参数,当我们查看cat /proc/cmdline时,这里面就有启动内核的命令,与这里是一样的,
initrd /initramfs-2.6.32-642.el6.x86_64.img这里是ramdisk的文件路径
为什么内核的文件路径是从根开始?根本不是通过文件系统访问的路径,最后一行也是这个道理。
不知道有没有注意到,无论是linux的内核还是ramdisk都是从根开始,不过,我们从根下可以看到,linux的内核是在/boot目录上,为什么这里是显示从根开始呢?当grub程序启动的时候,根文件系统是没有挂载的,所以grub根本就不是通过根去访问boot下的grub,而gurb程序可以直接进入到有grub的那个文件分区,这个分区有文件系统,为了能够识别这个文件系统,所以在gurb目录里面放有很多的文件系统的识别。
而且intrd后面的路径当中的版本号一定会和内核的版本号是一样的,不过后面加了.img这个后缀,这个后缀就是代表这是一个镜像设备。
无论你挂载还是不挂载文件系统都是存在的,内核必须依靠根才能访问磁盘上的文件,但是grub就没有这个限制,grub可以不用过根而访问到/boot下的文件。
看到这里你可能又有疑问了,GRUB为什么能访问硬盘,grub没有硬盘的驱动,有的仅仅是向个文件系统而已,其实在安装操作系统的时候引导程序为grub访问硬盘提供了一个入口,所以grub可以直接进入到/boot目录。
kernel
当grub找到内核之后,就会把内核映射到内存当中,grub的任何就圆满完成了,正面就是kernel的“舞台”了,那么kernel要做什么呢?kernel要做的第一步肯定是要探测硬件,然后加载驱动模块,可以内核本身并没有驱动模块,驱动是存放在了硬盘的文件系统当中了,于是尴尬的问题又出现了,内核要加载驱动才能访问硬盘,可以想要加载驱动必须得先进入到硬盘的文件系统,这又怎样解决呢?通过ramdisk来解决。
ramdisk
内核在挂载根的时候会遇到一个很大的问题:根在根文件系统上,而根文件系统又在磁盘等设备的分区上,而内核的主体部分里面是没人任何设备驱动的,如果这样的话,那么内核就不能驱动设备,没有驱动设备肯定就不能挂载根文件系统呀?而所有的驱动又在根文件系统下放着,这就陷入一个死循环,类似的循环还有bios的产生,这要怎样解决呢?
在内核与磁盘分区之意再加一个中间层就可以解决这个问题?这个中间层就只有一个功能就只是为内核来提供驱动,可能你又会想了,如果这样的话那么这个中间层为了应付各种各校的驱动不就变得体积非常庞大了吗?看起来也不是很完美?
内核是编译好的,模块也是编译好的,但是这个中间层不是事先就编译好的,那么这个中间层程序是如何产生的呢?这个中间层文件是给在磁盘安装操作系统时的安装程序(注意不是操作系统而是安装程序,比如:老毛桃,大白菜)在给磁盘安装系统最后一步就是收集磁盘的驱动特性动态生成一个小程序,这个小程序正正好好可以驱动当前的磁盘,而这个小程序就是我们上文说的中间层。这个中间层把内核与根文件系统建立连接,看起来已经比较完美了,只要内核先加载这个中间层获取磁盘的驱动,然后再去加载根文件系统,但是,但是,但是,问题又来了,就是内核无论想访问任何的文件都要先经过根文件系统,而现在根文件系统在硬盘上呢?又陷入了一个死循环,而那个中间层只是一个小程序,内核想要访问它却无从下手,又该怎样解决这个问题呢?
其实在安装操作系统的引导程序生成中间层的时候不仅是生成了磁盘的驱动程序,其实还生成了一个根文件系统,这个根文件系统里面有/bin/sbin/lib等等与真正的是一样,而生成磁盘驱动程序也会在这个根文件系内部,所以内核在访问这个中间层的时候要先挂载这个中间层里面“伪根文件系统“然后才能加载到驱动,进而才能访问真正的磁盘,得到真正的根文件系统。当内核找到了真正的根,在真正的根上加载到了内核外围的驱动程序,就会“卸磨杀驴”,会一脚把原本曾经帮助它的“伪根文件系统“踢开,完成根的切换,从中间层的伪根切换成真正的根。
init
当grub把内核以及伪根映射到内存空间并把控制权交给内核之后,接下来内核要做的事大体就是这些。
- 内核挂载伪根ramdisk
- 设备探测获得硬件的特性信息
- 驱动初始化,这时可能会需要从伪根当中的/lib/modules当中装载驱动模块,挂载之后进行根切换
- 以只读挂载真正根文件系统,怕出现bug直接崩溃,因只需要读取文件,所以采用只读挂载
- 装载第一个进程init,init程序会进行再次以读写的方式挂载根
原文地址:http://blog.51cto.com/13778749/2159571