Linux 初始 RAM 磁盘(initrd)概述
发布日期:2011-05-06
Linux 初始 RAM 磁盘(initrd)概述
学习 initrd 的阐发、创建以及在 Linux 引导进程中的用法
Linux初始 RAM 磁盘(initrd)是在体系引导进程中挂载的一个临时根文件体系,用来支持两阶段的引导进程。initrd 文件中包括了种种可实行步调和驱动步调,它们可以用来挂载实际的根文件体系,然后再将这个 initrd RAM 磁盘卸载,并开释内存。在很多嵌入式 Linux 体系中,initrd 便是终极的根文件体系。本文将探索 Linux 2.6 的初始 RAM 磁盘,包括怎样创建以及如安在 Linux 内核中利用。
什么是初始 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 磁盘里的非长期性根文件体系来说也非常有效。
参考数据
学习
您可以参阅本文在 developerWorks 环球站点上的 英文原文 。
“Linux 引导进程黑幕”(developerWorks,2006 年 5 月)探索了 Linux 从最初的 bootstrap 到启动第一个用户空间应用步调的进程。
在 “从 FireWire 配置引导 Linux”(developerWorks,2004 年 7 月)中,我们可以学习在种种平台的种种配置上怎样(利用 initrd)启动 Linux。
cpio 文件格局 既大抵又简便。因此 Fedora 团队选择利用它作为 initrd 的格局就没什么稀罕的了。
mkinitrd 东西非常得当创建 initrd 映像文件。除了创建 initrd 映像之外,它还可以确定要为您的体系加载哪些模块,并将这些模块也全部参加到这个映像中。
loop 配置 是一个非常有效的驱动步调,可以将映像文件作为文件体系挂载。
Network Boot and Exotic Root HOWTO 不但先容了从网络上引导 Linux 的进程,还先容了诸如软盘引导、CD-ROM 引导和嵌入式环境中的内容。
在 developerWorks Linux 专区 中可以找到为 Linux 开辟职员准备的更多资源。
随时存眷 developerWorks 技能变乱和网络广播。
得到产品和技能
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 上直接下载。
讨论
通过参加 developerWorks blogs 参加 developerWorks 社区。
关于作者
Tim Jones 是一名嵌入式软件工程师,他是 GNU/Linux Application Programming、AI Application Programming 以及 BSD Sockets Programming from a Multilanguage Perspective 等书的作者。他的工程背景非常广泛,从同步宇宙飞船的内核开辟到嵌入式架构筹划,再到网络协议的开辟。Tim 是 Emulex Corp. 的一名资深软件工程师。