ウォンツテック

そでやまのーと

OSを作ろう - ファイルシステム編

ファイルシステムの勉強のためLinux本のVFS回りを読んでいるんですが、ext3ファイルシステムのフォーマットをFD上に作っていた時に疑問だった「どうやってカーネルはルートディレクトリを識別するのだろう」という疑問が晴れません。でもよくよく本を読むと、ルートファイルシステムのマウント段階でread_super()関数によりスーパーブロックの読み込みとルートディレクトリ用のiノードオブジェクトとdentry(ディレクトリエントリ)オブジェクトの作成をしているという記述があったのでこの関数が肝ではないかと思ってソースを見てみる事にした。
まずはカーネルソースのfsディレクトリ以下でgrep -R "read_super" *をかけてファイルを探すと色々な実ファイルシステム上で定義されている(関数名は様々)みたいなのでその中の一つext3/super.cのext3_fill_superという関数を見てみると

static int ext3_fill_super (struct super_block *sb, void *data, int silent)     
{                                                                               
    struct buffer_head * bh;                                                    
    struct ext3_super_block *es = NULL;                                         
    struct ext3_sb_info *sbi;                                                   
    ext3_fsblk_t block;                                                         
    ext3_fsblk_t sb_block = get_sb_block(&data);                                
    ext3_fsblk_t logic_sb_block;                                                
    unsigned long offset = 0;                                                   
    unsigned long journal_inum = 0;                                             
    unsigned long journal_devnum = 0;                                           
    unsigned long def_mount_opts;                                               
    struct inode *root; 
...
    root = iget(sb, EXT3_ROOT_INO);                                             
    sb->s_root = d_alloc_root(root);                                            
    if (!sb->s_root) {                                                          
        printk(KERN_ERR "EXT3-fs: get root inode failed\n");                    
        iput(root);                                                             
        goto failed_mount4;                                                     
    }
...
}

という部分を見つけた。どうやらroot inodeはEXT3_ROOT_INOという定数により決め打ちの番号で取得しているらしい事がわかった。そこでgrep -R "ROOT_INO" *で検索をかけるとinclude/linux/以下のヘッダーファイル群に色んな実ファイルシステムのROOT_INO定義が出てきた

bfs_fs.h:#define BFS_ROOT_INO           2
bfs_fs.h:        ((((offset) - BFS_BSIZE) / sizeof(struct bfs_inode)) + BFS_ROOT_INO)
bfs_fs.h:       ( (__u32)(((ino) - BFS_ROOT_INO) * sizeof(struct bfs_inode)) + BFS_BSIZE)
ext2_fs.h:#define EXT2_ROOT_INO          2      /* Root inode */
ext3_fs.h:#define EXT3_ROOT_INO          2      /* Root inode */
ext3_fs.h:      return ino == EXT3_ROOT_INO ||
minix_fs.h:#define MINIX_ROOT_INO 1
msdos_fs.h:#define MSDOS_ROOT_INO       1       /* == MINIX_ROOT_INO */
proc_fs.h:      PROC_ROOT_INO = 1,
qnx4_fs.h:#define QNX4_ROOT_INO 1
sysv_fs.h:#define SYSV_ROOT_INO 2       /* inode of root directory */

のようにext2, ext3辺りでは2を、minix, MSDOS辺りでは1を使っている。これで「どうやってカーネルはルートディレクトリを識別するのだろう」という疑問は晴れた。


sodexのext3ファイルシステムではrootディレクトリのinodeはbootm.oとkernel.binファイルの次に作ってるので偶然2になっている。今後の事を考えてこの3つのinode番号はallocさせずに固定値でやっておこうかな。
※でもrootディレクトリのinode番号ってsuper blockに書いておいたほうがいいんでは?と思うのは私だけでしょうか。(メモリ上のsuper block構造体にはs_rootというrootディレクトリ用のメンバがあるけどディスク上にはそのようなメンバが無い)


しかし、純粋にLinuxカーネルを理解しようとして本やソースを読んでると99.99%くらい何言ってるかわからない状態だったけど、紛いなりにも同じような所のコードを書きながらだと理解度が1%くらいになった気がする。