JA EN
体系OSと実行環境
·★ 会員·12分で読めます

ファイルシステムを1から — 「保存した」は何を保証するか

エディタが「保存しました」と言った直後に電源が落ちたら、そのファイルは残るのか。図書館の目録という比喩から出発して、ブロック・inode・ディレクトリ・ジャーナリング・fsyncまでを前提知識ゼロで積み上げ、なぜデータベースがファイルシステムを信用しないのか、現場で何を触ると事故になるのかまで降ろします。

対象textタスクsystems

「保存しました」の直後に電源を抜いたら

エディタの右下に「保存しました」と出ます。その0.2秒後に停電したとして、さっき書いた文章は残っているでしょうか。

答えは「たぶん残る。ただし保証はない」です。しかも、残るかどうかを決めているのはエディタではありません。アプリとディスクの間には、ページキャッシュ・ファイルシステム・デバイスの内蔵キャッシュという「まだ書いていないが書いたことにしておく」層が何段も挟まっていて、そのどれもが速さのために嘘をつきます。ファイルシステムを学ぶとは、この嘘の分布を知ることです。

比喩: 目録カードと書架

大きな図書館を想像してください。本そのものは奥の書架にあり、棚には番号が振ってあるだけです。代わりに目録カードがあって、「この本は3棚の12番から7冊分の幅を占める」「420ページ」「誰が閲覧できるか」が書いてある。ただしカードに題名は書かれていません。

題名を扱うのは3つ目の帳面、名前の一覧です。ここには「『銀河ヒッチハイク・ガイド』→ カード番号 8123」という対応だけが並びます。

この3層がファイルシステムのほぼ全部です。書架がデータブロック、目録カードがinode、名前の一覧がディレクトリ。奇妙な分け方に見えますが、分けたからこそ「同じ本に2つの題名を付ける」「題名を変えても本を1ミリも動かさない」が一瞬で終わります。

ディスクはバイト単位では書けない

物理の制約から入ります。ストレージは1バイトずつ書き換えられません。デバイスが扱う最小単位はセクタ(512バイト、最近の機種は4096バイト)で、ファイルシステムはそれを束ねたブロック(多くのLinux環境で4KiB)で管理します。

結果、1バイトのファイルもディスク上の4KiBを占めます(内部断片化)。ls -l のサイズと du の占有量がずれるのはこれが理由です。ファイルが大きくなると位置の記録自体が重くなるので、現代のext4やXFSはエクステント、つまり「45000番から連続で2048ブロック」という範囲でまとめて記録します。

inode — 名前を持たない「ファイルの本体」

目録カードにあたるのが inode(index node)です。ファイル種別とパーミッション、所有者、サイズ、タイムスタンプ、そしてデータの位置。stat を叩くとほぼそのまま見えます。

$ stat notes.txt
  Size: 1240      Blocks: 8     IO Block: 4096   regular file
Device: 259,2  Inode: 8123      Links: 2
Access: (0644/-rw-r--r--)  Uid: (1000/k)  Gid: (1000/k)

効いてくるのは Links: 2 です。これはこのinodeを指している名前の数で、ln notes.txt memo.txt とすれば3になり、どの名前も完全に対等な「本物」になります(コピーではないので、片方を編集すると全部が変わります)。

だから削除のシステムコールは delete ではなく unlink です。やっているのは名前を1つ外してカウントを1減らすことだけ。カウントが0になり、かつ誰もそのファイルを開いていないときに初めてブロックが解放されます。この「かつ」が、後で現場の事故として戻ってきます。

ディレクトリは「表」でしかない

ディレクトリは魔法ではなく、「名前 → inode番号」の組を並べたただのファイルです。表の先頭には必ず自分自身を指す . と親を指す .. が入っているので、どのディレクトリもリンク数が2から始まります。

素朴に作れば、この表は先頭から舐めるしかありません。10万個のファイルがあれば1個を探すのに平均5万件の比較が要ります。そこでext4は dir_index(htree=ハッシュ化したB木)、XFSはB+木を使い、名前から一発で引けるようにしています。この差は「倍」ではなく「桁」で効きます。

FIG 11ディレクトリのファイル数nに対する探索コスト。線形走査 O(n) と索引付き O(log n) を並べると、nを右へ動かしたとき差が桁で開いていくのが分かります

ファイルの末尾に1ブロック追記する操作を分解します。(1) 空きブロック管理表の1ビットを「使用中」に変える、(2) データをそのブロックへ書く、(3) inodeのサイズと位置情報を書き換える。3つの別々の場所への書き込みで、しかもディスクは並べた順に処理するとは限りません。

この先にあるもの

§

ここから先は会員限定です

解説記事371本・教科書26章・学生モード48単元・論文精読6本が、月額¥490ですべて読み放題になります。新しい解説は毎日3本ずつ増えます。いつでも解約でき、解約後も期間の終わりまで読めます。

会員の方はログインすると続きが表示されます

コメント

コメントにはログインが必要です