Linux初始RAM磁盘Initrd概述.docx

上传人:b****5 文档编号:8161735 上传时间:2023-01-29 格式:DOCX 页数:17 大小:32.40KB
下载 相关 举报
Linux初始RAM磁盘Initrd概述.docx_第1页
第1页 / 共17页
Linux初始RAM磁盘Initrd概述.docx_第2页
第2页 / 共17页
Linux初始RAM磁盘Initrd概述.docx_第3页
第3页 / 共17页
Linux初始RAM磁盘Initrd概述.docx_第4页
第4页 / 共17页
Linux初始RAM磁盘Initrd概述.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

Linux初始RAM磁盘Initrd概述.docx

《Linux初始RAM磁盘Initrd概述.docx》由会员分享,可在线阅读,更多相关《Linux初始RAM磁盘Initrd概述.docx(17页珍藏版)》请在冰豆网上搜索。

Linux初始RAM磁盘Initrd概述.docx

Linux初始RAM磁盘Initrd概述

Linux初始RAM磁盘(initrd)概述

学习initrd的剖析、创建以及在Linux引导过程中的用法

级别:

中级

M.TimJones(mtj@),顾问工程师,Emulex

2006年8月21日

Linux®初始RAM磁盘(initrd)是在系统引导过程中挂载的一个临时根文件系统,用来支持两阶段的引导过程。

initrd文件中包含了各种可执行程序和驱动程序,它们可以用来挂载实际的根文件系统,然后再将这个initrdRAM磁盘卸载,并释放内存。

在很多嵌入式Linux系统中,initrd就是最终的根文件系统。

本文将探索Linux2.6的初始RAM磁盘,包括如何创建以及如何在Linux内核中使用。

什么是初始RAM磁盘?

初始RAM磁盘(initrd)是在实际根文件系统可用之前挂载到系统中的一个初始根文件系统。

initrd与内核绑定在一起,并作为内核引导过程的一部分进行加载。

内核然后会将这个initrd文件作为其两阶段引导过程的一部分来加载模块,这样才能稍后使用真正的文件系统,并挂载实际的根文件系统。

initrd中包含了实现这个目标所需要的目录和可执行程序的最小集合,例如将内核模块加载到内核中所使用的insmod工具。

在桌面或服务器Linux系统中,initrd是一个临时的文件系统。

其生存周期很短,只会用作到真实文件系统的一个桥梁。

在没有存储设备的嵌入式系统中,initrd是永久的根文件系统。

本文将对这两种情况进行探索。

initrd剖析

initrd映像中包含了支持Linux系统两阶段引导过程所需要的必要可执行程序和系统文件。

根据我们运行的Linux的版本不同,创建初始RAM磁盘的方法也可能会有所不同。

在FedoraCore3之前,initrd是使用loop设备来构建的。

loop设备是一个设备驱动程序,利用它可以将文件作为一个块设备挂载到系统中,然后就可以查看这个文件系统中的内容了。

在您的内核中可能并没有loop设备,不过这可以通过内核配置工具(makemenuconfig)选择DeviceDrivers>BlockDevices>LoopbackDeviceSupport来启用。

我们可以按照下面的方法来查看loop设备的内容(initrd文件的名字可能会稍有不同):

清单1.查看initrd的内容(适用于FC3之前的版本)

#mkdirtemp;cdtemp

#cp/boot/initrd.img.gz.

#gunzipinitrd.img.gz

#mount-text-oloopinitrd.img/mnt/initrd

#ls-la/mnt/initrd

#

现在我们就可以查看/mnt/initrd子目录中的内容了,这就代表了initrd文件的内容。

注意,即使您的initrd映像文件不是以.gz结尾,它也可能是一个压缩文件,您可以给这个文件添加上.gz后缀,然后再使用gunzip对其进行解压。

从FedoraCore3开始,默认的initrd映像变成了一个经过压缩的cpio归档文件。

我们不用再使用loop设备来将initrd作为压缩映像进行挂载,而是可以将其作为cpio归档文件来使用。

要查看cpio归档文件的内容,可以使用下面的命令:

清单2.查看initrd的内容(适用于FC3及其以后的版本)

#mkdirtemp;cdtemp

#cp/boot/initrd-2.6.14.2.imginitrd-2.6.14.2.img.gz

#gunzipinitrd-2.6.14.2.img.gz

#cpio-i--make-directories

#

结果会生成一个很小的根文件系统,如清单3所示。

