From: 全面解析Linux 内核 3.10.x - 本文章完全基于MIPS架构
早在之前,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 的启动方式更加适用于嵌入式小系统(被裁剪),因为本质上是一个cpio打包过的文件,本质上打包的时候被将一个单一的文件,目录,node打包进去。
上述我们已知内核支持的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 本质上是将一个文件(rootfs.cpio.gz)在加载内核的时候,先加载到ram,而后找到位置后跳转进入文件本身,启动1号进程,而后启动文件系统。其的原因就是为了解决initrd
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);
By: Keven - 点滴积累