定制自己的Linux时,Ramdisk、Initrd及根系统的制作

Linux 中的 Ramdisk Initrd

Ramdisk 简介

 

先简单介绍一下 ramdisk Ramdisk 是虚拟于 RAM 中的盘 (Disk) 。对于用户来说,可以把 RAM disk 与通常的硬盘分区(如 /dev/hda1 )同等对待来使用,例如:

redice # mkfs.ext2 /dev/ram0mke2fs 1.38 (30-Jun-2005)Filesystem label=OS type: LinuxBlock size=1024(log=0)Fragment size=1024 (log=0)2048 inodes, 8192 blocks409 blocks (4.99%) reserved for the super user First data block=11 block group8192 blocks per group, 8192 fragments per group2048 inodes per group. Writing inode tables: doneWriting superblocks and filesystem accounting information: done. This filesystem will be automatically checked every 24 mounts or180 days, whichever comes first. Use tune2fs -c or -i to override.

redice # mount /dev/ram0 /mnt/rdredice # ls /mnt/rdlost+foundredice

# mount/dev/hda2 on / type ext3proc on /proc type proc (rw)/dev/ram0 on /tmp/xxx type ext2 (rw)

 

当然, Ramdisk 与硬盘分区有其不同的地方,例如 RAM disk 不适合作为长期保存文件的介质,掉电后 Ramdisk 的内容会随内存内容的消失而消失。 Ramdisk 的其中一个优势是它的读写速度高,可以被用作需要高速读写的文件。但在 2.6 版本后, Ramdisk 的这一作用开始被 tmpfs(Virtual memory file system support) 取代。

回到上面的例子,我们格式化了一个 ramdisk(/dev/ram0) 并且将其 mount /mnt/rd 目录下,那么这个 Ramdisk 有多大呢?先看一下:

redice # df -h /dev/ram2Filesystem

容量    已用 可用 已用 % 挂载点 /dev/ram0

7.8M   1.0K   7.4M    1% /mnt/rd

 

从上面的信息看出, ramdisk 有大约 7.8M 的可用空间。我们再试一下另外的文件系统,重新格式化成 minix 分区并挂接试一下:

redice # umount /mnt/rdredice # mkfs.minix /dev/ram02752 inodes8192

blocksFirstdatazone=90 (90)

Zonesize=1024Maxsize=268966912

redice # mount /dev/ram0 /mnt/rdredice # df -h /dev/ram0Filesystem

容量    已用 可用 已用 % 挂载点 /dev/ram0

  8.0M   1.0K   8.0M    1% /mnt/rd

 

现在看出来了,的确是 8M (这同时说明, EXT2 文件系统本身要占用一定的存储空间,相比之下 minix 文件系统要少些),这个空间是在编译核心时就确定下来了,在配置 Ramdisk 时,有一个叫 Default RAM disk size 的参数决定默认情况下 Ramdisk 的大小。可以通过核心命令行参数 (ramdisk_size) 来改变这个值,例如要设置 Ramdisk 的大小为 16M ,在 grub 中可以用:

# grub.conf -default=0timeout=10splashimage=(hd0,0)/grub/splash.xpm.gztitle

Redice Linuxroot (hd0,0)

kernel /vmlinuz ro root=LABEL=/ hdc=ide-scsi ramdisk_size=16384

initrd /initrd

 

这样, Ramdisk 的大小就变成 16M 了。这个参数是 Ramdisk 直接编译到核心时才能使用的,如果 Ramdisk 编译为模块,则应该使用模块参数来设置 Ramdisk 的大小 :

redice # insmod rd rd_size=16384

 

  编译到核心时,可以通过下面的一些核心命令行参数来配置 Ramdisk:

ramdisk_size - ramdisk 的大小 (Kbytes)

ramdisk - ramdisk_size 的作用相同;

ramdisk_blocksize - ramdisk 的块大小,默认情况为 1024

 

当以模块的形式译时,模块支持以下几个加载参数:

rd_size - 同上面的 ramdisk_size ramdisk 参数;

rd_blocksize - 同上面的 ramdisk_blocksize

 

 

initrd 概述

上面已经提到, Ramdisk 需要先格式化然后理能使用。那么,如果核心希望使用 ramdisk 该如何做呢?于是 initrd 产生了, initrd 全称是 initial RAM disk ,它提供一种让核心可以简单使用 Ramdisk 的能力,简单的说,这些能力包括:

格式化一个 Ramdisk

加载文件系统内容到 Ramdisk

Ramdisk 作为根文件系统;

我们可以将 initrd 形像的比作 Norton Ghost

