一阶段引导
为了使用一阶段引导,我们需要构建内核,其具有安装内置根文件系统所需的所有驱动器(其他任何驱动器都可以在正常初始化过程中,在能够从根分区加载的模块中构建)。
如果我们要从非常小的设备引导(如软盘),最好的方法是构建的内核仅具有足够使我们可以安装根外置文件系统的内置驱动器-然后将其他所有项构建为模块。例如,我内置了SCSI支持、PCMCIA支持、IEE1394、SBP和类似支持,但是其他所有项(包括显卡支持、网络设备支持等等)都作为模块构建,这些模块存储在根分区(在外置驱动器上)中,而不是软盘上。
使用简单(一阶段)引导过程,我们应该能够构建具有所需支持的内核,将其放在软盘驱动器中,在软盘中安装引导加载程序(我使用 GRUB,但还有其他选择,如LILO),然后使用与此内核(对于GRUB)相似的内核引导:
root (fd0)
kernel (fd0)/boot/bzImage root=/dev/sda1
这种方法基本上可以工作,但有两个问题:
因为SBP支持使用SCSI仿真,为了检测磁盘和允许安装/dev/sda1,需要“重新扫描”仿真的SCSI总线。这种扫描使用一组简单的命令执行。不过,遗憾的是,使用一阶段引导,我们不能运行任何命令,直到内核已经完成引导,而内核直到安装了根文件系统才能完成引导-典型的自相矛盾困境。令人感到高兴的是,对于导致SCSI总线在启动时被扫描的2.4内核有可用的修补程序(有关更多详细信息,请参阅参考资料)。通过应用此修补程序,我可以使外置驱动器在引导过程中由内核自动检测,而不需要任何重新扫描命令。这使我们进入了下一个问题。
内核中有定时窗口,这意味着内核经常在其能够被正确的监测和初始化之前尝试安装根设备。对于此问题,也有可用的修补程序(请参阅参考资料获得相关链接),它只是使内核在启动时等待很短的时间,并使其在安装根文件系统失败时重试(为外置驱动器提供时间识别)。
通过应用这两个修补程序,我可以成功地在可引导软盘上构建内核,其将引导,然后使用外置FireWire驱动器作为根。
这种方法的主要问题是需要我们给内核源码打补丁-这最多是一件痛苦之事(当发行新的内核版本时),严重时会是个大问题(如果没有维护补丁程序与内核发生的其他更改保持一致的话)。
您可能已经想到如果我们的BIOS支持USB或FireWire且我们直接引导,我们就可以避免这两个问题。不幸的是,情况并不是这样:虽然此方法在引导过程中使用BIOS调用来访问磁盘,一旦内核开始初始化,将不再使用BIOS,而是使用内核驱动器访问磁盘-这样就会遇到相同的问题。
两阶段引导到了内核版本2.0.X,向Linux内核添加了一项引人注意的能力-使用“initial RAM disk”(或initrd)提供两阶段引导过程。
简而言之,内核像平常一样引导;但不安装“真实的”根文件系统,而是在RAM中创建微型根文件系统并安装该系统。在安装真实的根、切换为使用真实的根并销毁initial RAM disk之前,任何步骤都可以在此初始环境中执行。
这在各种环境中都有用,但是为了便于说明,我们将仅使用我们的迷你环境重新扫描SCSI总线,等待外置磁盘被识别,然后切换为使用该磁盘作为真实的根继续引导。
为了使用这种方法,我们需要创建两项,内核和initrd映像。
内核就是具有内置initrd支持的普通内核。initrd映像是包含我们的迷你根文件系统的回送文件系统映像(此映像可以使用gzip进行压缩以减少其大小)。
有关创建或定制自己的initrd映像的详细信息,可以查看参考资料部分。
在initrd映像中,有一个名为linuxrc的文件。当加载initrd时会执行此文件,所以确保其具有执行权限!我们为了进行说明,所以 linuxrc非常简单:
清单 1. initrd linuxrc #!/bin/sh REAL_ROOT=/dev/sda1 # mount the /proc filesystem mount -t proc none /proc #for scsi-emulation # modprobe sd_mod #for pcmcia # modprobe pcmcia_core #for FireWire # modprobe ieee1394 # modprobe ohci1394 # modprobe raw1394 # modprobe sbp2 #for USB # modprobe usbcore # modprobe ohci-hcd # modprobe uhci-hcd # modprobe usb-storage # loop rescanning the scsi bus + rerunning devfsd retries=5 i=1 until [ -e $REAL_ROOT ] do if [ $i -gt $retries ] then echo "Unable to mount real root ($REAL_ROOT) - Giving up!" /bin/ash exit fi echo "Real root ($REAL_ROOT) not found, retrying ($i)" sleep 1 echo "scsi add-single-device 0 0 0" > /proc/scsi/scsi echo "scsi add-single-device 1 0 0" > /proc/scsi/scsi echo "scsi add-single-device 2 0 0" > /proc/scsi/scsi /bin/devfsd /dev -np i=$((i+1)) done #umount /proc as it will be remounted by the normal init process umount /proc #now we simply exit, and the normal boot process should continue exit 0 |
我们做的所有操作都是加载适当的模块来支持外置驱动器:它们应该根据需要被解注。(我在内核中构建了所有必需的支持,因此不需要任何模块。)然后我们进行循环,重新扫描SCSI总线(通过将命令回送到 /proc pseudo-filesystem 中的特殊文件,并调用 devfsd ),直到出现根设备(我的例子中为 /dev/sda1)。在我的例子中,讨论的仿真FireWire SCSI总线是100,不过也可以尝试其他的,而不会有任何负面影响-如果您知道要使用的总线,可以裁剪脚本。同样,如果您有其他SCSI设备(或仿真SCSI设备),驱动器可能会有不同的字母(例如,/dev/sdb1)。如果不使用外置驱动器的第一个分区,则需要使用不同的编号(例如,/dev/sda2)。
现在所需要做的就是将相关文件复制到initrd映像中(可以使用mount-o loop 命令安装未压缩的映像)。特别地,需要确保具有linuxrc文件、在其中使用的所有命令和那些命令依靠的所有库。然后,(未装载的)映像可以进行压缩。
接着把内核(bzImage)和initrd像(initrd.gz)复制到(bootable,ext3)软盘中。
最后一步是在软盘中安装引导加载程序,并使用下列选项引导内核:kernel bzImage root=/dev/sda1 initrd=initrd.gz。
现在应该可以使用软盘进行引导:它将从软盘加载内核,将initrd映像加载到RAM中,等待识别根设备,然后像平常一样从那里继续引导。从此以后,可以移除软盘。
如果软盘不适合(例如,如果计算机没有软盘驱动器),则可以使用能够通过BIOS引导的任何设备。就个人而言,为了写作本文,我使用小的32Mb USB盘。或者,如果您不介意改变内置硬盘驱动器的话,为了更便于引导,可以在其中创建小的分区。