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);
}