ロード トップ 参照元 逆検索 検索 ヘルプ

[logo] ファイルシステム


ファイルシステム --- ここでは、ファイルシステムについて その仕組み や インターフェイスについて 考えてみたい。

についてまとめたいと考えている。

  • なんのために整理するのか... お勉強というのもあるけれども.. あえて具体的な目的を設定すれば、カーネルに依存しないファイルシステム... Linux にも BSD にも core の部分は依存せずに書けて、なおかつ userland で も利用できる。そういうものを作るにはどうしたら良いのだろう? ということを 考える ... それには、知識が必要でその知識を整理する。... そういうのが このページの目的。


    概要

    ファイルシステムの内部構造を説明するには、ファイルシステムの概念 引いては ファイルの概念まで コメントしなくてはならないかも知れない。

    もちろん、多くの人はそんなことは 知っているか そうでなくとも なんとなくは知っているから多くは説明する必要がないだろう。

    ここでは、カーネル内部がファイルをどのようなものとして扱っているか... という視点で、簡単に整理してみる。ちなみにファイルといっても種類があるので ふれるのはレギュラーファイルの概念のみである。

    ファイルとは... カーネルが提供する 不揮発性のデータ領域である。 中にどんなデータが入るかについては、1つ2つの例外を除き自由である。

    さらに、このデータ領域は 読み書きできるだけでなく、自由に 大きさを変更することができる。

    このデータ領域は、あるID で特定できる。この ID は、mount した ファイルシステムが提供する inode 番号 と mount したファイルシステムを一意に 決める ID -- デバイス番号の組合せである。

    ファイルシステムが提供する inode 番号 は、永続性があるが、 デバイス番号は、システムが立ち上げなおされれば、変わる可能性があり、 永続性はない。

    ファイルには、ディレクトリという name space の中で 名前を付けることができる。 名前 に対して inode 番号 が対応する。ディレクトリ自体も 実態はファイルで ある。したがって、ディレクトリに対しても 上位の ディレクトリの中で 名前を付けることができる。

    さて、ファイルという概念の 不揮発性のデータ領域には、いろいろな属性を 付けることができる。これは、struct stat で 定義されている。

    struct stat はシステムによって、さらには 同一システムでも バージョンに よって多少違う。ここで 少し整理してみる。

                属性の名前          UNIX V7    Linux (2.4)  NetBSD(1.5?)
                st_dev                o           o            o
                st_ino                o           o            o
                st_mode               o           o            o
                st_nlink              o           o            o
                st_uid                o           o            o
                st_gid                o           o            o
                st_rdev               o           o            o
                st_size               o           o            o
                st_atime              o           o            o
                st_atimensec                                   o
                st_mtime              o           o            o
                st_mtimensec                                   o
                st_ctime              o           o            o
                st_mtimensec                                   o
                st_blocksize                      o            o
                st_blocks                         o            o
                st_flags                                       o
                st_gen                                         o
    

    st_xtimensec というのは、名前から想像できるとおり、時間の精度をあげる ためのフィールドであるから、概念的には、st_xtime の一部と考えてよい。
    st_blocksize, st_blocks といったものは、レギュラーファイルの概念には ないから無視してよいだろう。
    st_flags や st_gen については、次のような説明がある。

       st_flags  : user defined flags for file 
       st_gen    : file generation number
    

    誤解を恐れずに言うならば、ファイルが持つべき属性は、ほとんど V7 の時代で 決められたものであり、それほど拡張されていない。 ただし、それぞれの属性の詳細までふれていないし、 おおむねそういうものだという意味である。

    また、これらの属性の多くは、不揮発性のデータ領域自体を定義することとは 関係がない。関係があるものは、st_ino,st_size だけといっても良いかも知れない。

    さて、ファイルシステムが提供しているものは、inode 番号 という ID を もった、不揮発性のデータ領域 および その属性 である ということに なるだろう。

    それに対してどういうメソッドを持つべきなのか ... V7 では、ファイルシステムが1つしかなく、 どこまでがファイルシステムのAPI か整理するには不適切である。 今のシステムは、多様化していて、ほんとうにもつべき ものはなにかという点について、釈然としない。 ここでは、NFS V2 プロトコル + αから表を作って 整理してみることにする。

    NFS           V7                  4.3BSD-Reno
    protocol                          vnodeops
                                       + vfsops
    
    GETATTR       stat1(internal)     vn_getattr
    SETATTR        --                 vn_setattr
    LOOKUP        namei()             vn_lookup
    ACCESS	      access()            vn_access
    READLINK       --                 vn_readlink
    READ          readi()             vn_read       
    WRITE         writei()            vn_write                
    CREATE        mknode(internal)    vn_create
    MKDIR	      (userland)          vn_mkdir
    SYMLINK        --                 vn_symlink
    MKNOD         mknode(internal)    vn_mknod
    REMOVE        unlink(syscall)     vn_remove
    RMDIR         (userland)          vn_rmdir
    RENAME        (userland)          vn_rename
    LINK          link(syscall)       vn_link
    READDIR	      (userland)          vn_readdir
    FSSTAT         --                 vfs_statfs
    COMMIT(V3)     --                 vn_fsync(?)
    
                                      vn_open
                                      vn_close
                                      vn_select
                                      vn_mmap
                                      vn_seek
    				  vn_abortop
    				  vn_inactive
    				  vn_lock
    				  vn_unlock
    				  vn_bmap
    				  vn_strategy
    				  vn_print
    				  vn_islocked
    
    				  vfs_mount
    				  vfs_start
    				  vfs_unmount
    				  vfs_quotactl
    				  vfs_fhtovp
    				  vfs_vptofh
    				  vfs_init
    

    うーん。まったく違うものを比較しているという趣になってしまった。 これに 現在の Linux とか BSD とかいれると もっとひどいことになるのは、 明らかなのだ。

    下位層に対するインターフェイスをどうするかという問題は これとは 別に解決しなければならない。

    ここでは、下位層は Disk のみであるということを想定して 考える。

    ページング というものを考慮しなければならないはずだが ... 最初は ブロックデバイスに対する I/O という観点のみで 比較する。
    比較対象は、V7, 4.3BSD-Reno および Linux(2.4) である。

           V7                4.3BSD-Reno             Linux-2.4
    
    bio
    
       getblk(dev,bno)       getblk(vp,bno,size)     getblk(dev,bno,size)
       geteblk()             geteblk(size)                  
        --                   brealloc(bp,size)       
       bread(dev,bno)        bread(vp,bno,size       bread(dev,bno,size) 
                                       ,cred,bpp)    
       breada(dev,bno        breada(vp,bno,size      --
          , rablock)           ,rablock,cred, bpp)   
                                                     
    
    
       brelse(bp)            brelse(bp)              brelse(bh)
       bdwrite(bp)           bdwrite(bp)		 mark_buffer_dirty(bh)
       bawrite(bp)           bawrite(bp)             --
       bwrite(bp)            bwrite(bp)              --
    
    
       iowait(bp)            biowait(bp)             wait_on_buffer(bh)
    
       iodone(bp)            biodone(bp)             end_buffer_io_async(bh)
                                                     end_buffer_io_sync(bh)
    
    process
    
       sleep(chan,pri)       sleep(chan,pri)	 sleep_on
       wakeup(chan)          wakeup(chan)            wake_up
    
    mutex
    
       spl6()                splbio()                
       spl0()
       splx(s)               splx(s)
    
    IO
      (*bdevsw[major(dev)]
       .d_strategy)(bp)      VOP_STRATEGY(bp)         ll_rw_block(bh)
                                                       or submit_bh(bh)
    

    うーん。Linux も似てるじゃんというには... 無理があるかなぁ。

    だいたい、Linux は、メタ情報に対しては、自分で I/O しなければならないが、 通常の read/write は、generic なものが用意されている。

    こういうやつ

    page:
          block_read_full_page  (+ get_block callback)
          block_write_full_page (+ get_block callback)
          block_sync_page
          block_prepare_write   (+ get_block callback)
          generic_commit_write  
          generic_block_bmap    (+ get_block callback)
    
    file:
          generic_file_read
          generic_file_write
          generic_file_mmap
    

    この callback はいわゆる bmap のようだ。 要するに file に対応する block がどこにあるか 決めるだけで良い。

    file に対する I/O は、システム依存で、ファイルシステムは、 メタ情報に専念するのがよろしい --- そういうことができるようには なっているわけか。

    02/08/02 ちょっと コメント。

    実をいうと、page と DISK 上の位置 を core が知っていて勝手に 書き込むといった考えのもとに作られたインターフェイスはどうかという気が している。

    非常にわかりやすいのは良いのだが... DISK 上のデータの 動的再配置の必要性や I/O 順番の 依存性を知ってしまうと、 それだけでは足りないように思う。

    これについては、 Linux では、jfs といった レイヤーが導入されているので、 それを理解してから、またコメントを書こうかと思う。


    (最終更新 Thu Mar 30 19:17:23 2006)