您的位置:首页 > 运维架构 > Linux

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

ongoingcre 2016-01-14 21:06 399 查看
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 的优点:

tmpfs随着其中数据的增减自动增减容量 - 动态分配

在tmpfs和page cache/dentry cache之间没有重复数据 - 独立空间

tmpfs重复利用了Linux caching的代码, 因此几乎没有增加内核尺寸, 而caching的代码已经经过良好测试, 所以tmpfs的代码质量也有保证.

不需要额外的文件系统驱动 - 简单明了

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 - 点滴积累
标签: