分享

6.4 初始RAM磁盘

 adkada 2011-11-10

6.4 初始RAM磁盘

Linux内核提供了一种机制,能挂载一个早期的根文件系统来执行启动相关的系统初始化和配置任务;该机制即为初始RAM磁盘(简称initrd)。相关支持代码必须直接编译到内核中,该内核配置选项位于内核配置工具的Block Devices,RAM disk support选项下,图6-1显示了initrd的配置界面。

 
(点击查看大图)图6-1 Linux内核配置工具

6.4.1 初始RAM磁盘的目的

初始RAM磁盘是一个功能完备的小型根文件系统,通常用来在系统引导过程结束之前加载一些特定的设备驱动模块。例如Red Hat和Fedora Core的Linux工作站发行版就使用了初始RAM磁盘,以便在挂载真正的根文件系统之前加载EXT3文件系统相关的设备驱动。Initrd一般用来加载访问真正的根文件系统所必需的设备驱动。

6.4.2 使用initrd引导

为了使用initrd的功能,绝大多数体系结构下,引导装入程序会负责将initrd映像传递到内核中。常见的情况是引导装入程序将一个压缩过的内核映像文件加载到内存中,并将initrd映像加载到另一段可用的内存区域中。要实现上述目的,在将控制权移交给内核之前,引导装入程序需要负责将initrd映像文件的加载地址传递给内核。具体的实现机制视体系结构、引导装入程序和平台实现的不同而有所差异;但不管怎样,内核都必须知道initrd映像的具体位置,这样才能进行正确加载。

对于某些体系结构和平台,构建内核时只产生一个复合的二进制映像,这种方案用在引导装入程序并不支持加载initrd映像的情况下。在这种情况下,内核和initrd映像被直接连接在一起。你可以在内核makefile文件中看到对bootpImage这个合成映像的说明,目前,这种方式仅用于arm体系结构。

那么内核又是如何知道哪里可以找到initrd映像呢?除非是通过引导装入程序中某些非常巧妙的地方,它通常是简单地通过内核命令行将initrd映像文件在内存中的起始地址和大小传递给内核来实现的。下面是使用内核命令行传递的一个例子,该例子所基于的平台是一个流行的ARM评估板,它所使用的处理器为TI的OMAP 5912。c

  1. console=ttyS0,115200 root=/dev/nfs                          
  2. \   
  3. nfsroot=192.168.1.9:/home/chris/sandbox/omap-target       
  4. \  
  5. 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引导内核

  1. # tftpboot 0x10000000 kernel-uImage  
  2. ...  
  3. Load address: 0x10000000  
  4. Loading: ############################ done  
  5. Bytes transferred = 1069092 (105024 hex)  
  6.  
  7. # tftpboot 0x10800000 initrd-uboot  
  8. ...  
  9. Load address: 0x10800000  
  10. Loading: ########################################### done  
  11. Bytes transferred = 282575 (44fcf hex)  
  12.  
  13. # bootm 0x10000000 0x10800040  
  14. Uncompressing kernel.................done.  
  15. ...  
  16. RAMDISK driver initialized: 16 RAM disks of 16384K size 1024 blocksize  
  17. ...  
  18. RAMDISK: Compressed image found at block 0  
  19. VFS: Mounted root (ext2 filesystem).  
  20. Greetings: this is linuxrc from Initial RAMDisk  
  21. Mounting /proc filesystem  
  22.  
  23. BusyBox v1.00 (2005.03.14-16:37+0000) Built-in shell (ash)  
  24. Enter 'help' for a list of built-in commands.  
  25.  
  26. # (<<<< 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映像地址。

这里特别关注一下U-Boot的特点。它完全支持通过以太网连接加载内核映像和ramdisk映像,这是一个非常有用的开发配置手段。当然,通过其他方式也能将内核映像和ramdisk映像加载到开发板中,比如可以使用基于硬件手段的闪存编程工具将它们烧写到闪存中,或者通过RS-232下载内核和文件系统的映像文件。但是由于这些映像文件一般都比较大(一个内核映像可能有一兆字节,ramdisk映像有几十兆字节),因此,如果采用基于以太网的tftp下载方式,开发人员就将节省大量的工程时间。不论采用哪种引导装入程序,务必确定它支持通过网络方式下载开发映像文件。

6.4.4 initrd的奥妙所在:linuxrc文件

在内核引导时,它会检测到存在initrd映像,将其从RAM中的指定物理地址处将这个二进制压缩格式文件复制到恰当的内核ramdisk中,并将其挂载为根文件系统。initrd的奥妙之处来自initrd映像中某个特殊文件的内容。当内核挂载这个初始ramdisk时,它会查找一个名为linuxrc的特殊文件;Linux内核会把该文件当作脚本文件,并执行包含在其中的命令。这一机制使得系统设计人员可以定制initrd的行为,代码清单6-11列举了一个简单的linuxrc文件。

