为什么要引入initrd?
Linux启动过程中肯定要载入内核镜像,在此过程中有些要素必须考虑:
首先,内核镜像不能太大。由于受到各种硬件和兼容性的限制,Linux的内核镜像不能太大,但是这并不容易做到。Linux内核的核心部分本身就不小了;而且还必须加入会使用到的驱动程序。
其次,要支持尽可能多的硬件设备。我们在启动过程中有一件重要工作:挂载root文件系统,因为进一步的数据和应用软件都在其上,所以我们的内核必须能够访问root文件系统。对于一般用户,如果他们使用IDE硬盘上的ext2文件分区作为root文件系统,不会有什么问题。因为不管是IDE硬盘还是 ext2文件系统,它们的驱动肯定会包含在内核镜像自身里面。但是,确实存在一些特殊情况:比如说我们希望发行Linux系统的安装光盘,那么对光盘的驱动,就不一定包含在内核里面了。(有人可能要奇怪了,咦,光盘中的内核镜像不都已经读进来了吗,怎么内核还访问不了光盘呢?注意,读入内核镜像的是 Boot Loader ,内核并不具备 Boot Loader 的功能。)如果没有光盘的驱动,我们又怎么把光盘里的软件包安装到用户的计算机里呢?把驱动程序预先编译到内核里?听起来还不错,可是如果我们除了光盘还有一些其它的安装介质,那么所有这些驱动就会让内核镜像庞大不堪。
而且,还有更严重的问题,各种不同的驱动程序很有可能会发生冲突,特别是以前ISA设备占市场主导地位的时候,这种冲突简直难以避免。
那时的解决办法是发行商提供预先编译好的支持各种设备的不同内核,把每个内核放进一张软盘,随发行包一起交给用户,用户自己选择装有合适内核的软盘进行引导。或者给用户提供制作引导盘的工具,让用户在安装前制作自己的启动盘。当然,哪一种办法都不能让人满意。
唯一的希望在于使用模块化机制。在内核启动的时候调用相应的模块加载驱动程序,然后访问root文件系统。无论是通过内核对设备做进一步的分析还是直接从用户那里得到配置信息,先配置再加载模块的办法,都能有效地避免冲突的发生。
除了在安装的时候需要在挂载root文件系统之前调用相应的模块之外,在完成安装的系统上,我们可能仍然需要在挂载root文件系统之前调用一些模块。这主要是为给计算机进行配置——一般都要针对不同的计算机进行内核配置。
理想情况下,用户按照自己的实际情况配置编译文件,重新编译内核,一步步完成这种工作。但是没有几个用户喜欢这种冗长并且极易出现错误的工作。而且编译和生成内核需要相应的工具,可是大部分用户不需要这些工具。
在安装的过程中可以直接编译一个整体式的内核,但这并不能很好的解决问题:首先,所有的编译工具还需要,其次,编译过程中出现差错导致无法完成任务的概率太大了。所以,我们仍然要使用模块机制:模块机制很可靠,出了错误也只不过不加载对应的模块而已,不会使整个任务失败。而载入模块,象前面说的,也是在挂载root文件系统之前就要得到模块的。
基于以上理由,Linux引入了initrd机制。
initrd做什么
initrd允许系统在启动的时候载入一个RAM盘,这个RAM盘可以被当作一个root文件系统,程序可以在其上运行。(有两重含义,第一,程序在上面;第二,程序的文件系统环境也在上面。)在此之后,可以从别的设备上挂载一个新的root文件系统,先前的root文件系统(initrd)就会被移动到一个目录上去,最终被卸载掉。
为什么要使用RAM盘呢?首先,使用RAM盘能方便的支持以后可能发生的变化;另外,也是为了保持 Boot Loader 工作尽可能的简单。在系统引导时,除了内核镜像之外,Boot Loader把所有相关的信息作为一个文件读入内存,内核在启动中将该文件作为一段连续的内存块看待。也就是把它当作RAM盘来使用了。正因为如此,这种机制被称作“初始 RAM 盘 (initial RAM Disk)”,缩 写成 initrd。
initrd主要用来把系统的启动划分为两个阶段:初始启动的内核只需保留最精简的驱动程序最小集,此后,在启动必须加载附加的模块时,从initrd中加载。
initrd进行的操作
使用initrd的时候,典型的系统启动的流程变为:
Boot Loader读入内核镜像以及initrd文件
内核将initrd文件转成“普通”的RAM盘,并且释放掉initrd文件占用的内存。
initrd被当作root文件系统,以可读可写(read-write)方式安装。
/linuxrc被执行(它可以是任何可执行文件,包括脚本在内;它以uid0身份执行,基本上能完成所有init程序可以做的工作)
linuxrc安装“实际” 的root文件系统
linuxrc通过pivot_root系统调用将root文件系统放置在root目录下。
常用的启动流 程(比如调用/sbin/init)开始执行。
卸载initrd文件系统。
注意,这是一个典型流程。其实initrd机制可以通过两种方式使用:要么就是作为一个普通的root文件系统使用,这样的话第5、第6两个步骤可以被略过,直接执行/sbin/init(我们的试验系统就是利用这种方法);要么作为一个过渡环境使用,通过它内核可以继续装载“实际”的root文件系统。