6.4 初始RAM磁盘
Linux内核提供了一种机制,能挂载一个早期的根文件系统来执行启动相关的系统初始化和配置任务;该机制即为初始RAM磁盘(简称initrd)。相关支持代码必须直接编译到内核中,该内核配置选项位于内核配置工具的Block Devices,RAM disk support选项下,图6-1显示了initrd的配置界面。
(点击查看大图)图6-1 Linux内核配置工具 |
6.4.1 初始RAM磁盘的目的
6.4.2 使用initrd引导
为了使用initrd的功能,绝大多数体系结构下,引导装入程序会负责将initrd映像传递到内核中。常见的情况是引导装入程序将一个压缩过的内核映像文件加载到内存中,并将initrd映像加载到另一段可用的内存区域中。要实现上述目的,在将控制权移交给内核之前,引导装入程序需要负责将initrd映像文件的加载地址传递给内核。具体的实现机制视体系结构、引导装入程序和平台实现的不同而有所差异;但不管怎样,内核都必须知道initrd映像的具体位置,这样才能进行正确加载。
对于某些体系结构和平台,构建内核时只产生一个复合的二进制映像,这种方案用在引导装入程序并不支持加载initrd映像的情况下。在这种情况下,内核和initrd映像被直接连接在一起。你可以在内核makefile文件中看到对bootpImage这个合成映像的说明,目前,这种方式仅用于arm体系结构。
那么内核又是如何知道哪里可以找到initrd映像呢?除非是通过引导装入程序中某些非常巧妙的地方,它通常是简单地通过内核命令行将initrd映像文件在内存中的起始地址和大小传递给内核来实现的。下面是使用内核命令行传递的一个例子,该例子所基于的平台是一个流行的ARM评估板,它所使用的处理器为TI的OMAP 5912。c
- console=ttyS0,115200 root=/dev/nfs
- \
- nfsroot=192.168.1.9:/home/chris/sandbox/omap-target
- \
- initrd=0x10800000,0x14af47
出于排版需要,上面的内核命令行被分成了几行显示;实际使用时,它是单独的一行,各部分之间用空格分隔。该内核命令行定义了如下内核行为:
在ttyS0设备上(波特率为115kbit/s)指定一个控制台;
通过NFS挂载一个根文件系统;
在宿主机192.168.1.9上找到NFS根文件系统,路径为/home/chris/sandbox/omaptarget;
从物理内存的0x10800000地址处加载并挂载一个初始RAM磁盘,其大小为0x14AF47(1 355 591字节)。
关于本例需要特别注意一点:几乎所有initrd映像都经过压缩,因此内核命令行里指定的initrd大小是指压缩后的initrd映像文件大小。
6.4.3 引导装入程序对于initrd的支持
我们来看一个运行在ARM处理器上的基于U-Boot的简单例子。这个引导装入程序已实现了对Linux内核的支持。使用U-Boot可以很容易地将initrd映像文件和内核映像文件组合在一起。代码清单6-10显示了一个包含ramdisk映像的典型引导过程。
代码清单6-10 使用ramdisk引导内核
- # tftpboot 0x10000000 kernel-uImage
- ...
- Load address: 0x10000000
- Loading: ############################ done
- Bytes transferred = 1069092 (105024 hex)
- # tftpboot 0x10800000 initrd-uboot
- ...
- Load address: 0x10800000
- Loading: ########################################### done
- Bytes transferred = 282575 (44fcf hex)
- # bootm 0x10000000 0x10800040
- Uncompressing kernel.................done.
- ...
- RAMDISK driver initialized: 16 RAM disks of 16384K size 1024 blocksize
- ...
- RAMDISK: Compressed image found at block 0
- VFS: Mounted root (ext2 filesystem).
- Greetings: this is linuxrc from Initial RAMDisk
- Mounting /proc filesystem
- BusyBox v1.00 (2005.03.14-16:37+0000) Built-in shell (ash)
- Enter 'help' for a list of built-in commands.
- # (<<<< Busybox command prompt)
从代码清单6-10中可以简单地了解U-Boot,下一章会更详细地介绍U-Boot。其中,U-Boot使用tftpboot命令从tftp服务器下载内核映像,内核映像下载后被放置到该目标板中内存起始地址256MB(16进制表示为0x10000000)处 。从tftp服务器下载的第二个映像文件是initrd映像,并放置在内存中较高的地址处(本例为256MB + 8MB地址处)。最后,我们使用了U-Boot的bootm命令(即boot from memory命令),它带有两个参数:前者为Linux内核映像的地址,后者是initrd映像地址。
在内核引导时,它会检测到存在initrd映像,将其从RAM中的指定物理地址处将这个二进制压缩格式文件复制到恰当的内核ramdisk中,并将其挂载为根文件系统。initrd的奥妙之处来自initrd映像中某个特殊文件的内容。当内核挂载这个初始ramdisk时,它会查找一个名为linuxrc的特殊文件;Linux内核会把该文件当作脚本文件,并执行包含在其中的命令。这一机制使得系统设计人员可以定制initrd的行为,代码清单6-11列举了一个简单的linuxrc文件。
代码清单6-11 linuxrc文件示例
- #!/bin/sh
- echo 'Greetings: this is 'linuxrc' from Initial Ramdisk'
- echo 'Mounting /proc filesystem'
- mount -t proc /proc /proc
- busybox sh
实际上,该文件会包括在挂载真正的根文件系统之前需要执行的指令。例如,为了从CompactFlash存储设备上获取一个真正的根文件系统,可能需要加载CompactFlash设备的驱动程序。这个例子只是简单地创建一个busybox shell,并终止引导过程以进行研究。你可以从代码清单6-10中看到由busybox shell给出的提示符#。如果在该提示符后面键入exit命令,内核将继续其引导过程直到结束。
当内核将ramdisk映像从物理内存复制到内核ramdisk时,内核会释放原来ramdisk映像所占用的内存空间,你可以把它看作是从物理内存的实地址处将initrd映像文件复制到内核自身的虚拟内存中(以内核ramdisk设备的形式存在)的一个过程。
代码清单6-11中最后需要说明的一点是:挂载/proc文件系统,该挂载命令中的proc单词看似多余;下面的命令同样有效:
- mount -t proc none /proc
作为Linux引导过程的一部分,Linux内核必须找出并且挂载一个根文件系统。在引导过程的后期,内核通过一个名为prepare_namespace()的函数来决定挂载什么并且在哪里挂载。如果当前内核如图6-1所示的配置启用了initrd支持,同时Linux内核命令也按这样进行了配置,那么内核就会对压缩的initrd映像文件进行解压缩,同时最终会从物理内存中把该映像文件内的内容复制到一个ramdisk设备(/dev/ram)里。这个时候,我们就在内核的ramdisk下有了一个恰当的文件系统。在将该文件系统的内容读到ramdisk之后,内核会将该ramdisk设备挂载为它的根文件系统。最后,内核生成一个内核线程来执行initrd映像 中的linuxrc脚本文件。
当执行linuxrc脚本之后,内核会卸载initrd并且继续执行系统引导的最后阶段。如果当前系统就有一个叫作/initrd的根设备,那么Linux会将initrd文件系统挂载到该路径下(在本书中称为挂载点);如果该目录(/initrd)不存在,那么initrd映像只是简单地被丢弃。
6.4.6 构建initrd映像文件
构建一个合适的根文件系统映像是嵌入式系统开发所面临的挑战之一,而创建一个恰当的initrd映像则更具有挑战性,因为它要求更加简练、更加专业化。基于以上考虑,我们将在本节探讨initrd的要求以及initrd文件系统的内容。
使用tree命令来显示本章这个initrd映像示例中的内容,其内容如代码清单6-12所示。
代码清单6-12 initrd示例内容
- .
- |-- bin
- | |-- busybox
- | |-- echo -> busybox
- | |-- mount -> busybox
- | '-- sh -> busybox
- |-- dev
- | |-- console
- | |-- ram0
- | '-- ttyS0
- |-- etc
- |-- linuxrc
- '-- proc
- 4 directories, 8 files