http://blog.chinaunix.net/uid-29616823-id-4405454.html
initramfs概述
initramfs与initrd类似,也是初始化好了且存在于ram中的,可以压缩也可以不压缩。但是目前initramfs只支持cpio包格式,它会被populate_rootfs()->unpack_to_rootfs(&_initramfs start, &__initramfs_end - &__initramfs_start, 0)函数(解压缩、)解析、安装。

initramfs与initrd的区别
1)Linux内核只认cpio格式的initramfs文件包(因为unpack_to_rootfs只能解析cpio格式文件),非cpio格式的initramfs文件包将被系统抛弃,而initrd可以是cpio包也可以是传统的印象(image)文件,实际使用中initrd都是传统印象文件。

2)initramfs在编译内核的同时被编译并与内核连接成一个文件,它被链接到地址__initramfs_star处,与内核同时被bootloader加载到ram中,而initrd是另外单独编译生成的,是一个独立的文件,它由bootloader单独加载到ram中内核空间外的地址,比如加载的地址为addr(是物理地址而非虚拟地址),大小为8MB,那么只要在命令行中加入“initrd=addr,8M”命令系统就可以找到initrd(当然通过适当修改Linux的目录架构、makefile文件和相关代码,以上两种情况都是可以相通的)。

3)initramfs被解析处理后其原始的cpio包(压缩或非压缩)所占的空间(&__initramfs_start&__initramfs_end)是作为系统的一部分一直保留在系统中,不会被释放掉,而对于initrd的印象(image)文件,如果没有在命令行中设置“keepinitrd”命令,即keep_initrd全局变量等于0,那么initrd印象文件被处理后其原始文件(压缩或非压缩)所占的空间(initrd_startinitrd_end)将被释放掉。

4)initramfs可以独立于ram disk单独存在,而要支持initrd必须首先支持ramdisk,即要配置CONFIG_BLK_DEV_INITRD选项首先必须配置CONFIG_BLK_DEV_RAM,因为initrd image实际就是初始化好了的ram disk印象文件,最后都要解析、写入到ram disk设备/dev/ram或/dev/ram0中。

initramfs相关全局变量
__initramfs_start

char数据类型,&__initramfs_start就是initramfs的cpio包起始虚拟地址。

__initramfs_end

char数据类型,&__initramfs_end就是initramfs的cpio包结束虚拟地址。

initramfs被编译链接的位置
initramfs被链接到&__initramfs_start虚拟地址处,属于.init.ramfs区,&__initramfs_start和&__initramfs_end在链接文件arch/arm/kernel/vmlinux.lds.S中定义如下:

__initramfs_start = .;

usr/built-in.o(.init.ramfs);所以initramfs文件实际就是usr目录下的built-in.o文件

__initramfs_end = .;

.init.ramfs在Linux2.6.10/usr/initramfs_data.S中定义,编译内核时会先编译Linux2.6.10\usr\gen_init_cpio.c生成应用程序gen_init_cpio;然后用gen_init_cpio生成Linux2.6.10\usr\ initramfs_data.cpio(.gz),它就是保存在&__initramfs_start与&__initramfs_end之间的.init.ramfs;可以通过修改Linux2.6.10\usr\Makefile文件将来使得编译链接时是否压缩initramfs。

initramfs文件的生成过程
1)cpio包格式二进制initramfs文件是由gen_init_cpio命令生成的,gen_init_cpio应用程序是由Linux2.6.10/gen_init_cpio.c文件编译而来,gen_init_cpio命令格式和用法说明如下。
gen_init_cpio命令格式:

#./gen_init_cpio
Usage:
./gen_init_cpio <cpio_list>
<cpio_list>文件格式:

#a comment ;注释行;

file ;描述initramfs中的一个文件

dir ;描述initramfs中的一个目录

nod <dev_type> ;描述initramfs中的一个节点

<cpio_list>文件每一行各字段说明:

:该文件/目录/节点在initramfs中的名字,包含绝对路径

:该文件/目录/节点在当前开发主机上的位置

:该文件/目录/节点的模式字

:该文件/目录/节点的用户id,0表示root用户

:该文件/目录/节点的组id,0表示root组

<dev_type> :该设备节点对应设备的类型,b表示块设备,c表示字符设备