在./bin目录中有一组很少但却非常必要的应用程序,包括nash(即notashell,是一个脚本解释器)、insmod(用来加载内核模块)和lvm(逻辑卷管理工具)。

清单3.默认的Linuxinitrd目录结构

#ls-la

#

drwxr-xr-x10rootroot4096May702:

48.

drwxr-x---15rootroot4096May700:

54..

drwxr-xr-x2rootroot4096May702:

48bin

drwxr-xr-x2rootroot4096May702:

48dev

drwxr-xr-x4rootroot4096May702:

48etc

-rwxr-xr-x1rootroot812May702:

48init

-rw-r--r--1rootroot1723392May702:

45initrd-2.6.14.2.img

drwxr-xr-x2rootroot4096May702:

48lib

drwxr-xr-x2rootroot4096May702:

48loopfs

drwxr-xr-x2rootroot4096May702:

48proc

lrwxrwxrwx1rootroot3May702:

48sbin->bin

drwxr-xr-x2rootroot4096May702:

48sys

drwxr-xr-x2rootroot4096May702:

48sysroot

#

清单3中比较有趣的是init文件就在根目录中。

与传统的Linux引导过程类似,这个文件也是在将initrd映像解压到RAM磁盘中时被调用的。

在本文稍后我们将来探索这个问题。

创建initrd所使用的工具

下面让我们回到最开始,来看一下initrd映像最初是如何构建的。

对于传统的Linux系统来说,initrd映像是在Linux构建过程中创建的。

有很多工具,例如mkinitrd,都可以用来使用必要的库和模块自动构建initrd,从而用作与真实的根文件系统之间的桥梁。

mkinitrd工具实际上就是一个shell脚本,因此我们可以看到它究竟是如何来实现这个结果的。

还有一个YAIRD(即YetAnotherMkinitrd)工具,可以对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

#RamdiskConstants

RDSIZE=4000

BLKSIZE=1024

#Createanemptyramdiskimage

ddif=/dev/zeroof=/tmp/ramdisk.imgbs=$BLKSIZEcount=$RDSIZE

#Makeitanext2mountablefilesystem

/sbin/mke2fs-F-m0-b$BLKSIZE/tmp/ramdisk.img$RDSIZE

#Mountitsothatwecanpopulate

mount/tmp/ramdisk.img/mnt/initrd-text2-oloop=/dev/loop0

#Populatethefilesystem(subdirectories)

mkdir/mnt/initrd/bin

mkdir/mnt/initrd/sys

mkdir/mnt/initrd/dev

mkdir/mnt/initrd/proc

#Grabbusyboxandcreatethesymboliclinks

pushd/mnt/initrd/bin

cp/usr/local/src/busybox-1.1.1/busybox.

ln-sbusyboxash

ln-sbusyboxmount

ln-sbusyboxecho

ln-sbusyboxls

ln-sbusyboxcat

ln-sbusyboxps

ln-sbusyboxdmesg

ln-sbusyboxsysctl

popd

#Grabthenecessarydevfiles

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

#Equatesbinwithbin

pushd/mnt/initrd

ln-sbinsbin

popd

#Createtheinitfile

cat>>/mnt/initrd/linuxrc<

#!

/bin/ash

echo

echo"Simpleinitrdisactive"

echo

mount-tproc/proc/proc

mount-tsysfsnone/sys

/bin/ash--login

EOF

chmod+x/mnt/initrd/linuxrc

#Finishup...

umount/mnt/initrd

gzip-9/tmp/ramdisk.img

cp/tmp/ramdisk.img.gz/boot/ramdisk.img.gz

为了创建initrd,我们最开始创建了一个空文件,这使用了/dev/zero(一个由零组成的码流)作为输入,并将其写入到ramdisk.img文件中。

所生成的文件大小是4MB(4000个1K大小的块)。

然后使用mke2fs命令在这个空文件上创建了一个ext2(即secondextended)文件系统。

现在这个文件变成了一个ext2格式的文件系统,我们使用loop设备将这个文件挂载到/mnt/initrd上了。

在这个挂载点上,我们现在就有了一个目录,它以ext2文件系统的形式呈现出来,我们可以对自己的initrd文件进行拼装了。

接下来的脚本提供了这种功能。

下一个步骤是创建构成根文件系统所需要的子目录:

/bin、/sys、/dev和/proc。

这里只列出了所需要的目录(例如没有库),但是其中包含了很多功能。

为了可以使用根文件系统,我们使用了BusyBox。

