カーネル(その7)
独自に作成した ext2 ディスクフォーマットを linux でマウントし、ディレクトリを作成する事に成功した。
今回は、ext2 ファイルシステムのデータ構造及び、ファイルシステム作成の処理を紹介する。ファイルシステムの基本的な話は以前にしているので、なるべくそこらには書かれていない事を書きたい。
ソースコードは、
http://www.sourceforge.jp/projects/onix
で公開している。
まずは、システム (マウントする側) はどのようにファイルシステムを認識するのかという話をする。尚、ここでは "ファイルシステム" といった場合、ディスクのファイルシステムフォーマットを指す事にする。
ディスク全体の情報はファイルシステムのスーパーブロック部分に記載されており、ディスクの先頭から 0x400 byte から存在する。これ以前にはマスターブートレコードの領域が存在し、先頭のスーパーブロックはこのように特別扱いされている。
システムは、マウント時にこのスーパーブロックの情報を解読してディスク全体の情報を把握し、その上で各ブロックグループの情報を読み取る。
「先頭の」といったが、かつて書いたようにファイルシステムには複数のスーパーブロックのコピーが存在する。
ちなみに。mke2fs コマンドによって作成したファイルシステムのディスクのダンプを採ってみるとわかるが、このスーパーブロックのコピーは奇数番目のブロックグループのみに存在し、偶数番目のブロックグループの構造は、 ここ で書かれたような構造はしておらず、データブロックのビットマップがブロックグループの先頭に来ている。これは、ファイルシステムの冗長性を維持しつつ効率を上げるための知恵なのだという(*1)。
(*1) : カーネルドキュメント (Documentation/filesystems/ext2.txt)
スーパーブロックのパラメータのうち、inode の総数を表す s_inodes_count や、空きブロックの総数を表す s_free_blocks_count なんかは、ブロックサイズ等のファイルシステムの設計方針が決まれば簡単に値を設定する事が出来る。以下で話す内容以外はこれら同様、特に問題なく設定できる。
最終書き込み時刻などの項目は、1970/01/01 00:00 を基準とした時間 (単位:秒) を指定する。一瞬、ちょっと先の未来までしか記録できないような気もするが、これらは 32bit 幅の領域を持っているので、2106年 とちょこっとまで記録できる。大学のBSDハッカーの准教授曰く、規格の寿命は長くても 15 年らしいので、心配無用だろう。
s_state はファイルシステムの状態を表す。これはマウント側が設定する項目で、0 bit目がマウントされているかどうかを表し、1 bit目でエラーの有無を表す。そして、エラー検出時の動作は s_errors メンバによって表される。onix では 1 に設定し、エラーを無視する。ちなみに。2 にした場合エラー検出時に再マウントを行い、カーネルパニック時には 3 に設定するらしい。これらは、OS 側の仕事。
s_first_inode メンバは、それ以前の inode がシステムに予約されている事を表す。これらは、システム(カーネル)の設計サイドが予約しているものではなく、規格によって決められている値である。例えば、ext2 のルートファイルシステムのトップのディレクトリの inode 番号は 2 番に予約されている。
s_feature_xx は、各種互換機能を表すビットマップとなっている。s_feature_ro_compat 及び s_feature_compat は、他の ext2 ファイルシステムとの互換性のある機能のビットマップを表している。s_feature_incompat は互換性の無い機能、つまり独自機能のビットマップを表している。
onix では現在いづれもサポートしていないので、すべて 0 に設定している。
これでおおよそスーパーブロックを作れる (init_super_block() [fs.h])。
次にブロックグループディスクリプタをつくる。
このデータ構造のオブジェクトに格納する値はさして迷うものはない。onix では、ブロックグループサイズを 0x8000 ブロックで固定しているので、各ビットマップ、inode テーブル、そしてデータ領域のオフセットは各ブロックグループにおいて固定になる (ext2 revision 1 でサポートされている偶数番目のスーパーブロックとブロックグループディスクリプタの省略は現在の所未対応)。
この段階でファイルシステムを作成して linux でマウントさせる事ができる。けれどもディレクトリ参照には失敗する。ルートディレクトリが存在しない為である。
ファイルシステムの作成において、最低限ルートディレクトリと 2 つのディレクトリエントリを作成する必要がある。
まず、ディレクトリ用の inode を作成する。
データサイズは 1 ブロックに固定し、時刻はスーパーブロック同様 2000/1/1 00:00:00 に設定する。設定値に迷うのが、i_links_count メンバかもしれない。これは、ファイルのハードリンクの個数を表すものである。原則、unix ではディレクトリのハードリンクは作る事が出来ないが、ここでは初期値を 2 に設定している。
ファイルシステムから見ると、ハードリンクとは対象ファイルの inode 番号を指すディレクトリエントリを表している。先の inode の i_links_count メンバはこのディレクトリエントリの個数を表している。これが 2 であるというのは、ディレクトリ内に自身のディレクトリを表す "." ディレクトリ及び、下位ディレクトリを表す ".." ディレクトリが存在する為である。ルートディレクトリエントリでは両者のディレクトリが自身を参照する為、ハードリンクの初期値を 2 に設定する。
他のパラメータの値については、容易に設定できる。
さて、最後にディレクトリをつくる。
ディレクトリエントリは可変長なサイズのデータ構造である。これは名前の長さが動的に決定される為である。又、このオブジェクトは 4 byte でアライメントされていなければならず、フラグメンテーションが起こった部分には 0 で埋める。
それと、ディレクトリエントリの長さについてだが。ディレクトリに存在する末尾のディレクトリエントリのサイズは (ディレクトリサイズ - 対象ファイルを除いたディレクトリ内のファイルのディレクトリエントリの長さの合計) となる。これより小さい値となると、ディレクトリサイズの余りがあるにも関らず、対象ファイルのディレクトリエントリの後ろにファイルが存在しない状況となり、マウント側でディレクトリを読み込めなくなる。
これは、各ディレクトリエントリのオブジェクトサイズが可変長である為に、各エントリを調べるに当たり、対象ファイルのディレクトリエントリのサイズを逐次調べながら操作するため、ディレクトリ内の末尾のファイルをそれとわかるようにするため、このような仕組みになっている「と思われる」。
ここまでが、ファイルシステムの初期化処理の主な部分である。
あとは、空きブロック探索のアルゴリズムを用いて、通常ファイルの作成も行える。linux では、この空きブロック探索アルゴリズムに、"クラスタ"なる上位レイヤにおけるディスク構造を想像し、パフォーマンスの良い (とされている) 空きブロック探索のプログラムが利用されているが、onix では単にビットマップの先頭から空いているブロックを渡しているだけである。
ところで、2 年前に自分は 「オペレーティングシステム」なる授業を大学で受けていた。その時に講義をされた講師の先生曰く「[ある民間企業]で働いていた頃、僕の隣のデスクの人間が優秀なディスクアロケーションプログラムを発表して東大に行ってしまった。」といったような事を仰っていた。この空きブロック探索というのは、想像以上に奥深いテーマのようだ (今の時代は枯れているかもわからないが)。
- Category(s)
- 学習経過
- The URL to Trackback this entry is:
- http://dev.ariel-networks.com/Members/ohyama/30ab30fc30cd30eb-305d306e7/tbping