:设备节点对应设备的主设备号

:设备节点对应设备的次设备号

<cpio_list>文件示例:

#Last modified: 1206050528
dir /dev 0755 0 0

nod /dev/console 0600 0 0 c 5 1

dir /root 0700 0 0

dir //lost+found 700 0 0

dir //bin 755 0 0

file //bin/addgroup /home/whh/tmp/bin/addgroup 777 0 0

file //bin/adduser /home/whh/tmp/bin/adduser 777 0 0

2)<cpio_list>文件生成方式
方法一:根据上述<cpio_list>文件格式说明自行编写。

方法二:写一个脚本文件一次性为指定目录下的每个文件/目录/节点生成一行描述符,并将它们全部写入一个指定的<cpio_list>文件中。在Linux2.6.10原始目录下的scripts\gen_initramfs_list.sh就是这样的脚本文件。它的使用方法如下:

#./gen_initramfs_list.sh <cpio_list文件>或<目录> > <my_cpio_list>

将把生成的所有文件、目录、节点的描述行写入文件my_cpio_list中。如果命令行中没有指定<cpio_list文件>和<目录>,那么gen_initramfs_list.sh会自行生成一个最简单的initramfs_list文件,如下所示:

dir /dev 0755 0 0

nod /dev/console 0600 0 0 c 5 1

dir /root 0700 0 0

3)配置并编译initramf
将要作为initramfs的目录放在某个目录下,如“/home/my_root/”目录。或者将已经写好的<cpio_list>文件放在某个目录下,如
“/home/my_list”。

将内核配置文件myconfig中的

CONFIG_INITRAMFS_SOURCE=""

改成

CONFIG_INITRAMFS_SOURCE="/home/my_root"

或者

CONFIG_INITRAMFS_SOURCE="/home/lyeffort-rootfs  /home/lyeffort-rootfs.txt"

CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_ROOT_UID=2
CONFIG_INITRAMFS_ROOT_GID=2
CONFIG_RD_GZIP=y

执行

#make myconfig
#make
或者将上面两步改成如下形式并执行:

#make menuconfig
在逐级弹出的
“Block devices”->“Source directory or cpio_list”
子菜单下输入:
/home/my_root
或者
/home/my_list
然后执行
#make

4)Linux2.6.10中cpio包格式二进制initramfs文件实际产生流程
Linux2.6.10中cpio包格式二进制initramfs文件实际产生过程是:第一步,gen_initramfs_list.s为指定目录“/home/my_root”或文件“/home/my_list”在Linux2.6.10/usr目录下生成真正的<cpio_list>文件initramfs_list;第二步,gen_init_cpio按照initramfs_list文件描述将对应的目录/home/my_root生成二进制cpio包initramfs_data.cpio;第三步,gzip将initramfs_data.cpio压缩成initramfs_data.cpio.gz;第四步,ld -m elf_i386 --format binary --oformat elf32-i386 -r -T initramfs_data.scr initramfs_data.cpio.gz -o initramfs_data.o生成initramfs_data.o;第五步,ld -m elf_i386 -r -o built-in.o initramfs_data.o最后生成built-in.o。

5)从arm中编译出的内核印象文件Image、编译连接文件vmlinux.lds.S、cpio格式initramfs二进制压缩文件initramfs_data.cpio.gz、编译连接符号文件System.map对照看,事实上链接到&__initramfs_start位置的就是initramfs_data.cpio.gz文件。

----------------------------------------------------------
initramfs工作过程简述

在2.6kernel启动时,它把rootfs作为它的第一个文件系统挂载(注意:这里的rootfs是真名!!!不是root filesystem的缩写)。rootfs是一个特殊的tmpfs,这个不能被删除或者是unmounted。很多使用2.6内核的系统通常都是挂载rootfs后什么都不做,然后启动另一个文件系统作为root filesystem。但是,这个不能掩盖rootfs存在的事实,你可以“cat /proc/mounts” 来查看,第一个挂载的肯定是rootfs。

