一、简介

(1) initrd

在早期的linux系统中,一般只有硬盘或者软盘被用来作为linux根文件系统的存储设备,因此也就很容易把这些设备的驱动程序集成到内核中。但是现在的嵌入式系统中可能将根文件系统保存到各种存储设备上,包括scsi、sata,u-disk等等。因此把这些设备的驱动代码全部编译到内核中显然就不是很方便。

为了解决这一矛盾,于是出现了基于ramdisk的initrd( bootloader initialized RAM disk )。Initrd是一个被压缩过的小型根目录,这个目录中包含了启动阶段中必须的驱动模块,可执行文件和启动脚本。当系统启动的时候,bootloader会把initrd文件读到内存中,然后把initrd文件在内存中的起始地址和大小传递给内核。内核在启动初始化过程中会解压缩initrd文件,然后将解压后的initrd挂载为根目录,然后执行根目录中的/linuxrc脚本(cpio格式的initrd为/init,而image格式的initrd<也称老式块设备的initrd或传统的文件镜像格式的initrd>为/initrc),您就可以在这个脚本中加载realfs(真实文件系统)存放设备的驱动程序以及在/dev目录下建立必要的设备节点。这样,就可以mount真正的根目录,并切换到这个根目录中来。

(2) Initramfs

在linux2.5中出现了initramfs,它的作用和initrd类似,只是和内核编译成一个文件(该initramfs是经过gzip压缩后的cpio格式的数据文件),该cpio格式的文件被链接进了内核中特殊的数据段.init.ramfs上,其中全局变量__initramfs_start和__initramfs_end分别指向这个数据段的起始地址和结束地址。内核启动时会对.init.ramfs段中的数据进行解压,然后使用它作为临时的根文件系统。

二、initramfs与initrd区别

(1) Linux内核只认cpio格式的initramfs文件包(因为unpack_to_rootfs只能解析cpio格式文件),非cpio格式的 initramfs文件包将被系统抛弃,而initrd可以是cpio包也可以是传统的镜像(image)文件,实际使用中initrd都是传统镜像文件。
(2) initramfs在编译内核的同时被编译并与内核连接成一个文件,它被链接到地址__initramfs_start处,与内核同时被 bootloader加载到ram中,而initrd是另外单独编译生成的,是一个独立的文件,它由bootloader单独加载到ram中内核空间外的地址,比如加载的地址为addr(是物理地址而非虚拟地址),大小为8MB,那么只要在命令行加入"initrd=addr,8M"命令,系统就可以找到 initrd(当然通过适当修改Linux的目录结构,makefile文件和相关代码,以上两种情况都是可以相通的)。
(3) initramfs被解析处理后原始的cpio包(压缩或非压缩)所占的空间(&__initramfs_start - &__initramfs_end)是作为系统的一部分直接保留在系统中,不会被释放掉,而对于initrd镜像文件,如果没有在命令行中设置"keepinitd"命令,那么initrd镜像文件被处理后其原始文件所占的空间(initrd_end - initrd_start)将被释放掉。
(4) initramfs可以独立ram disk单独存在,而要支持initrd必须要先支持ram disk,即要配置CONFIG_BLK_DEV_INITRD选项 -- 支持initrd,必须先要配置CONFIG_BLK_DEV_RAM -- 支持ram disk ,因为initrd image实际就是初始化好了的ramdisk镜像文件,最后都要解析、写入到ram disk设备/dev/ram或/dev/ram0中。注: 使用initramfs,命令行参数将不需要"initrd="和"root="命令

initramfs利弊:

由于initramfs使用cpio包格式,所以很容易将一个单一的文件、目录、node编译链接到系统中去,这样很简单的系统中使用起来很方便,不需要另外挂接文件系统。
但是因为cpio包实际是文件、目录、节点的描述语言包,为了描述一个文件、目录、节点,要增加很多额外的描述文字开销,特别是对于目录和节点,本身很小额外添加的描述文字却很多,这样使得cpio包比相应的image文件大很多。

使用initramfs的内核配置(使用initramfs做根文件系统):