这个工具是一个单一映像,其中包含了很多在Linux系统上通常可以找到的工具(例如ash、awk、sed、insmod等)。

BusyBox的优点是它将很多工具打包成一个文件,同时还可以共享它们的通用元素,这样可以极大地减少映像文件的大小。

这对于嵌入式系统来说非常理想。

将BusyBox映像从自己的源目录中拷贝到自己根目录下的/bin目录中。

然后创建了很多符号链接,它们都指向BusyBox工具。

BusyBox会判断所调用的是哪个工具,并执行这个工具的功能。

我们在这个目录中创建了几个链接来支持init脚本(每个命令都是一个指向BusyBox的链接。

下一个步骤是创建几个特殊的设备文件。

我从自己当前的/dev子目录中直接拷贝了这些文件,这使用了-a选项(归档)来保留它们的属性。

倒数第二个步骤是生成linuxrc文件。

在内核挂载RAM磁盘之后,它会查找init文件来执行。

如果没有找到init文件,内核就会调用linuxrc文件作为自己的启动脚本。

我们在这个文件中实现对环境的基本设置,例如挂载/proc文件系统。

除了/proc之外,我还挂载了/sys文件系统,并向终端打印一条消息。

最后,我们调用了ash(一个BourneShell的克隆),这样就可以与根文件系统进行交互了。

linuxrc文件然后使用chmod命令修改成可执行的。

最后,我们的根文件系统就完成了。

我们将其卸载掉,然后使用gzip对其进行压缩。

所生成的文件(ramdisk.img.gz)被拷贝到/boot子目录中,这样就可以通过GNUGRUB对其进行加载了。

要构建初始RAM磁盘,我们可以简单地调用mkird,这样就会自动创建这个映像文件,并将其拷贝到/boot目录中。

测试定制的初始RAM磁盘

新的initrd映像现在已经在/boot目录中了,因此下一个步骤是使用默认的内核来对其进行测试。

现在我们可以重新启动Linux系统了。

在出现GRUB界面时,按C键启动GRUB中的命令行工具。

我们现在可以与GRUB进行交互,从而定义要加载哪个内核和initrd映像文件。

kernel命令让我们可以指定内核文件,initrd命令可以用来指定initrd映像文件。

在定义好这些参数之后,就可以使用boot命令来引导内核了,如清单5所示。

清单5.使用GRUB手工引导内核和initrd

GNUGRUBversion0.95(638Klower/97216Kuppermemory)

[MinimalBASH-likelineeditingissupported.Forthefirstword,TAB

listspossiblecommandcompletions.AnywhereelseTABliststhepossible

completionsofadevice/filename.ESCatanytimeexits.]

grub>kernel/bzImage-2.6.1

[Linux-bzImage,setup=0x1400,size=0x29672e]

grub>initrd/ramdisk.img.gz

[Linux-initrd@0x5f2a000,0xb5108bytes]

grub>boot

UncompressingLinux...OK,bootingthekernel.

在内核启动之后,它会检查是否有initrd映像文件可用(稍后会更详细介绍),然后将其加载,并将其挂载成根文件系统。

在清单6中我们可以看到这个Linux启动过程最后的样子。

在启动之后,ashshell就可以用来输入命令了。

在这个例子中,我们将浏览一下根文件系统的内容,并查看一下虚拟proc文件系统中的内容。

我们还展示了如何通过touch命令在文件系统中创建文件。

注意所创建的第一个进程是linuxrc(通常都是init)。

清单6.使用简单的initrd引导Linux内核

...

md:

AutodetectingRAIDarrays

md:

autorun

md:

...autorunDONE.

RAMDISK:

Compressedimagefoundatblock0

VFS:

Mountedroot(ext2filesystem).

Freeingunusedkernelmemory:

208kfreed

/$ls

binetclinuxrcprocsys

devliblost+foundsbin

/$cat/proc/1/cmdline

/bin/ash/linuxrc

/$cdbin

/bin$ls

ashcatechomountsysctl

busyboxdmesglsps

/bin$touchzfile

/bin$ls

ashcatechomountsysctl

busyboxdmesglspszfile

使用初始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:

Mountedroot(ext2filesystem).的地方。

最后,返回到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(DynamicHostConfigurationProtocol)可以用来确定网络参数,例如IP地址和子网掩码。

TFTP(TrivialFileTransferProtocol)可以用来将内核映像和初始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团队选择

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 表格模板 > 合同协议

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1