rootfs被挂载后,kernel立马就解压了那个用gzip压缩的CPIO归档文件到rootfs。每个2.6的内核都会执行这一步,但是默认那个压缩档是空的,所以也就没有往rootfs内添加任何东西。接着kernel会尝试在rootfs去找寻/init,一旦找到init并执行,kernel也就完成了启动工作,然后便是刚刚执行的init程序接管了接下来的工作。如果kernel没法调用"/init"程序,可能就会回过头去,按照便准的做法去解析参数“root=”,试图找到另一个filesystem然后挂载它。

这里的使用initramfs是指,提供一个/init程序给rootfs使用,我们可以通过两种途径实现:使用编译进内核的cpio.gz档案,或者是一个独立的cpio.gz档案。以前的initrd就是编译一个独立的档案,很多使用initramfs的方式也是给它提供一个独立的档案。

开工啦!

为了不看起来那么乏味,我们尝试通过一个看的着的例子来展示这个过程。

唔,我们还是把“hello world”作为第一个要放到initramfs中去的程序。事实上,rootfs和其它的root filesystem并没有什么区别,如果你喜欢,你可以放/etc和/usr和/tmp和。。。然后还可以mount /proc 和/sysfs过去。但是这里我们只需要放/init过去。程序的最后我们使用sleeping而不是exiting,这主要是考虑如果PID 1的程序退出,kernel会panic,这会干扰我们的视线。

#include

int main(int argc, char *argv[])
{
printf(“Hello world\n”);
sleep(999999999);
}
然后呢,静态编译,然后我们就不用考虑拷贝需要的库过去了~

gcc -static hello.c -o hello
如果在命令行执行这个小程序,它会打印hello world,让后停在那里。你可以用ctrl-x让它退出。如果是initramfs执行这个程序,我们会看到在boot messages的最后,有个“hello world”被打印。

注意:如果是要放到你的开发板上去执行,记得使用你的交叉编译工具。打包的过程是和平台无关的,但是二进制文件需要用目标系统的compiler。

那么,我们该怎样把这个程序给kernel用内?好吧,有四种基本方法:第一种是把cpio.gz作为一个独立的包,然后告诉bootloader它在哪里;或者你可以用下面三种方法之一,把initramfs直接编译进kernel里去。

把cipo.gz作为独立的档案

很多人喜欢把它编译进内核里面去,如果你乐意,你也可以这么做。但是我们现在要用另一种方式。我们可以使能内核的initrd支持,然后用cpio.gz来代替ramdisk(initrd)。聪明的内核会为我们自动检测文件的类型,然后把我们的压缩包解压放进rootfs;它不是创建一个ram disk,这不会影响initramfs内存效率高这一优势。

因为external initramfs是在built-in initramfs之后执行的,所以如果两个档案内包含有同名的内容,独立档案会覆盖掉built-in填进去去的东西。这意味着,你不用修改kernel,就可以update或者是ucstomize你的rootfs而不用换掉你的内核。

另外一个好消息是,这样做你可以不用顾虑license的问题!你可以在rootfs里面运行non-GPL的程序,或者是给你的驱动提供non-GPL的firmware…额,编译进内核的话,算是内核的修改吧?制作自己的initramfs,只是算是使用,你不用公布你的源代码哦亲!

那么,怎么制作cpio.gz档案呢?一种方法是你用cpio和gzip命令自己来压缩。当然,你也可以用kernel build来做这个,如果你觉得不是那么麻烦的话。原意自己做的,只需要敲下面这些代码进去…

mkdir sub
cp hello sub/init
cd sub
find . | cpio -o -H newc | gzip > …/initramfs_data.cpio.gz
cd …
rm -rf sub
按照传统的使用initrd的方法,把上面生成的initramfs_data.cpio.gz放到该放的地方去(别问我要放哪里,我也还不知道),它就会在boot结束的地方为你打印一朵漂亮的“hello world”,然后等待一段时间并重启。

试试吧!

如果它没有工作,照例的你该查查initial ramdisk支持是不是有被选中,然后看看你的init 程序是不是静态链接的,再看看它是不是又执行权限,或者是名字是不是对的。你可以用下面的命令来解压任何的initramfs档案到当前文件夹:

zcat initramfs_data.cpio.gz | cpio -i -d -H newc --no-absolute-filenames

把initramfs编译到内核里面去

使用initramfs最简单的方式,莫过于用已经做好的cpio.gz把kernel里面那个空的给换掉。这是2.6 kernel天生支持的,所以,你不用做什么特殊的设置。