General setup --->
[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
(/rootfs_dir) Initramfs source file(s) //输入根文件系统的所在目录
使用initramfs的内核启动参数不需要"initrd="和"root="参数,但是必须在initramfs中创建/init文件或者修改内核启动最后代码(init文件是软连接,指向什么? init -> bin/busybox,否则内核启动将会失败)
链接入内核的initramfs文件在linux-2.6.24/usr/initramfs_data.cpio.gz

使用initrd的内核配置(使用网口将根文件系统下载到RAM -- tftp addr ramdisk.gz):

  1. 配置initrd
    General setup --->
    [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
    () Initramfs source file(s) //清空根文件系统的目录配置
  2. 配置ramdisk
    Device Drivers --->
    Block devices --->
    <*> RAM disk support
    (16) Default number of RAM disks // 内核在/dev/目录下生成16个ram设备节点
    (4096) Default RAM disk size (kbytes)
    (1024) Default RAM disk block size (bytes)
    使用 initrd的内 核启动参数:initrd=addr,0x400000 root=/dev/ram rw
    注:
    (1) addr是根文件系统的下载地址;
    (2) 0x400000是根文件系统的大小,该大小需要和内核配置的ramdisk size 4096 kbytes相一致;
    (3) /dev/ram是ramdisk的设备节点,rw表示根文件系统可读、可写;

    根文件系统存放在FLASH分区:

  3. 内核启动参数不需要"initrd="(也可以写成"noinitrd");
    root=/dev/mtdblock2 (/dev/mtdblock2 -- 根文件系统所烧写的FLASH分区)
  4. 内核配置不需要ram disk;也不需要配置initramfs或者initrd
    [ ] Initial RAM filesystem and RAM disk (initramfs/initrd) support
    注: boot的FLASH分区要和kernel的FLASH分区匹配(而非一致),需要进一步解释。

处理流程
linux内核支持两种格式的文件系统镜像:传统格式的文件系统镜像image-initrd和cpio-initrd格式的镜像。
下面分别说明:

cpio-initrd的处理流程:(执行流程可以对照下面博文的代码分析:linux的initrd机制和initramfs机制之根文件挂载流程:代码分析)
1.uboot把内核以及initrd文件加载到内存的特定位置。
2.内核判断initrd的文件格式,如果是cpio格式。
3.将initrd的内容释放到rootfs中。
4.执行initrd中的/init文件,执行到这一点,内核的工作全部结束,完全交给/init文件处理。
可见对于cpio-initrd格式的镜像,它执行的是init文件

image-initrd的处理流程
1.uboot把内核以及initrd文件加载到内存的特定位置。
2.内核判断initrd的文件格式,如果不是cpio格式,将其作为image-initrd处理。
3.内核将initrd的内容保存在rootfs下的/initrd.image文件中。
4.内核将/initrd.image的内容读入/dev/ram0设备中,也就是读入了一个内存盘中。
5.接着内核以可读写的方式把/dev/ram0设备挂载为原始的根文件系统。
6.如果/dev/ram0被指定为真正的根文件系统,那么内核跳至最后一步正常启动。
7.执行initrd上的/linuxrc文件,linuxrc通常是一个脚本文件,负责加载内核访问根文件系统必须的驱动,以及加载根文件系统。
8./linuxrc执行完毕,实际根文件系统被挂载,执行权转交给内核。
9.如果实际根文件系统存在/initrd目录,那么/dev/ram0将从/移动到/initrd。否则如果/initrd目录不存在,/dev/ram0将被卸载。
10.在实际根文件系统上进行正常启动过程,执行/sbin/init。
对于image-initrd格式的镜像,它执行的是linuxrc文件

三、两种格式镜像比较

  1. cpio-initrd的制作方法比image-initrd简单。
  2. cpio-initrd的内核处理流程相比image-initrd更简单,因为:
    a. 根据上面的流程对比可知,cpio-initrd格式的镜像是释放到rootfs中的,不需要额外的文件系统支持,
    而image-initrd格式的镜像先是被挂载成虚拟文件系统,而后被卸载,基于具体的文件系统
    b. image-initrd内核在执行完/linuxrc进程后,还要返回执行内核进行一些收尾工作,
    并且要负责执行真正的根文件系统的/sbin/init。

处理流程对比如下图所示:(来自网络)
initramfs1.jpg

由对比可以看出cpio-initrd格式的镜像更具优势,这也是它逐渐代替image-initrd格式镜像的原因

四、initrd镜像的制作

cpio-initrd格式镜像制作:
进入到要制作的文件系统的根目录;

bash# find . | cpio -c -o > ../initrd.img
bash# gzip ../initrd.img

image-initrd格式镜像制作:
进入到要制作的文件系统的根目录;

bash# dd if=/dev/zero of=../initrd.img bs=512k count=5
bash# mkfs.ext2 -F -m0 ../initrd.img
bash# mount -t ext2 -o loop ../initrd.img /mnt
bash# cp -r * /mnt
bash# umount /mnt
bash# gzip -9 ../initrd.img

对于image-initrd格式镜像的制作,往往采用制作工具,如genext2fs

五、image-initrd格式镜像实例解读
参见下一篇博文
一、initrd

ram disk中的file system叫做initrd,全名叫做initial ramdisk。
如何创建initial ramisk

host > dd if=/dev/zero of=/dev/ram0 bs=1k count=<count>
host > mke2fs -vm0 /dev/ram0 <count>
host > tune2fs -c 0 /dev/ram0
host > dd if=/dev/ram0 bs=1k count=<count> | gzip -v9 > ramdisk.gz

这段代码就创建了大小为count的ramdisk
创建完之后还要添加哪些东西

还要添加一些必要的文件让他工作,可能是库,应用程序等。例如busybox。

host $ mkdir mnt
host $ gunzip ramdisk.gz
host $ mount -o loop ramdisk mnt/
host $ ... copy stuff you want to have in ramdisk to mnt...
host $ umount mnt
host $ gzip -v9 ramdisk
 

内核如何支持initial ramdisk

#
# General setup
#
...
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE=""
...

#
# UBI - Unsorted block images
#
.../*****************initramfs 应该不需要配置下面的参数************************/
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=1
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
...
告诉uboot怎么找到她

UBOOT # tftp 0x87000000 ramdisk.gz
UBOOT # erase 0x2200000 +0x<filesize>
UBOOT # cp.b 0x87000000 0x2200000 0x<filesize>

UBOOT # setenv bootargs ... root=/dev/ram0 rw initrd=0x87000000,8M
UBOOT # setenv bootcmd cp.b 0x2200000 0x87000000 0x<filesize>; bootm
UBOOT # saveenv

注意: ramdisk 中要有ram0节点
最后启动内核
二、initramfs

initramfs相当于把initrd放进了内核,通过cpio(这是一个文件处理工具)实现。
如何创建
比initrd简单多了

host > mkdir target_fs

host > ... copy stuff you want to have in initramfs to target_fs...

注意:

  1. initramfs中的cpio系统不能处理hard link,用soft link
  2. 顶层必须有个init程序,这是kernel要用的,可以这么做

    /init -> /bin/busybox

    接着

    host > cd target_fs
    host > find . | cpio -H newc -o > ../target_fs.cpio

    内核支持

    #
    # General setup
    #
    ...
    CONFIG_BLK_DEV_INITRD=y
    CONFIG_INITRAMFS_SOURCE="<path_to>/target_fs>"
    ...
    
    #
    # UBI - Unsorted block images
    #
    ...
    CONFIG_BLK_DEV_RAM=y
    CONFIG_BLK_DEV_RAM_COUNT=1
    CONFIG_BLK_DEV_RAM_SIZE=8192
    CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
     

    然后执行make uImage的时候就被包含到kernel中了。
    uboot支持

因为已经在kernel中了,不需要像initrd一样通过参数 root=/xxx rw initrd=xxx来告诉uboot了
三、比较

initrd方式中kernel和initial file system为独立的部分,互不影响,下载的时候镜像也小。
创建修改initramfs比initrd容易。
在烧写的时候,显然一个镜像更容易管理。

一、简介

(1) initrd

在早期的linux系统中,一般只有硬盘或者软盘被用来作为linux根文件系统的存储设备,因此也就很容易把这些设备的驱动程序集成到内核中。但是现在的嵌入式系统中可能将根文件系统保存到各种存储设备上,包括scsi、sata,u-disk等等。因此把这些设备的驱动代码全部编译到内核中显然就不是很方便。

为了解决这一矛盾,于是出现了基于ramdisk的initrd( bootloader initialized RAM disk )。Initrd是一个被压缩过的小型根目录,这个目录中包含了启动阶段中必须的驱动模块,可执行文件和启动脚本。当系统启动的时候,bootloader会把initrd文件读到内存中,然后把initrd文件在内存中的起始地址和大小传递给内核。内核在启动初始化过程中会解压缩initrd文件,然后将解压后的initrd挂载为根目录,然后执行根目录中的/linuxrc脚本(cpio格式的initrd为/init,而image格式的initrd<也称老式块设备的initrd或传统的文件镜像格式的initrd>为/initrc),您就可以在这个脚本中加载realfs(真实文件系统)存放设备的驱动程序以及在/dev目录下建立必要的设备节点。这样,就可以mount真正的根目录,并切换到这个根目录中来。

(2) Initramfs

在linux2.5中出现了initramfs,它的作用和initrd类似,只是和内核编译成一个文件(该initramfs是经过gzip压缩后的cpio格式的数据文件),该cpio格式的文件被链接进了内核中特殊的数据段.init.ramfs上,其中全局变量__initramfs_start和__initramfs_end分别指向这个数据段的起始地址和结束地址。内核启动时会对.init.ramfs段中的数据进行解压,然后使用它作为临时的根文件系统。

二、initramfs与initrd区别

(1) Linux内核只认cpio格式的initramfs文件包(因为unpack_to_rootfs只能解析cpio格式文件),非cpio格式的 initramfs文件包将被系统抛弃,而initrd可以是cpio包也可以是传统的镜像(image)文件,实际使用中initrd都是传统镜像文件。
(2) initramfs在编译内核的同时被编译并与内核连接成一个文件,它被链接到地址__initramfs_start处,与内核同时被 bootloader加载到ram中,而initrd是另外单独编译生成的,是一个独立的文件,它由bootloader单独加载到ram中内核空间外的地址,比如加载的地址为addr(是物理地址而非虚拟地址),大小为8MB,那么只要在命令行加入"initrd=addr,8M"命令,系统就可以找到 initrd(当然通过适当修改Linux的目录结构,makefile文件和相关代码,以上两种情况都是可以相通的)。
(3) initramfs被解析处理后原始的cpio包(压缩或非压缩)所占的空间(&__initramfs_start - &__initramfs_end)是作为系统的一部分直接保留在系统中,不会被释放掉,而对于initrd镜像文件,如果没有在命令行中设置"keepinitd"命令,那么initrd镜像文件被处理后其原始文件所占的空间(initrd_end - initrd_start)将被释放掉。
(4) initramfs可以独立ram disk单独存在,而要支持initrd必须要先支持ram disk,即要配置CONFIG_BLK_DEV_INITRD选项 -- 支持initrd,必须先要配置CONFIG_BLK_DEV_RAM -- 支持ram disk ,因为initrd image实际就是初始化好了的ramdisk镜像文件,最后都要解析、写入到ram disk设备/dev/ram或/dev/ram0中。注: 使用initramfs,命令行参数将不需要"initrd="和"root="命令

initramfs利弊:

由于initramfs使用cpio包格式,所以很容易将一个单一的文件、目录、node编译链接到系统中去,这样很简单的系统中使用起来很方便,不需要另外挂接文件系统。
但是因为cpio包实际是文件、目录、节点的描述语言包,为了描述一个文件、目录、节点,要增加很多额外的描述文字开销,特别是对于目录和节点,本身很小额外添加的描述文字却很多,这样使得cpio包比相应的image文件大很多。

使用initramfs的内核配置(使用initramfs做根文件系统):

General setup --->
[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
(/rootfs_dir) Initramfs source file(s) //输入根文件系统的所在目录
使用initramfs的内核启动参数不需要"initrd="和"root="参数,但是必须在initramfs中创建/init文件或者修改内核启动最后代码(init文件是软连接,指向什么? init -> bin/busybox,否则内核启动将会失败)
链接入内核的initramfs文件在linux-2.6.24/usr/initramfs_data.cpio.gz

使用initrd的内核配置(使用网口将根文件系统下载到RAM -- tftp addr ramdisk.gz):

  1. 配置initrd
    General setup --->
    [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
    () Initramfs source file(s) //清空根文件系统的目录配置
  2. 配置ramdisk
    Device Drivers --->
    Block devices --->
    <*> RAM disk support
    (16) Default number of RAM disks // 内核在/dev/目录下生成16个ram设备节点
    (4096) Default RAM disk size (kbytes)
    (1024) Default RAM disk block size (bytes)
    使用 initrd的内 核启动参数:initrd=addr,0x400000 root=/dev/ram rw
    注:
    (1) addr是根文件系统的下载地址;
    (2) 0x400000是根文件系统的大小,该大小需要和内核配置的ramdisk size 4096 kbytes相一致;
    (3) /dev/ram是ramdisk的设备节点,rw表示根文件系统可读、可写;

    根文件系统存放在FLASH分区:

  3. 内核启动参数不需要"initrd="(也可以写成"noinitrd");
    root=/dev/mtdblock2 (/dev/mtdblock2 -- 根文件系统所烧写的FLASH分区)
  4. 内核配置不需要ram disk;也不需要配置initramfs或者initrd
    [ ] Initial RAM filesystem and RAM disk (initramfs/initrd) support
    注: boot的FLASH分区要和kernel的FLASH分区匹配(而非一致),需要进一步解释。