ルートファイルシステム
カーネルが起動としてルートファイルシステムがマウントされるまでの関数の遷移は以下の通りで、ルートファイルシステムは2つのフェーズで行っている。まず内部ramディスクをルートファイルシステムとしてマウントし、その後正規のルートファイルシステムをその上にマウントし直すと言う具合だ。これはカーネルとして実ルートファイルシステムを何処からでも柔軟にマウントできるようにするための仕組みらしい。
カーネルが起動するとまず、start_kernel関数で各種オブジェクトコンポーネントの初期化を行う。その中のルートファイルシステムの処理は、VFSシステム関連の初期化を行うvfs_caches_init関数のmnt_init関数で、最初のと言うべきファイルシステムinit_rootfs関数よりルートファイルシステムをシステムに登録し、init_mount_tree関数で仮ルートファイルシステムが/としてマウントされる。
ルートファイルシステムは以下のように、スーパブロックをramfsとするramディスクである。
その後、kernel_init関数からprepare_namespace関数で実際のルートファイルシステムがマウントされる。実際のルートファイルシステムではそのファイルシステム種類(NFSとかFDとか)により処理が分かれる。ここではHDとする。
まず、実際のルートファイルシステムを取得する。例えばカーネルにroot=XXXというパラメータが渡されたら、引数XXXでroot_dev_setup関数がコールされ、saved_root_name変数にパラメータがセットされるようになっている。__setup("root=", root_dev_setup)で、.init.setupセクションに
そして、最後にその/rootを/にマウントポイント移動し、そこをカレントとすることで、ユーザの指定するファイルシステムをルートファイルシステムとしてマウントする。
prepare_namespace関数でルートファイルシステムをマウントすると、最終処理としてinit_post関数がよばれ、そこから、すべてのプロセスの親となるinitプロセスが起動される。initプロセスは以下順序でその1つが起動されればよい。
なおinitrdの場合、仮ルートファイルシステムまでは同じだが、直接initrdをramfsに読み込んで、それをマウントすることで、ROOT_DEVによるマウント処理はスキップする。(initプロセス処理も、initrd内のinitプロセス相当が起動)通常initrdはinitrd内で通常のルートファイルシステムをマウントする。
注:rootのホームディレクトリのrootでない。
start_kernel->vfs_caches_init->mnt_init->init_rootfs ->init_mount_tree ->rest_init->kernel_init->prepare_namespace->mount_root
カーネルが起動するとまず、start_kernel関数で各種オブジェクトコンポーネントの初期化を行う。その中のルートファイルシステムの処理は、VFSシステム関連の初期化を行うvfs_caches_init関数のmnt_init関数で、最初のと言うべきファイルシステムinit_rootfs関数よりルートファイルシステムをシステムに登録し、init_mount_tree関数で仮ルートファイルシステムが/としてマウントされる。
ルートファイルシステムは以下のように、スーパブロックをramfsとするramディスクである。
static struct file_system_type rootfs_fs_type = { .name = "rootfs", .get_sb = rootfs_get_sb, .kill_sb = kill_litter_super, }; static int rootfs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt) { return get_sb_nodev(fs_type, flags|MS_NOUSER, data, ramfs_fill_super, mnt); }
その後、kernel_init関数からprepare_namespace関数で実際のルートファイルシステムがマウントされる。実際のルートファイルシステムではそのファイルシステム種類(NFSとかFDとか)により処理が分かれる。ここではHDとする。
まず、実際のルートファイルシステムを取得する。例えばカーネルにroot=XXXというパラメータが渡されたら、引数XXXでroot_dev_setup関数がコールされ、saved_root_name変数にパラメータがセットされるようになっている。__setup("root=", root_dev_setup)で、.init.setupセクションに
struct obs_kernel_param { const char *str; int (*setup_func)(char *); int early; };の構造体で設定関数を定義していく。root=の場合、strはroot=の文字列が設定される__setup_str_##unique_id[] のアドレスが、*setup_funcにはroot_dev_setup関数がと言う感じである。そしてroot=XXXのXXXはsaved_root_nameへと渡っていく。
static int __init root_dev_setup(char *line) { strlcpy(saved_root_name, line, sizeof(saved_root_name)); return 1; } __setup("root=", root_dev_setup); #define __setup(str, fn) \ __setup_param(str, fn, fn, 0) #define __setup_param(str, unique_id, fn, early) \ static char __setup_str_##unique_id[] __initdata __aligned(1) = str; \ static struct obs_kernel_param __setup_##unique_id \ __used __section(.init.setup) \ __attribute__((aligned((sizeof(long))))) \ = { __setup_str_##unique_id, fn, early }そしてこのデバイス識別子を、MKDEVマクロでメジャー番号、マイナー番号をパックにして、静的変数ROOT_DEVに設定し、 mount_root関数で実際のルートファイルシステムをマウントする。この時ramfs下の仮ルートファイルシステムに、ROOT_DEVで/dev/rootの実ルートファイルシステムのデバイスファイルを作成し、このデバイスで仮ルートファイルシステムの/root(注)にマウントすることになる。ブートパラメータの取得方法については今ひとつ分かりません。なんかコメントではCopy the BIOS EDD information from boot_params into a safe place.って書いてありましたが。またカーネルパラメタとかfstabとか・・・。まあ、とりあえずROOT_DEVにルートファイルシステムのデバイス識別子ということで。
create_dev("/dev/root", ROOT_DEV); mount_block_root("/dev/root", root_mountflags);
そして、最後にその/rootを/にマウントポイント移動し、そこをカレントとすることで、ユーザの指定するファイルシステムをルートファイルシステムとしてマウントする。
sys_mount(".", "/", NULL, MS_MOVE, NULL); sys_chroot(".");
prepare_namespace関数でルートファイルシステムをマウントすると、最終処理としてinit_post関数がよばれ、そこから、すべてのプロセスの親となるinitプロセスが起動される。initプロセスは以下順序でその1つが起動されればよい。
run_init_process("/sbin/init"); run_init_process("/etc/init"); run_init_process("/bin/init"); run_init_process("/bin/sh");
なおinitrdの場合、仮ルートファイルシステムまでは同じだが、直接initrdをramfsに読み込んで、それをマウントすることで、ROOT_DEVによるマウント処理はスキップする。(initプロセス処理も、initrd内のinitプロセス相当が起動)通常initrdはinitrd内で通常のルートファイルシステムをマウントする。
注:rootのホームディレクトリのrootでない。