全面解析Linux 内核 3.10.x - initramfs 启动流程

From: 全面解析Linux 内核 3.10.x - 本文章完全基于MIPS架构

坚持也许就是胜利 - Keven

早在之前,Linus提出要把cache当作文件系统装载。

这里有一份来自initramfs 合并的邮件请求,请点击。
我给大家翻译一些小片段(杜撰加翻译)。

————————————–致亲爱的 Linus——————————
*亲爱的Linus:*
最近我苦思冥想,有一个想法不吐不快(关于kernel 启动 rootfs的idea)。
是怎么回事呢?你知道我的,一个重度的最小系统使用着,最近我又在捣鼓完毕了一个新的东西,打算上我的mini system装个逼,让大伙瞅瞅也好继续摸牌我(此处省略1W字+)。可是当我把东西移植完毕后,兴致冲冲的准备让大伙膜拜我的时候(内心已经有点不淡定了),我的mini 突然蹦出一个消息告诉我,找不到文件系统..Oh..shit(当时我的内心是崩溃的),虽然最后问题也被我解决了,但是你知道吗?我的自尊心已经深深受到了伤害。 怎么可以让人看我的笑话?于是我花了一个通宵研究了为何会出现这种错误。并且给出了重新的优化方案。当然我并不是在原先的基本上去添加,而是单独把它拿了出来,起来个名字叫 initramfs ,是不是觉得名字很棒?(得瑟中…)
下面我简单给您阐述下initramfs 的设计原理以及流程,您看是不是可以拉入主线版本呢?(哈哈,本天才将会被千千万万的程序猿们膜拜,颤抖吧…)
a.基本设计思路
将rootfs打包为cpio的压缩文件,并且告诉内核它的起始和大小,当内核启动后在也不需要单独将rootfs做为一个块设备挂载了(因为这样需要内核单独为此种方式写一种驱动来支持)。
b.与老的initrd方式的几点不同
老的initrd image方式加载步骤。
1.boot把Kernel以及initrd文件加载到内存/或者写入Nanflash的特定位置。
2.Kernel判断initrd的文件格式,如果不是cpio格式,将其作为文件image处理。
3.Kernel将initrd的内容保存在rootfs下的/initrd.image文件中。
4.Kernel将/initrd.image的内容读入/dev/ram0设备(虚拟内存盘)中。
5.Kernel以可读写的方式把/dev/ram0设备挂载为原始的根文件系统。
6.如果/dev/ram0被指定为真正的根文件系统,那么内核跳至最后一步正常启动。
7.执行initrd上的/linuxrc文件,linuxrc通常是一个脚本文件,负责加载内核访问根文件系统必须的驱动,以及加载根文件系统。
8./linuxrc执行完毕,常规根文件系统被挂载。
9.如果常规根文件系统存在/initrd目录,那么/dev/ram0将从/移动到/initrd。否则如果/initrd目录不存在,/dev/ram0将被卸载。
10.在常规根文件系统上进行正常启动过程 ,执行/sbin/init。

* 问题 *

/dev/initrd block device 建立的时候有空闲限制,維護繁瑣運作於 initrd 階段,镜像操作实际上是不断將 /dev/initrd 对应到可存取镜像系統的位置,做了不必要的資源消耗。
initramfs 的加载步骤
1.boot 把内核以及 rootfs.cpio.gz 文件加载到内存的位置。
2.Kernel判断文件是否存在,如果存在,解压缩。并且内容释放到rootfs中。
3.默认执行/init,在执行/sbin/init 启动1号进程,进入文件系统。

———————————–The End————————————–

initramfs是在一个叫ramfs的cache实现上加了一层很薄的封装,其他内核开发人员编写了一个改进版tmpfs,这个文件系统上的数据可以写出到交换分区,而且可以设定一个tmpfs装载点的最大尺寸以免耗尽内存。initramfs就是tmpfs的一个应用。initramfs与initrd类似,也是将image初始化完毕且存在于ram中的。可以选择压缩。
目前initramfs只支持cpio包格式,支持主流的压缩方式(gzip,bzip),内核默认支持为gzip压缩的rootfs。

