1、根文件系统存放在flash中,设备启动过程中会耗时比较久时间。这是因为从flash中读取rootfs的数据操作比较耗时,同时读取存放在rootfs中的*.so,*.ko等文件也比较耗时。这都是由flash本身的读写属性决定的。对于一些需要快速启动的设备,flash的读写速率无法满足,因此可以考虑挂载到内存中的rootfs,即ramdisk。
2、使用ramdisk需要内核的支持,需要打开CONFIG_BLK_DEV_INITRD宏
3、initramfs初始化流程图如下图所示,主要分成两部分。
3.1、挂载rootfs(“/”),vfs_caches_init分支
3.2、制作rootfs,rest_init分支
4、建立rootfs
struct vfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{
......
root = mount_fs(type, flags, name, data);
......
}
EXPORT_SYMBOL_GPL(vfs_kern_mount);
根据vfs_kern_mount函数传入的name="rootfs",最终会执行rootfs_mount完成根文件系统建立。
static struct file_system_type rootfs_fs_type = {
.name = "rootfs",
.mount = rootfs_mount,
.kill_sb = kill_litter_super,
};
5、制作根文件系统
5.1、如何从__initcall6_start到populate_rootfs
device_initcall(populate_rootfs);
populate_rootfs函数通过device_initcall宏,装载到特定的section中(.initcall6.init)
#define device_initcall(fn) __define_initcall(fn, 6)
#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
#define ___define_initcall(fn, id, __sec) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(#__sec ".init"))) = fn;
即device_initcall函数__initcall_populate_rootfs6存放在.initcall6.init的section中
通过//include/asm-generic/vmlinux.lds.h 链接脚本文件,发现
当执行__initcall6_start函数时,就是执行存放在.initcall6.init这个section的数据,即__initcall_populate_rootfs6函数
5.2、制作rootfs
static int __init populate_rootfs(void)
{
char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size);
......
if (initrd_start) {
......
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();
/*
* Try loading default modules from initramfs. This gives
* us a chance to load before device_initcalls.
*/
load_default_modules();
}
return 0;
}
5.2.1、先unpcak_to_rootfs的[__initramfs_start, __initramfs_start+__initramfs_size]数据
通过//include/asm-generic/vmlinux.lds.h 链接脚本文件,发现
其中定义了.init.ramfs和.init.ramfs.info两个section
通过//usr/initramfs_data.S文件发现,__initramfs_size大小由initramfs_data.o确定
其中INITRAMFS_IMAGE在//usr/Makefile文件中定义
initramfs加载后一般不会使用
5.2.2、后unpack_to_rootfs的[initrd_start, initrd_end]数据
其中initrd_end和initrd_start通过cmdline传入,并且数据在uboot阶段已经从flash的rootfs分区解压后搬迁到initrd_start地址。
5.2.2.1、flash镜像中rootfs的制作
rootfs通过cpio命令制作生成*.cpio的文件,根据压缩方式完成压缩(build\images.mk脚本中)
#lzo压缩方式,rootfs.bin是rootfs_initramfs.cpio经过lzo压缩后的文件
lzop -x rootfs.bin
#将cpio文件转换成目录格式,ls查看具体的目录内容
cpio -idmv < rootfs_initramfs.cpio
5.2.2.2、内存中rootfs的制作
static char * __init unpack_to_rootfs(char *buf, unsigned len)
{
......
while (!message && len) {
loff_t saved_offset = this_header;
if (*buf == '0' && !(this_header & 3)) {
state = Start;
written = write_buffer(buf, len);
......
}
......
this_header = 0;
decompress = decompress_method(buf, len, &compress_name);
if (decompress) {
res = decompress(buf, len, NULL, flush_buffer, NULL,
&my_inptr, error);
if (res)
error("decompressor failed");
}
......
}
}
当前buf中的数据已经完成解压,直接通过write_buffer函数制作;不通过具体的压缩方式decompress后flush_buffer实现
write_buffer中通过以下函数解析buf数据,完成根文件系统的制作
static __initdata int (*actions[])(void) = {
[Start] = do_start,
[Collect] = do_collect,
[GotHeader] = do_header,
[SkipIt] = do_skip,
[GotName] = do_name,
[CopyFile] = do_copy,
[GotSymlink] = do_symlink,
[Reset] = do_reset,
};
6、运行跟文件系统中的应用程序
//kernel_init函数内,执行ramdisk_execute_command应用程序,其中ramdisk_execute_command从cmdline中传入
if (ramdisk_execute_command) {
if (!run_init_process(ramdisk_execute_command))
return 0;
pr_err("Failed to execute %s\n", ramdisk_execute_command);
}