kernel的config option里面有一项CONFIG_INITRAMFS_SOURCE(I.E. General setup—>Initramfs source file(s) in menuconfig)。这个选项指向放着内核打包initramfs需要的所有文件。默认情况下,这个选项是留空的,所以内核编译出来之后initramfs也就是空的,也就是前面提到的rootfs什么都不做的情形。

CONFIG_INITRAMFS_SOURCE 可以是一个绝对路径,也可以是一个从kernel’s top build dir(你敲入build或者是make的地方)开始的相对路径。而指向的目标可以有以下三种:一个已经做好的cpio.gz,或者一个已经为制作cpio.gz准备好所有内容的文件夹,或者是一个text的配置文件。第三种方式是最灵活的,我们先依次来介绍这三种方法。

1)使用一个已经做好的cpio.gz档案

If you already have your own initramfs_data.cpio.gz file (because you created it yourself, or saved the cpio.gz file produced by a previous kernel build), you can point CONFIG_INITRAMFS_SOURCE at it and the kernel build will autodetect the file type and link it into the resulting kernel image.

You can also leave CONFIG_INITRAMFS_SOURCE empty, and instead copy your cpio.gz file to usr/initramfs_data.cpio.gz in your kernel’s build directory. The kernel’s makefile won’t generate a new archive if it doesn’t need to.

Either way, if you build a kernel like this you can boot it without supplying an external initrd image, and it’ll still finish its boot by running your init program out of rootfs. This is packaging method #2, if you’d like to try it now.

2)指定给内核一个文件或者文件夹

If CONFIG_INITRAMFS_SOURCE points to a directory, the kernel will archive it up for you. This is a very easy way to create an initramfs archive, and is method #3.

Interestingly, the kernel build doesn’t use the standard cpio command to create initramfs archives. You don’t even need to have any cpio tools installed on your build system. Instead the kernel build (in usr/Makefile) generates a text file describing the directory with the script “gen_initramfs_list.sh”, and then feeds that descript to a program called “gen_init_cpio” (built from C source in the kernel’s usr directory), which create the cpio archive. This looks something like the following:

scripts/gen_initramfs_list.sh $CONFIG_INITRAMFS_SOURCE > usr/initramfs_list
usr/gen_init_cpio usr/initramfs_list > usr/initramfs_data.cpio
gzip usr/initramfs_data.cpio
To package up our hello world program, you could simply copy it into its own directory, name it “init”, point CONFIG_INITRAMFS_SOURCE at that directory, and rebuild the kernel. The resulting kernel should end its boot by printing “hello world”. And if you need to tweak the contents of that directory, rebuilding the kernel will re-package the contents of that directory if anything has changed.

The downside of this method is that it if your initramfs has device nodes, or cares about file ownership and permissions, you need to be able to create those things in a directory for it to copy. This is hard to do if you haven’t got root access, or are using a cross-compile environment like cygwin. That’s where the fourth and final method comes in.

3)使用configuration文件initramfs_list来告诉内核initramfs在哪里

This is the most flexible method. The kernel’s gen_initramfs_list.sh script creates a text description file listing the contents of initramfs, and gen_init_cpio uses this file to produce an archive. This file is a standard text file, easily editable, containing one line per file. Each line starts with a keyword indicating what type of entry it describes.

The config file to create our “hello world” initramfs only needs a single line:

file /init usr/hello 500 0 0
This takes the file “hello” and packages it so it shows up as /init in rootfs, with permissions 500, with uid and gid 0. It expects to find the source file “hello” in a “usr” subdirectory under the kernel’s build directory. (If you’re building the kernel in a different directory than the source directory, this path would be relative to the build directory, not the source directory.)

To try it yourself, copy “hello” into usr in the kernel’s build directory, copy the above configuration line to its own file, use “make menuconfig” to point CONFIG_INITRAMFS_SOURCE to that file, run the kernel build, and test boot the new kernel. Alternately, you can put the “hello” file in its own directory and use “scripts/gen_initramfs_list.sh dirname” to create a configuration file (where dirname is the path to your directory, from the kernel’s build directory). For large projects, you may want to generate a starting configuration with the script, and then customize it with any text editor.

