Linux——initrd根文件挂载分析

  • Post author:
  • Post category:linux




一、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")))



版权声明:本文为Feng_8071原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。