份的硬盘分区,而 Linux 启动阶段的 Ramdisk 相当于一个未格式化的硬盘分区,核心可以直接将 initrd 的内容释放到一个未初始化的

Ramdisk 里,这个过程与 Ghost 恢复一个分区的过程十分相似。于是,相应的内容被加载到相应的 Ramdisk 中,同时,这个 Ramdisk 也被格

式化成某种由 initrd 格式所表达的分区格式。

 

initrd Ghost 备份的分区有许多相似之处,例如,它有一定的大小,包含分区上的文件系统格式等。 initrd 支持的格式包括:

Ext2 文件系统;

Romfs 文件系统;

cramfs 文件系统;

minix 文件系统;

 

如果核心选择了 Gzip 支持(通常这是默认的,在 init/do_mounts_rd.c 中定义的 BUILD_CRAMDISK 宏)还可以使用 Gzip 压缩的 initrd 。相关的代码可以在核心源码 drivers/block/rd.c:identify_ramdisk_image 中找到。

1) 传统方法制作 initrd

制作 initrd 传统的作法是通过软盘(显然过时了,不介绍了)、 ramdisk loop 设备( /dev/loop) 。通过 ramdisk 来制作的方法比较简单 ( ext2 文件系统为例 )

redice # mkfs.ext2 /dev/ram0redice

# mount /dev/ram0 /mnt/rdredice

# cp _what_you_like_   /mnt/rd

# 把需要的文件复制过去 redice # dd if=/dev/ram0 of=/tmp/initrdredice

# gzip -9 /tmp/initrd

 

这个过程也最能够解释 initrd 的本质,对于 Linux 来说, Ramdisk 的一个块设备,而

initrd 是这个块设备上所有内容的“克隆”(由命令 dd 来完成)而生成的文件。核心中加载 initrd 相关的代码则用于完成将相反的过程,即将这一个文件恢复到 Ramdisk 中去。

 

2) 通过 loop 设备来制作 initrd 的过程:

redice # dd if=/dev/zero of=/tmp/initrd bs=1024 count=4096 # 制作一个 4M 的空白文件

redice # losetup /dev/loop0 /tmp/initrd

# 映射到 loop 设备上; redice # mkfs.ext2 /dev/loop0

# 创建文件系统; redice # mount /dev/loop0 /mnt/rdredice # cp _what_you_like_ /mnt/rd

# 复制需要的文件; redice # umount /mnt/rdredice # losetup -d /dev/loop0redice

# gzip -9 /tmp/initrd

不过,现在已经有了一些更好的工具来完成这些工作,包括 genromfs uClinux 里常用的工具), genext2fs mkcramfs 等。这些工具提供了一些方便开发的新特性,例如,不需要上面烦索的过程,只要将文件复制到某个目录中,将其作为根目录,即可生成 initrd ;另一个重要的改进是,这些工具都可以以普通用户的身份来生成 initrd

 

文档

l   Linux 文档中关于 ramdisk 的介绍,核心目录里 Documentation/ramdisk.txt;

l   Linux 文档中关于 initrd 的介绍,核心目录 Documentation/initrd.txt;

l   Linux 文档中关于 tmpfs 的介绍,核心目录 Documentation/filesystems/tmpfs.txt;

l   How to use a Ramdisk for Linux

资源

l   genromfs - genromfs is a space-efficient, small, read-only filesystem originally for Linux and used by some Linux based projects.

l   genext2fs - Simply, it generates an ext2 filesystem as a normal (i.e. non-root) user. It doesn't require you to mount the image file to copy files on it. It doesn't even require you to be the superuser to make device nodes or set group/user ids.

l   cramfs - cramfs is a Linux filesystem designed to be simple, small, and to compress things well. It is used on a number of embedded systems and small devices.

 