This configuration file can also specify device nodes (with the “nod” keyword), directories (“dir”), symbolic links (“slink”), named FIFO pipes (“pipe”), and unix domain sockets (“sock”). Full documentation on this file’s format is available by running “usr/gen_init_cpio” (with no arguments) after a kernel build.

A more complicated example containing device nodes and symlinks could look like this:

dir /dev 755 0 0
nod /dev/console 644 0 0 c 5 1
nod /dev/loop0 644 0 0 b 7 0
dir /bin 755 1000 1000
slink /bin/sh busybox 777 0 0
file /bin/busybox initramfs/busybox 755 0 0
dir /proc 755 0 0
dir /sys 755 0 0
dir /mnt 755 0 0
file /init initramfs/init.sh 755 0 0
One significant advantage of the configuration file method is that any regular user can create one, specifying ownership and permissions and the creation of device nodes in initramfs, without any special permissions on the build system. Creating a cpio archive using the cpio command line tool, or pointing the kernel build at a directory, requires a directory that contains everything initramfs will contain. The configuration file method merely requires a few source files to get data from, and a description file.

This also comes in handy cross-compiling from other environments such as cygwin, where the local filesystem may not even be capable of reproducing everything initramfs should have in it.

总结一下

这四种给rootfs提供内容的方式都有一个共同点:在kernel启动时,一系列的文件被解压到rootfs,如果kernel能在其中找到可执行的文件“/init”,kernel就会运行它;这意味着,kernel不会再去理会“root=”是指向哪里的。

此外,一旦initramfs里面的init 进程运行起来,kernel就会认为启动已经完成。接下来,init将掌控整个宇宙!它拥有霹雳无敌的专门为它预留的Process ID #1,整个系统接下来的所有都将由它来创造!还有,它的地位将是不可剥夺的,嗯哼,PID 1 退出的话,系统会panic的。

接下来我会介绍其他一些,在rootfs中,init程序可以做的事。

Footnote 1: The kernel doesn’t allow rootfs to be unmounted for the same reason the same reason it won’t let the first process (PID 1, generally running init) be killed. The fact the lists of mounts and processes are never empty simplifies the kernel’s implementation.

Footnote 2: The cpio format is another way of combining files together, like tar and zip. It’s an older and simpler storage format that dates back to the original unix, and it’s the storage format used inside RPM packages. It’s not as widely used as tar or zip because the command line syntax of the cpio command is unnecessarily complicated (type “man 1 cpio” at a Linux or Cygwin command line if you have a strong stomach). Luckily, we don’t need to use this command.

Footnote 3: The kernel will always panic if PID 1 exits; this is unrelated to initramfs. All of the signals that might kill init are blocked, even “kill -9” which will reliably kill any other process. But init can still call the exit() syscall itself, and the kernel panics if this happens in PID 1. Avoiding it here is mostly a cosmetic issue: we don’t want the panic scrolling our “Hello World!” message off the top of the screen.

Footnote 4: Statically linking programs against glibc produces enormous, bloated binaries. Yes, this is expected to be over 400k for a hello world proram. You can try using the “strip” command on the resulting binary, but it won’t help much. This sort of bloat is why uClibc exists.

Footnote 5: Older 2.6 kernels had a bug where they would append to duplicate files rather than overwriting. Test your kernel version before depending on this behavior.

Footnote 6:User Mode Linux or QEMU can be very helpful testing out initramfs, but are beyond the scope of this article.

Footnote 7: Well, sort of. The default one is probably meant to be empty, but due to a small bug (gen_initramfs_list.sh spits out an example file when run with no arguments) the version in the 2.6.16 kernel actually contains a “/dev/console” node and a “/root” directory, which aren’t used for anything. It gzips down to about 135 bytes, and might as well actually be empty. On Intel you can run “readelf -S vmlinux” and look for section “.init.ramfs” to see the cpio.gz archive linked into a 2.6 kernel. Elf section names might vary a bit on other platforms.

Some useful stuff is here:

-----------------------------------------------------------------------
initrd 制作

在Linux操作系统中,有一项特殊的功能——初始化内存盘INITRD(INITial Ram Disk)技术,而且内核支持压缩的文件系统映像。有了这两项功能,我们可以让Linux系统从小的初始化内存盘启动,并把系统内存的一部分作为根文件系统挂载。

