rootfsとルートファイルシステム
start_kernel()からvfs_caches_init()をコールし、mnt_init()でまずrootfsを作成します。それをルートプロセスのrootディレクトリとする事で、以降のプロセスはルートプロセスのネームスペース継承し、従ってシステムのルートファイルとなるわけです。
その後、INITCALLS配下に定義された関数群を順次コールし、その一つのpopulate_rootfs()が初期起動のルートファイルシステムのイメージをrootfsに展開し、そのファイルシステムの/initを起動する事でのシステムがスタートされます。
bdi_init()はramfsのブロックデバイス属性を設定(ブロックデバイスの読み書きする情報)で、rootfs下のinode取得毎時、 inode->i_mapping->backing_dev_inf=&ramfs_backing_dev_infotoとして設定されます。
ramdisk_execute_commandは初期ルートファイルシステムが展開された後に、最初に起動されるプロセスで、デフォルトは/initです。これはブートローダから引数として設定できます。
なお、ramdisk_execute_command(/init)が初期ルートファイルシステムに存在しない場合、prepare_namespace()をコールする事で、別のデバイスからのそれをルートファイルシステムとする事で、改めて初期ルートファイルシステムを設定しなおします。(initram.imgがcpioでなくブロックデバイスイメージの場合もinitrd_load()で/initrd.imageをrootfsブロックデバイスへrawデバイスとして書き込むことで実現します。initram.imgがブロックデバイスイメージの場合、populate_rootfsでブロックデバイスイメージとする/initrd.imageファイルを作成します。)
__initramfs_startはカーネルイメージに組み込まれている物(たぶん)で、.init.ramfsはセクションに展開され、initrd_startはgrubで展開されたramイメージの先頭アドレスとなります。
unpack_to_rootfs()はgzipで圧縮されたcpioのファイルを前提としており、__initramfs_startはcpioのケースと、ブロックデバイスイメージの場合があり、cpioだと/rootfsに展開されますが、ブロックデバイスイメージだとエラーとなり、initrd_startでの展開で、これもcpioだと展開されますが、そうでないとエラーとなります。
エラーならinitrd_startはブロックデバイスイメージという事で、イメージその物をrootfsに/initrd.imageファイルを作成し、そのファイルにブロックデバイスイメージを書き込みます。/initrd.imageは以降でrootfsをrawとしてそのイメージをブロックデバイスにrawデバイスとして書き込みことで、rootfsをルートファイルシステムとします。
その後、INITCALLS配下に定義された関数群を順次コールし、その一つのpopulate_rootfs()が初期起動のルートファイルシステムのイメージをrootfsに展開し、そのファイルシステムの/initを起動する事でのシステムがスタートされます。
void __init vfs_caches_init(unsigned long mempages) { unsigned long reserve; reserve = min((mempages - nr_free_pages()) * 3/2, mempages - 1); mempages -= reserve; names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); dcache_init(); inode_init(); files_init(mempages); mnt_init(); bdev_cache_init(); chrdev_init(); }mnt_init()は、init_rootfs()でrootfsファイルシステムを登録し、init_mount_tree()でrootfsのrootをルートプロセスのrootとする事で、システムのルートファイルシステムとします。
void __init mnt_init(void) { unsigned u; int err; init_rwsem(&namespace_sem); mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct mount), 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC); if (!mount_hashtable) panic("Failed to allocate mount hash table\n"); printk(KERN_INFO "Mount-cache hash table entries: %lu\n", HASH_SIZE); for (u = 0; u < HASH_SIZE; u++) INIT_LIST_HEAD(&mount_hashtable[u]); br_lock_init(vfsmount_lock); err = sysfs_init(); if (err) printk(KERN_WARNING "%s: sysfs_init error: %d\n", __func__, err); fs_kobj = kobject_create_and_add("fs", NULL); if (!fs_kobj) printk(KERN_WARNING "%s: kobj create error\n", __func__); init_rootfs(); init_mount_tree(); }init_rootfs()はrootfs_fs_typeファルシステムをregister_filesystem()で登録しますが、mountコールバックのrootfs_mount()はスーパブロック取得はramfs_fs_typeと同じです。従ってrootfsはramfsその物です。ただしmountフラグがMS_NOUSERでユーザがそのファイルシステムを参照できません。
bdi_init()はramfsのブロックデバイス属性を設定(ブロックデバイスの読み書きする情報)で、rootfs下のinode取得毎時、 inode->i_mapping->backing_dev_inf=&ramfs_backing_dev_infotoとして設定されます。
static struct file_system_type rootfs_fs_type = { .name = "rootfs", .mount = rootfs_mount, .kill_sb = kill_litter_super, }; int __init init_rootfs(void) { int err; err = bdi_init(&ramfs_backing_dev_info); if (err) return err; err = register_filesystem(&rootfs_fs_type); if (err) bdi_destroy(&ramfs_backing_dev_info); return err; } static struct dentry *rootfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return mount_nodev(fs_type, flags|MS_NOUSER, data, ramfs_fill_super); } static struct file_system_type ramfs_fs_type = { .name = "ramfs", .mount = ramfs_mount, .kill_sb = ramfs_kill_sb, }; static int __init init_ramfs_fs(void) { return register_filesystem(&ramfs_fs_type); } module_init(init_ramfs_fs) struct dentry *ramfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return mount_nodev(fs_type, flags, data, ramfs_fill_super); }init_mount_tree()でrootfsのrootをルートプロセスのrootとします。do_kern_mount()でstruct vfsmountを取得し、create_mnt_ns()で取得したmnt_namespaceのrootをmnt(rootfs)とするネームスペースとし、初期タスクのinit_taskのネームスペースに設定し、プロセスのカレントディレクトリ/ルートディレクトリとする事で、システムのルートファイルシステムとなります。(mountシステムコールと違ってvfsmountをdentryeにバインドするのでありません。そもそもmount先のdentryがありません。)
static void __init init_mount_tree(void) { struct vfsmount *mnt; struct mnt_namespace *ns; struct path root; mnt = do_kern_mount("rootfs", 0, "rootfs", NULL); if (IS_ERR(mnt)) panic("Can't create rootfs"); ns = create_mnt_ns(mnt); if (IS_ERR(ns)) panic("Can't allocate initial namespace"); init_task.nsproxy->mnt_ns = ns; get_mnt_ns(ns); root.mnt = mnt; root.dentry = mnt->mnt_root; set_fs_pwd(current->fs, &root); set_fs_root(current->fs, &root); }ここまでの処理が終わると、次はrootfsに実際のファイルシステム(initrd.img)をバインドします。これはstart_kernel()の各初期化が終了した最後のフェーズで rest_init()をコールし、kernel_thread()で起動されるkernel_init()で実現されます。
static noinline void __init_refok rest_init(void) { int pid; rcu_scheduler_starting(); kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); numa_default_policy(); pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); rcu_read_lock(); kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns); rcu_read_unlock(); complete(&kthreadd_done); init_idle_bootup_task(current); preempt_enable_no_resched(); schedule(); preempt_disable(); cpu_idle(); }do_basic_setup()はrootfs_initcallマクロで定義された関数群をコールするもので、その一つのpopulate_rootfs()が初期ルートファイルシステムのinitramイメージををrootfsに設定します。なお、rootfsに初期ルートファイルシステムをmountするとの説明があったりしますが、そうでなくrootfsに初期ルートファイルシステムイメージを展開することで実現します。(デバイスファイルシステム形式のイメージであっても同じです。)
ramdisk_execute_commandは初期ルートファイルシステムが展開された後に、最初に起動されるプロセスで、デフォルトは/initです。これはブートローダから引数として設定できます。
なお、ramdisk_execute_command(/init)が初期ルートファイルシステムに存在しない場合、prepare_namespace()をコールする事で、別のデバイスからのそれをルートファイルシステムとする事で、改めて初期ルートファイルシステムを設定しなおします。(initram.imgがcpioでなくブロックデバイスイメージの場合もinitrd_load()で/initrd.imageをrootfsブロックデバイスへrawデバイスとして書き込むことで実現します。initram.imgがブロックデバイスイメージの場合、populate_rootfsでブロックデバイスイメージとする/initrd.imageファイルを作成します。)
static int __init kernel_init(void * unused) { wait_for_completion(&kthreadd_done); gfp_allowed_mask = __GFP_BITS_MASK; set_mems_allowed(node_states[N_HIGH_MEMORY]); set_cpus_allowed_ptr(current, cpu_all_mask); cad_pid = task_pid(current); smp_prepare_cpus(setup_max_cpus); do_pre_smp_initcalls(); lockup_detector_init(); smp_init(); sched_init_smp(); do_basic_setup(); if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) printk(KERN_WARNING "Warning: unable to open an initial console.\n"); (void) sys_dup(0); (void) sys_dup(0); if (!ramdisk_execute_command) ramdisk_execute_command = "/init"; if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { ramdisk_execute_command = NULL; prepare_namespace(); } init_post(); return 0; } void __init prepare_namespace(void) { int is_floppy; if (root_delay) { printk(KERN_INFO "Waiting %dsec before mounting root device...\n", root_delay); ssleep(root_delay); } wait_for_device_probe(); md_run_setup(); if (saved_root_name[0]) { root_device_name = saved_root_name; if (!strncmp(root_device_name, "mtd", 3) || !strncmp(root_device_name, "ubi", 3)) { mount_block_root(root_device_name, root_mountflags); goto out; } ROOT_DEV = name_to_dev_t(root_device_name); if (strncmp(root_device_name, "/dev/", 5) == 0) root_device_name += 5; } if (initrd_load()) goto out; if ((ROOT_DEV == 0) && root_wait) { printk(KERN_INFO "Waiting for root device %s...\n", saved_root_name); while (driver_probe_done() != 0 || (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0) msleep(100); async_synchronize_full(); } is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR; if (is_floppy && rd_doload && rd_load_disk(0)) ROOT_DEV = Root_RAM0; mount_root(); out: devtmpfs_mount("dev"); sys_mount(".", "/", NULL, MS_MOVE, NULL); sys_chroot((const char __user __force *)"."); } int __init initrd_load(void) { if (mount_initrd) { create_dev("/dev/ram", Root_RAM0); if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) { sys_unlink("/initrd.image"); handle_initrd(); return 1; } } sys_unlink("/initrd.image"); return 0; }populate_rootfs()はrootfsに初期ramディスクを展開します。__initramfs_startに展開されている場合と、initrd_startに展開される2つのケースがあります。
__initramfs_startはカーネルイメージに組み込まれている物(たぶん)で、.init.ramfsはセクションに展開され、initrd_startはgrubで展開されたramイメージの先頭アドレスとなります。
unpack_to_rootfs()はgzipで圧縮されたcpioのファイルを前提としており、__initramfs_startはcpioのケースと、ブロックデバイスイメージの場合があり、cpioだと/rootfsに展開されますが、ブロックデバイスイメージだとエラーとなり、initrd_startでの展開で、これもcpioだと展開されますが、そうでないとエラーとなります。
エラーならinitrd_startはブロックデバイスイメージという事で、イメージその物をrootfsに/initrd.imageファイルを作成し、そのファイルにブロックデバイスイメージを書き込みます。/initrd.imageは以降でrootfsをrawとしてそのイメージをブロックデバイスにrawデバイスとして書き込みことで、rootfsをルートファイルシステムとします。
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(); return 0; } 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((const char __user __force *) "", O_WRONLY|O_CREAT, 0700); if (fd >= 0) { sys_write(fd, (char *)initrd_start, initrd_end - initrd_start); sys_close(fd); free_initrd(); } #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 } return 0; } rootfs_initcall(populate_rootfs);populate_rootfs()はrootfs_initcallマクロで定義され、initcallrootfs.initセクションに配置されます。do_basic_setup()でrootfs_initcallマクロで設定された全てのfnを()コールします。
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs) #define __define_initcall(level,fn,id) \ static initcall_t __initcall_##fn##id __used \ __attribute__((__section__(".initcall" level ".init"))) = fn #define INITCALLS \ *(.initcallearly.init) \ VMLINUX_SYMBOL(__early_initcall_end) = .; \ *(.initcall0.init) \ *(.initcall0s.init) \ *(.initcall1.init) \ *(.initcall1s.init) \ *(.initcall2.init) \ *(.initcall2s.init) \ *(.initcall3.init) \ *(.initcall3s.init) \ *(.initcall4.init) \ *(.initcall4s.init) \ *(.initcall5.init) \ *(.initcall5s.init) \ *(.initcallrootfs.init) \ *(.initcall6.init) \ *(.initcall6s.init) \ *(.initcall7.init) \ *(.initcall7s.init) #define INIT_CALLS \ VMLINUX_SYMBOL(__initcall_start) = .; \ INITCALLS \ VMLINUX_SYMBOL(__initcall_end) = .; static void __init do_basic_setup(void) { cpuset_init_smp(); usermodehelper_init(); shmem_init(); driver_init(); init_irq_proc(); do_ctors(); usermodehelper_enable(); do_initcalls(); } static void __init do_initcalls(void) { initcall_t *fn; for (fn = __early_initcall_end; fn < __initcall_end; fn++) do_one_initcall(*fn); }