rootfsとルートファイルシステム


start_kernel()からvfs_caches_init()をコールし、mnt_init()でまずrootfsを作成します。それをルートプロセスのrootディレクトリとする事で、以降のプロセスはルートプロセスのネームスペース継承し、従ってシステムのルートファイルとなるわけです。

その後、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);
}

最終更新 2015/02/14 16:39:36 - north
(2012/01/09 16:04:07 作成)


検索

アクセス数
3689201
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。