什么是初始 RAM 磁盘?
初始 RAM 磁盘(initrd)是在实际根文件系统可用之前挂载到系统中的一个初始根文件系统。initrd 与内核绑定在一起,并作为内核引导过程的一部分进行加载。内核然后会将这个 initrd 文件作为其两阶段引导过程的一部分来加载模块,这样才能稍后使用真正的文件系统,并挂载实际的根文件系统。
initrd 中包含了实现这个目标所需要的目录和可执行程序的最小集合,例如将内核模块加载到内核中所使用的 insmod
工具。
在桌面或服务器 Linux 系统中,initrd 是一个临时的文件系统。其生存周期很短,只会用作到真实文件系统的一个桥梁。在没有存储设备的嵌入式系统中,initrd 是永久的根文件系统。本文将对这两种情况进行探索。
initrd 剖析
initrd 映像中包含了支持 Linux 系统两阶段引导过程所需要的必要可执行程序和系统文件。
根据我们运行的 Linux 的版本不同,创建初始 RAM 磁盘的方法也可能会有所不同。在 Fedora Core 3 之前,initrd 是使用 loop 设备 来构建的。loop 设备 是一个设备驱动程序,利用它可以将文件作为一个块设备挂载到系统中,然后就可以查看这个文件系统中的内容了。在您的内核中可能并没有 loop 设备,不过这可以通过内核配置工具(make menuconfig
)选择 Device Drivers > Block Devices > Loopback Device Support 来启用。我们可以按照下面的方法来查看 loop 设备的内容(initrd 文件的名字可能会稍有不同):
清单 1. 查看 initrd 的内容(适用于 FC3 之前的版本)
# mkdir temp ; cd temp
# cp /boot/initrd.img.gz .
# gunzip initrd.img.gz
# mount -t ext -o loop initrd.img /mnt/initrd
# ls -la /mnt/initrd
#
|
现在我们就可以查看 /mnt/initrd 子目录中的内容了,这就代表了 initrd 文件的内容。注意,即使您的 initrd 映像文件不是以 .gz 结尾,它也可能是一个压缩文件,您可以给这个文件添加上 .gz 后缀,然后再使用 gunzip 对其进行解压。
从 Fedora Core 3 开始,默认的 initrd 映像变成了一个经过压缩的 cpio 归档文件。我们不用再使用 loop 设备来将 initrd 作为压缩映像进行挂载,而是可以将其作为 cpio 归档文件来使用。要查看 cpio 归档文件的内容,可以使用下面的命令:
清单 2. 查看 initrd 的内容(适用于 FC3 及其以后的版本)
# mkdir temp ; cd temp
# cp /boot/initrd-2.6.14.2.img initrd-2.6.14.2.img.gz
# gunzip initrd-2.6.14.2.img.gz
# cpio -i --make-directories < initrd-2.6.14.2.img
#
|
结果会生成一个很小的根文件系统,如清单 3 所示。在 ./bin 目录中有一组很少但却非常必要的应用程序,包括 nash
(即 not a shell,是一个脚本解释器)、insmod
(用来加载内核模块)和 lvm
(逻辑卷管理工具)。
清单 3. 默认的 Linux initrd 目录结构
# ls -la
#
drwxr-xr-x 10 root root 4096 May 7 02:48 .
drwxr-x--- 15 root root 4096 May 7 00:54 ..
drwxr-xr-x 2 root root 4096 May 7 02:48 bin
drwxr-xr-x 2 root root 4096 May 7 02:48 dev
drwxr-xr-x 4 root root 4096 May 7 02:48 etc
-rwxr-xr-x 1 root root 812 May 7 02:48 init
-rw-r--r-- 1 root root 1723392 May 7 02:45 initrd-2.6.14.2.img
drwxr-xr-x 2 root root 4096 May 7 02:48 lib
drwxr-xr-x 2 root root 4096 May 7 02:48 loopfs
drwxr-xr-x 2 root root 4096 May 7 02:48 proc
lrwxrwxrwx 1 root root 3 May 7 02:48 sbin -> bin
drwxr-xr-x 2 root root 4096 May 7 02:48 sys
drwxr-xr-x 2 root root 4096 May 7 02:48 sysroot
#
|
清单 3 中比较有趣的是 init 文件就在根目录中。与传统的 Linux 引导过程类似,这个文件也是在将 initrd 映像解压到 RAM 磁盘中时被调用的。在本文稍后我们将来探索这个问题。
创建 initrd 所使用的工具
|
cpio 命令
使用 cpio 命令,我们可以对 cpio 文件进行操作。cpio 是一种文件格式,它简单地使用文件头将一组文件串接在一起。cpio 文件格式可以使用 ASCII 和二进制文件。为了保证可移植性,我们可以使用 ASCII 格式。为了减小文件大小,我们可以使用二进制的版本。
|
|
下面让我们回到最开始,来看一下 initrd 映像最初是如何构建的。对于传统的 Linux 系统来说,initrd 映像是在 Linux 构建过程中创建的。有很多工具,例如 mkinitrd
,都可以用来使用必要的库和模块自动构建 initrd,从而用作与真实的根文件系统之间的桥梁。mkinitrd
工具实际上就是一个 shell 脚本,因此我们可以看到它究竟是如何来实现这个结果的。还有一个 YAIRD
(即 Yet Another Mkinitrd)工具,可以对 initrd 构建过程的各个方面进行定制。
手工构建定制的初始 RAM 磁盘
由于在很多基于 Linux 的嵌入式系统上没有硬盘,因此 initrd 也会作为这种系统上的永久根文件系统使用。清单 4 显示了如何创建一个 initrd 映像文件。我使用了一个标准的 Linux 桌面,这样您即使没有嵌入式平台,也可以按照下面的步骤来执行了。除了交叉编译,其他概念(也适用于 initrd 的构建)对于嵌入式平台都是相同的。
清单 4. 创建定制 initrd 的工具(mkird)
#!/bin/bash
# Housekeeping...
rm -f /tmp/ramdisk.img
rm -f /tmp/ramdisk.img.gz
# Ramdisk Constants
RDSIZE=4000
BLKSIZE=1024
# Create an empty ramdisk image
dd if=/dev/zero of=/tmp/ramdisk.img bs=$BLKSIZE count=$RDSIZE
# Make it an ext2 mountable file system
/sbin/mke2fs -F -m 0 -b $BLKSIZE /tmp/ramdisk.img $RDSIZE
# Mount it so that we can populate
mount /tmp/ramdisk.img /mnt/initrd -t ext2 -o loop=/dev/loop0
# Populate the filesystem (subdirectories)
mkdir /mnt/initrd/bin
mkdir /mnt/initrd/sys
mkdir /mnt/initrd/dev
mkdir /mnt/initrd/proc
# Grab busybox and create the symbolic links
pushd /mnt/initrd/bin
cp /usr/local/src/busybox-1.1.1/busybox .
ln -s busybox ash
ln -s busybox mount
ln -s busybox echo
ln -s busybox ls
ln -s busybox cat
ln -s busybox ps
ln -s busybox dmesg
ln -s busybox sysctl
popd
# Grab the necessary dev files
cp -a /dev/console /mnt/initrd/dev
cp -a /dev/ramdisk /mnt/initrd/dev
cp -a /dev/ram0 /mnt/initrd/dev
cp -a /dev/null /mnt/initrd/dev
cp -a /dev/tty1 /mnt/initrd/dev
cp -a /dev/tty2 /mnt/initrd/dev
# Equate sbin with bin
pushd /mnt/initrd
ln -s bin sbin
popd
# Create the init file
cat >> /mnt/initrd/linuxrc << EOF
#!/bin/ash
echo
echo "Simple initrd is active"
echo
mount -t proc /proc /proc
mount -t sysfs none /sys
/bin/ash --login
EOF
chmod +x /mnt/initrd/linuxrc
# Finish up...
umount /mnt/initrd
gzip -9 /tmp/ramdisk.img
cp /tmp/ramdisk.img.gz /boot/ramdisk.img.gz
|
|
initrd Linux 发行版
Minimax 是一个开放源码项目,其设计目标是成为一个全部封装在 initrd 中的 Linux 发行版。它的大小是 32MB,为了尽量小,它使用了 BusyBox 和 uClibc。除了非常小之外,它还使用了 2.6 版本的 Linux 内核,并提供了很多有用的工具。
|
|
为了创建 initrd,我们最开始创建了一个空文件,这使用了 /dev/zero
(一个由零组成的码流)作为输入,并将其写入到 ramdisk.img 文件中。所生成的文件大小是 4MB(4000 个 1K 大小的块)。然后使用 mke2fs
命令在这个空文件上创建了一个 ext2(即 second extended)文件系统。现在这个文件变成了一个 ext2 格式的文件系统,我们使用 loop 设备将这个文件挂载到 /mnt/initrd 上了。在这个挂载点上,我们现在就有了一个目录,它以 ext2 文件系统的形式呈现出来,我们可以对自己的 initrd 文件进行拼装了。接下来的脚本提供了这种功能。
下一个步骤是创建构成根文件系统所需要的子目录:/bin、/sys、/dev 和 /proc。这里只列出了所需要的目录(例如没有库),但是其中包含了很多功能。
|
ext2 文件系统的替代品
尽管 ext2 是一种通用的 Linux 文件系统格式,但是还有一些替代品可以减小 initrd 映像文件以及所挂载上来的文件系统的大小。这种文件系统的例子有 romfs(ROM 文件系统)、cramfs(压缩 ROM 文件系统)和 squashfs(高度压缩只读文件系统)。如果我们需要暂时将数据写入文件系统中,ext2 可以很好地实现这种功能。最后,e2compr 是 ext2 文件系统驱动程序的一个扩展,可以支持在线压缩。
|
|
为了可以使用根文件系统,我们使用了 BusyBox。这个工具是一个单一映像,其中包含了很多在 Linux 系统上通常可以找到的工具(例如 ash、awk、sed、insmod 等)。BusyBox 的优点是它将很多工具打包成一个文件,同时还可以共享它们的通用元素,这样可以极大地减少映像文件的大小。这对于嵌入式系统来说非常理想。将 BusyBox 映像从自己的源目录中拷贝到自己根目录下的 /bin 目录中。然后创建了很多符号链接,它们都指向 BusyBox 工具。BusyBox 会判断所调用的是哪个工具,并执行这个工具的功能。我们在这个目录中创建了几个链接来支持 init 脚本(每个命令都是一个指向 BusyBox 的链接。)
下一个步骤是创建几个特殊的设备文件。我从自己当前的 /dev 子目录中直接拷贝了这些文件,这使用了 -a
选项(归档)来保留它们的属性。
倒数第二个步骤是生成 linuxrc 文件。在内核挂载 RAM 磁盘之后,它会查找 init
文件来执行。如果没有找到 init
文件,内核就会调用 linuxrc 文件作为自己的启动脚本。我们在这个文件中实现对环境的基本设置,例如挂载 /proc 文件系统。除了 /proc 之外,我还挂载了 /sys 文件系统,并向终端打印一条消息。最后,我们调用了 ash
(一个 Bourne Shell 的克隆),这样就可以与根文件系统进行交互了。linuxrc 文件然后使用 chmod
命令修改成可执行的。
最后,我们的根文件系统就完成了。我们将其卸载掉,然后使用 gzip
对其进行压缩。所生成的文件(ramdisk.img.gz)被拷贝到 /boot 子目录中,这样就可以通过 GNU GRUB 对其进行加载了。
要构建初始 RAM 磁盘,我们可以简单地调用 mkird
,这样就会自动创建这个映像文件,并将其拷贝到 /boot 目录中。
测试定制的初始 RAM 磁盘
|
Linux 内核中对 initrd 的支持
对于 Linux 内核来说,要支持初始 RAM 磁盘,内核必须要使用 CONFIG_BLK_DEV_RAM 和 CONFIG_BLK_DEV_INITRD 选项进行编译。
|
|
新的 initrd 映像现在已经在 /boot 目录中了,因此下一个步骤是使用默认的内核来对其进行测试。现在我们可以重新启动 Linux 系统了。在出现 GRUB 界面时,按 C 键启动 GRUB 中的命令行工具。我们现在可以与 GRUB 进行交互,从而定义要加载哪个内核和 initrd 映像文件。kernel
命令让我们可以指定内核文件,initrd
命令可以用来指定 initrd 映像文件。在定义好这些参数之后,就可以使用 boot
命令来引导内核了,如清单 5 所示。
清单 5. 使用 GRUB 手工引导内核和 initrd
GNU GRUB version 0.95 (638K lower / 97216K upper memory)
[ Minimal BASH-like line editing is supported. For the first word, TAB
lists possible command completions. Anywhere else TAB lists the possible
completions of a device/filename. ESC at any time exits.]
grub> kernel /bzImage-2.6.1
[Linux-bzImage, setup=0x1400, size=0x29672e]
grub> initrd /ramdisk.img.gz
[Linux-initrd @ 0x5f2a000, 0xb5108 bytes]
grub> boot
Uncompressing Linux... OK, booting the kernel.
|
在内核启动之后,它会检查是否有 initrd 映像文件可用(稍后会更详细介绍),然后将其加载,并将其挂载成根文件系统。在清单 6 中我们可以看到这个 Linux 启动过程最后的样子。在启动之后,ash shell 就可以用来输入命令了。在这个例子中,我们将浏览一下根文件系统的内容,并查看一下虚拟 proc 文件系统中的内容。我们还展示了如何通过 touch 命令在文件系统中创建文件。注意所创建的第一个进程是 linuxrc
(通常都是 init
)。
清单 6. 使用简单的 initrd 引导 Linux 内核
...
md: Autodetecting RAID arrays
md: autorun
md: ... autorun DONE.
RAMDISK: Compressed image found at block 0
VFS: Mounted root (ext2 file system).
Freeing unused kernel memory: 208k freed
/ $ ls
bin etc linuxrc proc sys
dev lib lost+found sbin
/ $ cat /proc/1/cmdline
/bin/ash/linuxrc
/ $ cd bin
/bin $ ls
ash cat echo mount sysctl
busybox dmesg ls ps
/bin $ touch zfile
/bin $ ls
ash cat echo mount sysctl
busybox dmesg ls ps zfile
|
使用初始 RAM 磁盘来引导系统
现在我们已经了解了如何构建并使用定制的初始 RAM 磁盘,本节将探索内核是如何识别 initrd 并将其作为根文件系统进行挂载的。我们将介绍启动链中的几个主要函数,并解释一下到底在进行什么操作。
引导加载程序,例如 GRUB,定义了要加载的内核,并将这个内核映像以及相关的 initrd 拷贝到内存中。我们可以在 Linux 内核源代码目录中的 ./init 子目录中找到很多这种功能。
在内核和 initrd 映像被解压并拷贝到内存中之后,内核就会被调用了。它会执行不同的初始化操作,最终您会发现自己到了 init/main.c:init()
(subdir/file:function)函数中。这个函数执行了大量的子系统初始化操作。此处会执行一个对 init/do_mounts.c:prepare_namespace()
的调用,这个函数用来准备名称空间(挂载 dev 文件系统、RAID 或 md、设备以及最后的 initrd)。加载 initrd 是通过调用 init/do_mounts_initrd.c:initrd_load()
实现的。
initrd_load()
函数调用了 init/do_mounts_rd.c:rd_load_image()
,它通过调用 init/do_mounts_rd.c:identify_ramdisk_image()
来确定要加载哪个 RAM 磁盘。这个函数会检查映像文件的 magic 号来确定它是 minux、etc2、romfs、cramfs 或 gzip 格式。在返回到 initrd_load_image
之前,它还会调用 init/do_mounts_rd:crd_load()
。这个函数负责为 RAM 磁盘分配空间,并计算循环冗余校验码(CRC),然后对 RAM 磁盘映像进行解压,并将其加载到内存中。现在,我们在一个适合挂载的块设备中就有了这个 initrd 映像。
现在使用一个 init/do_mounts.c:mount_root()
调用将这个块设备挂载到根文件系统上。它会创建根设备,并调用 init/do_mounts.c:mount_block_root()
。在这里调用 init/do_mounts.c:do_mount_root()
,后者又会调用 fs/namespace.c:sys_mount()
来真正挂载根文件系统,然后 chdir
到这个文件系统中。这就是我们在清单 6 中所看到的熟悉消息 VFS: Mounted root (ext2 file system).
的地方。
最后,返回到 init
函数中,并调用 init/main.c:run_init_process
。这会导致调用 execve
来启动 init 进程(在本例中是 /linuxrc
)。linuxrc 可以是一个可执行程序,也可以是一个脚本(条件是它有脚本解释器可用)。
这些函数的调用层次结构如清单 7 所示。尽管此处并没有列出拷贝和挂载初始 RAM 磁盘所涉及的所有函数,但是这足以为我们提供一个整体流程的粗略框架。
清单 7. initrd 加载和挂载过程中所使用的主要函数的层次结构
init/main.c:init
init/do_mounts.c:prepare_namespace
init/do_mounts_initrd.c:initrd_load
init/do_mounts_rd.c:rd_load_image
init/do_mounts_rd.c:identify_ramdisk_image
init/do_mounts_rd.c:crd_load
lib/inflate.c:gunzip
init/do_mounts.c:mount_root
init/do_mounts.c:mount_block_root
init/do_mounts.c:do_mount_root
fs/namespace.c:sys_mount
init/main.c:run_init_process
execve
|
无盘引导
与嵌入式引导的情况类似,本地磁盘(软盘或 CD-ROM)对于引导内核和 ramdisk 根文件系统来说都不是必需的。DHCP(Dynamic Host Configuration Protocol)可以用来确定网络参数,例如 IP 地址和子网掩码。TFTP(Trivial File Transfer Protocol)可以用来将内核映像和初始 ramdisk 映像传输到本地设备上。传输完成之后,就可以引导 Linux 内核并挂载 initrd 了,这与本地映像引导的过程类似。
压缩 initrd
在构建嵌入式系统时,我们可能希望将 initrd 映像文件做得尽可能小,这其中有一些技巧需要考虑。首先是使用 BusyBox(本文中已经展示过了)。BusyBox 可以将数 MB 的工具压缩成几百 KB。
在这个例子中,BusyBox 映像是静态链接的,因此它不需要其他库。然而,如果我们需要标准的 C 库(我们自己定制的二进制可能需要这个库),除了巨大的 glibc 之外,我们还有其他选择。第一个较小的库是 uClibc,这是为对空间要求非常严格的系统准备的一个标准 C 库。另外一个适合空间紧张的环境的库是 dietlib。要记住我们需要使用这些库来重新编译想在嵌入式系统中重新编译的二进制文件,因此这需要额外再做一些工作(但是这是非常值得的)。
结束语
初始 RAM 磁盘最初是设计用来通过一个临时根文件系统来作为内核到最终的根文件系统之间的桥梁。initrd 对于在嵌入式系统中加载到 RAM 磁盘里的非持久性根文件系统来说也非常有用。
参考资料
学习
获得产品和技术
-
cpio 文件格式(现在可以用作 Fedora Core 的一种 initrd 映像格式)具有很长的历史,可以在很多 UNIX 系统上使用。
-
ash shell 是 Bourne Shell 的一个克隆(它们大部分是兼容的),它虽然很小,但是完全可以正常工作。它非常适合在对空间要求非常严格的嵌入式系统上用作脚本解释器。
-
BusyBox 是一种缩减您下一个嵌入式 Linux 项目内存需求的好方法。
- 要进一步缩减 initrd 文件的大小,请考虑使用 glibc 的替代库,例如 uClibc 或 dietlib。如果您喜欢使用 C++,那么可以试用一下 uClibc++ 库的 Alpha 版本。
-
Minimax 是一个完全封装在 initrd 映像文件中的 Linux 发行版!
-
订购免费的 SEK for Linux,这有两张 DVD,包括最新的 IBM for Linux 的试用软件,包括 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere®。
- 在您的下一个开发项目中采用 IBM 试用软件,这可以从 developerWorks 上直接下载。
讨论
关于作者
|
|
|
Tim Jones 是一名嵌入式软件工程师,他是 GNU/Linux Application Programming、AI Application Programming 以及 BSD Sockets Programming from a Multilanguage Perspective 等书的作者。他的工程背景非常广泛,从同步宇宙飞船的内核开发到嵌入式架构设计,再到网络协议的开发。Tim 是 Emulex Corp. 的一名资深软件工程师。
|
分享到:
相关推荐
### Linux初始RAM磁盘(initrd)概述 #### 什么是初始RAM磁盘? Linux初始RAM磁盘(initrd)是一种特殊的临时根文件系统,在系统启动过程中被挂载以支持两阶段启动过程。在真实根文件系统的可用性之前,initrd作为...
Linux系统内存磁盘初始化技术,即initrd(Initial RAM Disk),是Linux内核启动过程中的一个重要环节,尤其在处理复杂的系统引导场景时。initrd是一个临时的根文件系统,它在系统启动时被加载到内存中,目的是为了...
什么是内存磁盘初始化?深入分析initrd,测试自定义的初始化RAM盘...
Initrd,全称为“boot loader initialized RAM disk”,即由引导加载器初始化的内存磁盘。它是一个临时的文件系统,用于在内核启动时提供必要的驱动程序和初始化脚本,以便在访问实际的根文件系统之前完成一些必要的...
Initrd全称“bootloader-initialized RAM disk”,是由bootloader初始化的内存磁盘。在Linux内核启动前,bootloader将存储介质中的Initrd文件加载到内存中。内核启动时,会首先访问位于内存中的Initrd文件系统,执行...
Initrd(Initial RAM Disk)是Linux启动过程中的一个临时文件系统,用于在主根文件系统可用之前提供一个临时的环境。它的主要目的是加载必要的驱动程序和支持文件,以便能够挂载实际的根文件系统。Initrd是一个压缩...
在此过程中,initrd(initial ram disk)和initramfs(initial ram filesystem)是两个经常被提及的概念,它们在Linux系统启动过程中扮演着关键的角色。 Linux启动流程可以分为几个主要的步骤,包括BIOS或UEFI的...
Linux内存磁盘初始化技术,通常被称为initrd(Initial RAM Disk),是Linux内核启动过程中的一个重要环节,尤其在处理复杂的系统引导和嵌入式环境时。initrd是一种临时的根文件系统,它在系统启动的早期阶段被挂载,...
系统启动时,内核需要知道可用的内存大小、控制台设备以及initrd(初始RAM磁盘)的位置。这些信息通常在平台的defaultConfig配置中指定。但在某些情况下,系统实际使用的配置可能与defaultConfig中配置的不符。要...
5. **加载内核和初始化RAM磁盘(initrd.img):** 当用户选择了某个操作系统后,GRUB会加载该操作系统的内核文件(`vmlinuz`)以及初始化RAM磁盘(`initrd.img`)。 6. **传递控制给内核:** 内核被加载完毕后,GRUB将控制...
3. **文件系统初始化**:包括根文件系统(rootfs)和初始RAM磁盘(initrd)的处理。 4. **启动init进程**:启动第一个用户空间进程。 #### 六、根文件系统(rootfs)与初始RAM磁盘(initrd) - **根文件系统(rootfs)**:是...
3. **创建初始化RAM磁盘**:为了启动Linux内核,还需要一个初始RAM磁盘(initrd)。可以使用`mkfs.cramfs`工具创建一个包含基本文件系统的映像,比如`/etc`、`/bin`等目录。 4. **配置Bochs**:编辑Bochs的配置文件...
内核映像通常包括内核代码、初始RAM磁盘(initrd)和模块。 四、初始化内核 内核开始执行,进行硬件检测和初始化,包括CPU、内存、磁盘控制器等。同时,它会加载必要的驱动程序。内核也会设置页表,初始化调度器,...
`vmlinuz`是Linux内核的压缩版本,而`initrd.img`则是一个初始RAM磁盘,包含了启动时所需的基本文件系统和模块,用于支持特定硬件的初始化。 `Autoboot.bat`脚本通常会包含调用`loadlin.exe`的命令,同时传递内核和...
Initrd,全称Bootloader Initialized RAM Disk,是在系统启动过程中由Bootloader初始化的内存磁盘。它的主要作用是在内核访问真正的根文件系统之前提供一个临时的文件系统,以便加载驱动程序和执行其他启动任务。...
`initrd.gz`就是这个初始化RAM磁盘的压缩版本,通常在引导加载器(如GRUB)中被解压并加载。 `vmlinuz` 是Linux内核本身的一个压缩版本。它包含了运行系统所需的基本代码和数据,用于处理硬件初始化、加载驱动程序...
`initrd`全称Initial RAM Disk,是一个临时的RAM磁盘,它在Linux内核启动时被加载到内存中。它的主要目的是为系统的进一步初始化提供一个环境,尤其是当系统的根文件系统不可直接由BIOS或启动加载器访问时。例如,在...