initramfs 的优点:

  1. tmpfs随着其中数据的增减自动增减容量 - 动态分配
  2. 在tmpfs和page cache/dentry cache之间没有重复数据 - 独立空间
  3. tmpfs重复利用了Linux caching的代码, 因此几乎没有增加内核尺寸, 而caching的代码已经经过良好测试, 所以tmpfs的代码质量也有保证.
  4. 不需要额外的文件系统驱动 - 简单明了

initramfs 的启动方式更加适用于嵌入式小系统(被裁剪),因为本质上是一个cpio打包过的文件,本质上打包的时候被将一个单一的文件,目录,node打包进去。

配置支持initramfs

制作initramfs 镜像

上述我们已知内核支持的initramfs 镜像为.cpio格式。
那么将你已经编译生成的rootfs,通过以下命令打包。

cd rootfs/
find ./ -print | cpio -H newc -ov > rootfs.cpio

在3.10.x中。配置下面宏。
CONFIG_INITRAMFS_SOURCE="/home/XX/rootfs.cpio"
或者
`CONFIG_INITRAMFS_SOURCE=”/home/XX/rootfs.cpio.gz”
这里的区别在于是否加一层压缩,就体积而言,我们当然希望我们的镜像越小越好,所以这里大多都选择.gz/.bz2压缩过的文件。
然后编译内核即可,内核默认支持的解压缩方式(GZIP)。
一组压缩的支持必然是由一组对应的解压缩.
如:

CONFIG_RD_GZIP=y
# CONFIG_INITRAMFS_COMPRESSION_GZIP is not set
CONFIG_DECOMPRESS_GZIP=y

如果你要配置为bz2 或者 xz等压缩格式的支持。
还需要打开对BZ2、XZ的支持。

CONFIG_RD_BZIP2=y
CONFIG_INITRAMFS_COMPRESSION_BZIP2=y
CONFIG_DECOMPRESS_BZIP2=y

initramfs 的的本质

根据上述配置,我们都中断initramfs 本质上是将一个文件(rootfs.cpio.gz)在加载内核的时候,先加载到ram,而后找到位置后跳转进入文件本身,启动1号进程,而后启动文件系统。其的原因就是为了解决initrd

3.10.x中关于initramfs 启动解析流程

vim usr/initramfs_data.S
.section .init.ramfs,"a"
__irf_start:
.incbin __stringify(INITRAMFS_IMAGE)
__irf_end:
.section .init.ramfs.info,"a"
.globl VMLINUX_SYMBOL(__initramfs_size)
VMLINUX_SYMBOL(__initramfs_size):
#ifdef CONFIG_64BIT
        .quad __irf_end - __irf_start
#else
        .long __irf_end - __irf_start
#endif

vim arch/arm/kernel/vmlinux.lds.S
#ifdef CONFIG_BLK_DEV_INITRD
        . = ALIGN(32);
        __initramfs_start = .;
            usr/built-in.o(.init.ramfs)
        __initramfs_end = .;
#endif

static int __init populate_rootfs(void)
{
    char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size);
    if (err)
        panic(err); /* Failed to decompress INTERNAL initramfs */
    if (initrd_start) {
#ifdef CONFIG_BLK_DEV_RAM
        int fd;
        printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n");
        err = unpack_to_rootfs((char *)initrd_start,
            initrd_end - initrd_start);
        if (!err) {
            free_initrd();
            goto done;
        } else {
            clean_rootfs();
            unpack_to_rootfs(__initramfs_start, __initramfs_size);
        }
        printk(KERN_INFO "rootfs image is not initramfs (%s)"
                "; looks like an initrd\n", err);
        fd = sys_open("/initrd.image",
                  O_WRONLY|O_CREAT, 0700);
        if (fd >= 0) {
            sys_write(fd, (char *)initrd_start,
                    initrd_end - initrd_start);
            sys_close(fd);
            free_initrd();
        }
    done:
#else
        printk(KERN_INFO "Unpacking initramfs...\n");
        err = unpack_to_rootfs((char *)initrd_start,
            initrd_end - initrd_start);
        if (err)
            printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err);
        free_initrd();
#endif
        /*
         * Try loading default modules from initramfs.  This gives
         * us a chance to load before device_initcalls.
         */
        load_default_modules();
    }
    return 0;
}
rootfs_initcall(populate_rootfs);

Q & A


By: Keven - 点滴积累

你可能感兴趣的:(全面解析Linux 内核 3.10.x - initramfs 启动流程)