代码清单6-11 linuxrc文件示例

  1. #!/bin/sh  
  2.  
  3. echo 'Greetings: this is 'linuxrc' from Initial Ramdisk'  
  4. echo 'Mounting /proc filesystem'  
  5. mount -t proc /proc /proc  
  6.  
  7. busybox sh 

实际上,该文件会包括在挂载真正的根文件系统之前需要执行的指令。例如,为了从CompactFlash存储设备上获取一个真正的根文件系统,可能需要加载CompactFlash设备的驱动程序。这个例子只是简单地创建一个busybox shell,并终止引导过程以进行研究。你可以从代码清单6-10中看到由busybox shell给出的提示符#。如果在该提示符后面键入exit命令,内核将继续其引导过程直到结束。

当内核将ramdisk映像从物理内存复制到内核ramdisk时,内核会释放原来ramdisk映像所占用的内存空间,你可以把它看作是从物理内存的实地址处将initrd映像文件复制到内核自身的虚拟内存中(以内核ramdisk设备的形式存在)的一个过程。

代码清单6-11中最后需要说明的一点是:挂载/proc文件系统,该挂载命令中的proc单词看似多余;下面的命令同样有效:

  1. mount -t proc none /proc 
注意,上面mount命令将设备名部分改为none,该挂载命令忽略了设备名描述,是因为并没有实际的物理设备与proc文件系统相对应,该命令采用-t proc就足以将/proc文件系统挂载到/proc挂载点。使用前一个命令形式只是为了说明我们正在将一个内核虚拟设备(/proc文件系统)挂载到/proc挂载点,实际上挂载命令会忽略这个参数(设备名)。你可以选择自己喜欢的做法。
 
6.4.5 initrd探究

作为Linux引导过程的一部分,Linux内核必须找出并且挂载一个根文件系统。在引导过程的后期,内核通过一个名为prepare_namespace()的函数来决定挂载什么并且在哪里挂载。如果当前内核如图6-1所示的配置启用了initrd支持,同时Linux内核命令也按这样进行了配置,那么内核就会对压缩的initrd映像文件进行解压缩,同时最终会从物理内存中把该映像文件内的内容复制到一个ramdisk设备(/dev/ram)里。这个时候,我们就在内核的ramdisk下有了一个恰当的文件系统。在将该文件系统的内容读到ramdisk之后,内核会将该ramdisk设备挂载为它的根文件系统。最后,内核生成一个内核线程来执行initrd映像 中的linuxrc脚本文件。

当执行linuxrc脚本之后,内核会卸载initrd并且继续执行系统引导的最后阶段。如果当前系统就有一个叫作/initrd的根设备,那么Linux会将initrd文件系统挂载到该路径下(在本书中称为挂载点);如果该目录(/initrd)不存在,那么initrd映像只是简单地被丢弃。

如果内核命令行中包含一个指定ramdisk的命令行参数root=(例如root=/dev/ram0),在前面内容中描述的initrd处理过程就会有两个重要的改变。首先,对于可执行文件linuxrc的处理会跳过;其次,Linux内核不会再试着挂载其他文件系统作为其根文件系统,也就是说你会有这样的一个Linux系统,即以initrd作为唯一的根文件系统。在对系统进行小型化配置的时候这样做是非常有益的。在这样的系统下,唯一的根文件系统即为ramdisk。将/dev/ram0内容放到内核命令行中将使得完全的系统初始化以initrd作为最终的根文件系统而结束。

6.4.6 构建initrd映像文件

构建一个合适的根文件系统映像是嵌入式系统开发所面临的挑战之一,而创建一个恰当的initrd映像则更具有挑战性,因为它要求更加简练、更加专业化。基于以上考虑,我们将在本节探讨initrd的要求以及initrd文件系统的内容。

使用tree命令来显示本章这个initrd映像示例中的内容,其内容如代码清单6-12所示。

代码清单6-12 initrd示例内容

  1. .  
  2. |-- bin  
  3. |   |-- busybox  
  4. |   |-- echo -> busybox  
  5. |   |-- mount -> busybox  
  6. |   '-- sh -> busybox  
  7. |-- dev  
  8. |   |-- console  
  9. |   |-- ram0  
  10. |   '-- ttyS0  
  11. |-- etc  
  12. |-- linuxrc  
  13. '-- proc  
  14.  
  15. 4 directories, 8 files 
可以看到,其内容非常简练,解压缩后的所有文件加起来不到500KB。由于该initrd是基于busybox构建的,因此它具有很多的功能。由于busybox是静态链接的,因此不依赖任何系统库。第11章将详细介绍busybox。
 
 
 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多