全面解析Linux 内核 3.10.x - initramfs 启动流程
2016-01-14 21:06
1656 查看
From: 全面解析Linux 内核 3.10.x - 本文章完全基于MIPS架构
这里有一份来自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。
在tmpfs和page cache/dentry cache之间没有重复数据 - 独立空间
tmpfs重复利用了Linux caching的代码, 因此几乎没有增加内核尺寸, 而caching的代码已经经过良好测试, 所以tmpfs的代码质量也有保证.
不需要额外的文件系统驱动 - 简单明了
initramfs 的启动方式更加适用于嵌入式小系统(被裁剪),因为本质上是一个cpio打包过的文件,本质上打包的时候被将一个单一的文件,目录,node打包进去。
那么将你已经编译生成的rootfs,通过以下命令打包。
在3.10.x中。配置下面宏。
或者
`CONFIG_INITRAMFS_SOURCE=”/home/XX/rootfs.cpio.gz”
这里的区别在于是否加一层压缩,就体积而言,我们当然希望我们的镜像越小越好,所以这里大多都选择.gz/.bz2压缩过的文件。
然后编译内核即可,内核默认支持的解压缩方式(GZIP)。
一组压缩的支持必然是由一组对应的解压缩.
如:
如果你要配置为bz2 或者 xz等压缩格式的支持。
还需要打开对BZ2、XZ的支持。
坚持也许就是胜利 - 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号进程,而后启动文件系统。其的原因就是为了解决initrd3.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 - 点滴积累相关文章推荐
- 制作openstack用的centos6.5镜像
- Linux-中断和中断处理
- Linux创建用户、用户组 及 删除
- CentOS服务器 6.6 安装MySQL5.5.46
- Linux进程间通信——使用共享内存
- Java开发常用的Linux命令
- Linux下媒体文件播放问题
- Linux驱动mmap内存映射
- Linux下截图
- Linux下玩转Dota2
- 如何在Linux上使用命令行查看硬件信息
- Linux下挂载/卸载光盘
- 检测Linux内存使用情况的free命令的10个
- 在Linux虚拟机和物理机之间共享文件夹
- 10大好用的Linux实用工具推荐
- Linux 命令 Knowhow
- CENTOS6.5添加JAVA环境变量
- linux VLAN配置(vconfig)
- CentOS-Linux安装 XS-Tools (XenServer)
- hadoop基础-------虚拟机(三)-----VMware虚拟机下linux系统的与windows主机实现复制粘贴