一、initrd介绍
对于使用initramfs镜像的ramdisk来说,这个rootfs即为ramfs(ram file system),它是一个在解压initramfs镜像后就存在且挂载的文件系统,但是系统启动之后用户并不能找到它,因为在内核启动完成后它就会被切换到真实的根文件系统。
前文也提到过,在systemd眼中,initramfs构建的也是一个系统,只不过是虚拟系统,最终systemd会从这个虚拟系统中切换到真实的系统中,切换的内容主要包括两项:切换根分区,切换systemd进程自身。
initrd的具体实现过程是这样的:bootloader把根文件系统映象装载到内存指定位置,把相关参数传递给内核,内核启动时把initrd中的内容复制到ramdisk中(ram0),把initrd占用的内存释放掉,在ram0上mount根文件系统initrd = init ramdisk,是一个启动时存在于内存的文件系统。
二、initrd启动流程
使用initrd的时候,典型的系统启动的流程变为:
1) Boot Loader读入内核镜像以及initrd文件
2) 内核将initrd文件转成“普通”的RAM盘,并且释放掉initrd文件占用的内存。
3) initrd被当作root文件系统,以可读可写(read-write)方式安装。
4) /linuxrc被执行(它可以是任何可执行文件,包括脚本在内;它以uid0身份执行,基本上能完成所有init程序可以做的工作)
5) linuxrc安装“实际”的root文件系统
6) linuxrc通过pivot_root系统调用将root文件系统放置在root目录下。
7) 常用的启动流程(比如调用/sbin/init)开始执行。
8) 卸载initrd文件系统。
注意,这是一个典型流程。其实initrd机制可以通过两种方式使用:要么就是作为一个普通的root文件系统使用,这样的话第5、第6两个步骤可以被略过,直接执行/sbin/init(我们的试验系统就是利用这种方法);要么作为一个过渡环境使用,通过它内核可以继续装载“实际”的root文件系统。
三、initrd启动代码基本过程
加载Linux Kernel和Rootfs加载到内存,并跳转到Linux Kernel入口地址执行程序。
start_kernel入口——init/main.c: asmlinkage __visible void __init start_kernel(void)
fs/dcache.c: vfs_caches_init();
fs/namespace.c:void __init mnt_init(void)
init/do_mounts.c:int __init init_rootfs(void)
fs/namespace.c:static void __init init_mount_tree(void)
fs/namespace.c:vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
fs/super.c:mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
mount_nodev: mount a filesystem that is not backed by a device
//解压initramfs文件系统中的内容到rootfs
init/main.c:void __init __weak arch_call_rest_init(void)——rest_init();——启动idel进行(pid=0,运行在内核态);启动kernel_init进程(init进程通过kernel_thread创建,在内核完成初始化后,加载init程序);启动kthreadd进程(由idle通过kernel_thread创建,并运行在内核空间,负责所有的内核线程的调度和管理)
init/main.c kernel_thread(kernel_init, NULL, CLONE_FS);
init/main.c tatic noinline void __init kernel_init_freeable(void)
static void __init do_basic_setup(void)
static void __init do_initcalls(void)
init/noinitramfs.c:rootfs_initcall(default_rootfs)
注:此时rootfs还没有挂载,现在的根目录是ramfs
init/do_mounts.c: void __init prepare_namespace(void)
init/do_mounts.c: void __init mount_block_root(char *name, int flags)
init/do_mounts.c: static int __init do_mount_root(char *name, char *fs, int flags, void *data)
void __init mount_root(void)
四、initrd启动详细代码分析
void __init vfs_caches_init(void)
{
names_cachep = kmem_cache_create_usercopy("names_cache", PATH_MAX, 0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC, 0, PATH_MAX, NULL);
dcache_init();//页目录缓存的初始化
inode_init();//索引节点缓存的初始化
files_init();//文件初始化
files_maxfiles_init();
mnt_init();//最重要的函数,虚拟文件系统挂载的初始化,创建一个rootfs,这是一个虚拟的rootfs,即内存文件系统,后面才会执行真实文件系统
bdev_cache_init();//块设备初始化
chrdev_init();//字符设备初始化
}
void __init mnt_init(void)
{
int err;
mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct mount),
0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
mount_hashtable = alloc_large_system_hash("Mount-cache",
sizeof(struct hlist_head),
mhash_entries, 19,
HASH_ZERO,
&m_hash_shift, &m_hash_mask, 0, 0);
mountpoint_hashtable = alloc_large_system_hash("Mountpoint-cache",
sizeof(struct hlist_head),
mphash_entries, 19,
HASH_ZERO,
&mp_hash_shift, &mp_hash_mask, 0, 0);
if (!mount_hashtable || !mountpoint_hashtable)
panic("Failed to allocate mount hash table\n");
kernfs_init(); //RCU context,申请kmem_cache_create("kernfs_node_cache"资源
err = sysfs_init();//sysfs 初始化
if (err)
printk(KERN_WARNING "%s: sysfs_init error: %d\n",
__func__, err);
fs_kobj = kobject_create_and_add("fs", NULL);//生成名为fs的kobject对象fs_kobj
if (!fs_kobj)
printk(KERN_WARNING "%s: kobj create error\n", __func__);
init_rootfs();//初始化rootfs文件系统
init_mount_tree();//初始化mount树。挂载rootfs文件系统
}
int __init init_rootfs(void)
{
int err = register_filesystem(&rootfs_fs_type); //注册rootfs文件系统
if (err)
return err;
if (IS_ENABLED(CONFIG_TMPFS) && !saved_root_name[0] &&
(!root_fs_names || strstr(root_fs_names, "tmpfs")))