使用 ram disk 初始化( initrd

 

initrd 提供了在 boot loader 下加载 ram disk 的方法。该 ram disk 可以被作为根文件系统挂载进来,里面的程序也可以运行。然后,新的根文件系统可以从其他设备挂载。之前的根(来自 initrd )可以被转移到一个目录然后被卸载。

initrd 主要设计用来使系统启动于两个条件,一个是内核来自于非常小的驱动器,一个是额外的模块需要从 initrd 中加载。

本文给出 initrd 的概要描述,更具体的可以参考 [1]

操作

当使用 initrd, 典型的系统启动顺序如下:

1. boot loader 加载内核并初始化 ram disk

2. 内核把 initrd 转化成正常的 ram disk 并释放 initrd 使用的内存

3. initrd 作为 root 被挂载,赋予读写权限。

4. /linuxrc 被执行(这可以是任何可执行文件,如脚本,运行在 uid 0, 可以做任何初始化)。

5. linuxrc 挂载真正的根文件系统

6. linuxrc 使用 pivot_root 系统调用把根文件系统放在根目录。

7. 正常的启动序列( /sbin/init )在根文件系统上执行。

8. initrd 文件系统被移去。

注意,改变根目录不牵扯卸载它。挂载在 initrd 的文件系统仍然可以被访问。

 

启动命令选项, initrd 添加了如下新的选项:

initrd=<path> (e.g. LOADLIN) :加载特定的文件作为初始的 ram disk 。当使用 lilo, 你应该在 /etc/lilo.conf 中指定 ram disk 镜像文件位置,使用 INITRD 变量。

noinitrd initrd 数据被保留却不转化成 ram disk ,正常的根文件系统被加载。 initrd 的数据可以 /dev/initrd 中读取。注意,在 initrd 中的数据可以是任意结构的,不一定要是文件系统镜像。该选项多用于调试。

注意: /dev/initrd 是只读的,而且只能被使用一次。只要最后一个进程关闭它,所有的数据将会释放掉,而 /dev/initrd 将不再被打开。

root=/dev/ram0 (without devfs)

root=/dev/rd/0 (with devfs)

initrd 作为根文件系统被挂载,随后正常的启动顺序中, ram disk 仍然作为 root

 

安装

首先,用于 initrd 文件系统的目录在根文件系统上被创建:

mkdir /initrd

名字不是很重要。更多的可以参考 pivot_root(2) man

在启动过程中,如果根文件系统被创建(如果你有软盘启动),那么根文件系统应该创建 /initrd 目录。

如果 initrd 没有被挂载,他里面的内容仍然可以被访问,通过以下的方法(注意,这在使用 devfs 时不可用):

# mknod /dev/initrd b 1 250

# chmod 400 /dev/initrd

 

第二步,内核编译的时候必须支持 ram disk 并启动 ram disk 使能。所有从 initrd 中执行的程序必须被编译到内核中。

第三步,你必须创建 ram disk 镜像。这可以通过在块设备上创建文件系统实现,把所需的文件拷贝,然后把文件拷贝到 initrd 文件。根据最新的内核,至少三种设备可以用:

ü   软盘(慢,却随处可用)

ü   ram disk (快,需要物理内存)

ü   loopback device (最高档)

我将介绍 loopback device 方法:(保证 loopback device 被内核支持)

1 )创建一个空的文件系统,拥有合适的大小:

# dd if=/dev/zero of=initrd bs=300k count=1

# mke2fs -F -m0 initrd

如果空间不多,你可以用 minix fs 而不是 ext2

2 )挂载文件系统

# mount -t ext2 -o loop initrd /mnt

3 )创建控制台设备(如果用 devfs ,就没必要)

# mkdir /mnt/dev

# mknod /mnt/dev/console c 5 1

拷贝 initrd 环境需要的所有文件。不要忘掉最重要的文件 /linuxrc 。注意, /linuxrc 必须有执行权限。

正确的 initrd 环境可以不用重启就可以通过命令行测试:

# chroot /mnt /linuxrc

4 )卸载文件系统

# umount /mnt

5 )现在 initrd 被保存在 initrd 文件中。还可以压缩他。

# gzip -9 initrd

为了试验 initrd ,你可以创建一个急救盘,只需创建 /linuxrc /bin/sh 的符号链接。

你还可以试着把 initrd 更小。

6 )最后,你要启动内核并加载 initrd. 几乎所有的 boot loader 都支持 initrd.

虽然启动进程兼容老的机制,下面的启动命令行还是得给出:

root=/dev/ram0 init=/linuxrc rw

如果不使用 devfs. 或者

root=/dev/rd/0 init=/linuxrc rw

如果使用 devfs. 使用 LOADLIN, 只要执行

LOADLIN <kernel> initrd=<disk_image>

e.g. LOADLIN C:/LINUX/BZIMAGE initrd=C:/LINUX/INITRD.GZ root=/dev/ram0

init=/linuxrc rw

使用 LILO ,你添加选项 INITRD=<path> /etc/lilo.conf 中,使用 APPEDN 命令即可。

image = /bzImage

initrd = /boot/initrd.gz

append = "root=/dev/ram0 init=/linuxrc rw"

然后运行 /sbin/lilo

对于其他的启动程序,请参考相应的文档。现在你可以启动并享受使用 initrd 的乐趣了。

(感谢作者)

你可能感兴趣的:(定制自己的Linux时,Ramdisk、Initrd及根系统的制作)