Ramdisk就是将内存的一部分分配为一个分区并作为硬盘来使用。对于系统运行时不断使用的程序,将它们放在Ramdisk中将加快计算机的操作,如大数据量的网络服务器、无盘工作站等。为了能够使用Ramdisk,我们在编译内核时须将block device中的Ramdisk支持选上,它下面还有两个选项,一个是设定Ramdisk的大小,默认是4096k;另一个是设定默认个数。如果要使用initrd,还得选上的支持。它既可以直接编译进内核,也可以编译成模块,在需要的时候加载。我们由于在启动时就用它,所以必须将它直接编译进内核。

下面是2.6内核对模块选择路径:

Linux Kernel Configuration
      -> Device Drivers
        ->Block devices
          ->RAM block device support
            ->Default number of RAM disks  (设定Ramdisk的个数,默认是16)
            ->Default RAM disk size (kbytes) (设定Ramdisk的大小,默认是4096k)

Linux Kernel Configuration
      ->General setup
        ->Inital RAM filesystem and RAM disk(initramfs/initrd) support

如果对Ramdisk的支持已经编译进内核,我们就可以使用它了。首先在/mnt目录下创建目录ram,运行mkdir /mnt/ram;然后对/dev/ram0创建文件系统,运行mke2fs /dev/ram0;最后挂载上/dev/ram,运行mount /dev/ram0 /mnt/ram,就可以象对普通硬盘一样对它进行操作了。值得注意的是,在创建文件系统的时候,在屏幕上输出1024 inodes ,4096 blocks,即ramdisk大小为4M=4096个块,但是我们挂载上之后,用命令df –k /dev/ram查看时,显示出来ramdisk大小只有3963K,这是由于文件系统本身占用了一些空间。(这个空间是在编译核心时就由Default RAM disk size (kbytes)确定下来)

我们能根据需要改变ramdisk的大小。如我们要把默认的4M增大到8M,当ramdisk是直接编译进内核的情况下,可在grub配置文件 grub.conf中加入ramdisk=8192 ,运行grub后,重启计算机后,ramdisk大小变为8M。

例如要设置Ramdisk的大小为8M,在grub中可以用:

# grub.conf -
  default=0
  timeout=10
  splashimage=(hd0,0)/grub/splash.xpm.gz
  title Redice Linux
root (hd0,0)
kernel /vmlinuz ro root=LABEL=/ hdc=ide-scsi ramdisk=8192
initrd /initrd

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

a、在模块加载配置文件 /etc/modules.conf中加入一行:

options rd rd_size=8192,

b、在加载rd模块是在后面加上说明,即insmod rd rd_size=8192。

# insmod rd rd_size=8192

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

ramdisk_size - ramdisk的大小(Kbytes);
  ramdisk - 与ramdisk_size的作用相同;
  ramdisk_blocksize - ramdisk的块大小,默认情况为1024;

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

rd_size - 同上面的ramdisk_size或ramdisk参数;
  rd_blocksize - 同上面的ramdisk_blocksize;

或者在启动是作为启动行参数ramdisk=8192;

创建initrd ramdisk 映像

上面已经提到,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中找到。

制作initrd
 
  initrd 主要有两种格式:传统的ramdisk和cpio格式(这种格式的好处是内核原生不需要额外的文件系统支持)

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

通过ramdisk

# mkfs.ext2 /dev/ram0
  # mount /dev/ram0 /mnt/rd
  # cp what_you_like /mnt/rd # 把需要的文件复制过去 
  # dd if=/dev/ram0 of=/tmp/initrd
  # gzip -9 /tmp/initrd

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

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

dd if=/dev/zero of=/tmp/initrd bs=1024 count=4096 # 制作一个4M的空白文件
  losetup /dev/loop0 /tmp/initrd # 映射到loop设备上;
  mkfs.ext2 /dev/loop0 # 创建文件系统;
  mount /dev/loop0 /mnt/rd
  cp what_you_like /mnt/rd # 复制需要的文件;
  umount /mnt/rd
  losetup -d /dev/loop0
  gzip -9 /tmp/initrd

通过cpio来制作initrd的过程:

cd /path/to                    # 到需要复制的文件的目录
  find . |cpio -o -H newc |gzip -c > …/initrd.gz

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

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