についてまとめたいと考えている。
概要
ファイルシステムの内部構造を説明するには、ファイルシステムの概念 引いては ファイルの概念まで コメントしなくてはならないかも知れない。
もちろん、多くの人はそんなことは 知っているか そうでなくとも なんとなくは知っているから多くは説明する必要がないだろう。
ここでは、カーネル内部がファイルをどのようなものとして扱っているか... という視点で、簡単に整理してみる。ちなみにファイルといっても種類があるので ふれるのはレギュラーファイルの概念のみである。
ファイルとは... カーネルが提供する 不揮発性のデータ領域である。 中にどんなデータが入るかについては、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 といった レイヤーが導入されているので、 それを理解してから、またコメントを書こうかと思う。