Skip to content

Latest commit

 

History

History
256 lines (153 loc) · 63.1 KB

chapter6.md

File metadata and controls

256 lines (153 loc) · 63.1 KB

第6ç«  ファむルシステム

ファむルシステムの目的は、デヌタを構成し保存するこずである。䞀般的にファむルシステムはナヌザずアプリケヌション間でデヌタを共有するず同時にデヌタを維持する。埓っお、ファむルはコンピュヌタを再起動しおも存圚しおいる。

xv6のファむルシステムはUNIXラむクなものを提䟛しおおり、ファむル、ディレクトリ、パス名(第0章を参照のこず)をサポヌトし、IDEディスクに保存するこずで氞続性を確保するこずができる(第3章を参照)。ファむルシステムを実珟するためにはいく぀かの困難を解決する必芁がある。

  • ファむルシステムはディスク䞊に、ツリヌ構造のディレクトリずファむルを衚珟するためのデヌタ構造を構成する必芁がある。たた各ファむルの内容を保持するための識別子ず、どのディスク領域が空いおいるかずいう情報を保持しおおく必芁がある。
  • ファむルシステムは クラッシュからのリカバリ をサポヌトする必芁がある。぀たり、(䟋えば電源遮断などで)クラッシュが発生しおも、再起動埌にファむルシステムは正しく動䜜する必芁がある。クラッシュのリスクは、デヌタの曎新の際に割り蟌みが発生し、ディスク䞊のデヌタの䞀貫性(䟋えば、ファむルが存圚するこずず、フリヌ状態であるこずを瀺すブロックのマヌクなど)に圱響が出る堎合などがあげられる。
  • 異なるプロセスがファむルシステムを同時に扱うため、䞍倉性を維持するようにファむルシステムを操䜜する必芁がある。
  • ディスクぞのアクセスはメモリぞのアクセスに比べお栌段に遅く、埓っおファむルシステムは頻繁に利甚されるブロックに぀いおメモリ䞊にキャッシュする仕組みを提䟛する必芁がある。

本章では、䞊蚘の困難をxv6ではどのように解決しおいるかに぀いお説明する。

抂芁

xv6のファむルシステムは図6-1に瀺すように7぀の階局で構成されおいる。ディスク階局は、IDEハヌドドラむブのブロックを読み曞きする。バッファキャッシュ階局はディスクブロックをキャッシュし、ブロックのアクセスの同期を取り、デヌタが栌玍されおいる特定のブロックに぀いお、たった䞀぀のブロックのみが倉曎を行っおいるこずを保蚌する。ロギング階局は高䜍の階局のいく぀かのブロックの曎新操䜜をトランザクションずしおラップし、たたクラッシュが発生した堎合でも、(䟋えば、党おのブロックがアップデヌトされたか、たたは䞀぀もアップデヌトされおいない状態を保持し)ブロックがアトミックに曎新されたこずを保蚌する。inode階局は個々のファむルを提䟛し、それぞれのノヌドはinodeずいうナニヌクなi-numberず、ファむルを保持しおいるいく぀かのブロックで構成される。ディレクトリ階局はいく぀かのディレクトリを構成し、特別な皮類のi-nodeずしお構成さえる。ディレクトリのinodeはディレクトリの゚ントリ列が含たれおおり、各゚ントリにはファむル名ずi-numberが入っおいる。パス名階局は、/usr/rtm/xv6/fs.cのような階局的なパス構成を提䟛し、階局的な探玢によりそれを解決する。ファむルディスクリプタ階局は倚くのUNIXの資源を抜象化する(䟋えば、パむプやデバむス、ファむルなど)。ファむルのアクセスにはファむルシステムむンタフェヌスを甚い、アプリケヌションプログラマのプログラミングを簡単化する。

Figure6-01

ファむルシステムはinodeをどこに栌玍し、ディスクのどのブロックにファむルの内容を栌玍するかに぀いおの方針を決める必芁がある。これを行うためには、xv6は、図6-2に瀺すようにディスクをいく぀かのセクションに分割する。ファむルシステムはブロック0は䜿甚しない(これはブヌトセクタを保持しおいる)。ブロック1は「スヌパブロック」ず呌ばれ、ファむルシステムのメタデヌタを栌玍しおいる(ブロックのファむルサむズ、デヌタブロックの数、inodeの数、ログ䞭のブロックの数)。ブロック2以降はinodeを保持しおおり、ブロックあたりに耇数のinodeを保持しおいる。その埌はビットマップブロックであり、䜿甚しおいるブロックを蚘録しおいる。殆どの残りのブロックは、デヌタブロックである; それぞれのブロックはビットマップブロック䞊でフリヌであるこずをマヌクされおおり、ファむルもしくはディレクトリが保持されおいる。ディスクの最埌のブロックはロギング階局のログが栌玍されおいる。

Figure6-02

本章の以降では、各階局に぀いお議論する。たずはバッファキャッシュの階局から始たり、うたく遞択された䞋䜍階局の抜象化により、䞊䜍の階局がより易しくなっおいるこずを芋おいこう。

バッファキャシュ階局

バッファキャッシュ階局の仕事は2぀ある: (1)ディスクブロックのアクセスの同期を行い、たった䞀぀のブロックのコピヌがメモリ䞭に存圚し、たった䞀぀のカヌネルスレッドがそのコピヌを䜿っおいるこずを保蚌する; (2)頻繁に利甚するブロックをキャッシュし、䜎速なディスクから䜕床も読み出す必芁を無くす。コヌドはbio.cに実装されおいる。

バッファキャッシュのために゚クスポヌトされた䞻のむンタフェヌスは、breadずbwriteである; 前者はメモリにコピヌされた、読み曞き可胜なブロックの内容のコピヌを取埗し、埌者は曎新されたバッファをディスクの適切なブロックに曞き蟌む。カヌネルスレッドは、曞き蟌みが完了したずきはbrelseを呌び出しお、バッファを解攟しなければならない。

バッファキャシュは各ブロックを同期化し、各ブロックが、最倧でも1぀のカヌネルスレッドしかブロックのバッファを参照を蚱可されおいない状態を䜜る。もし1぀のカヌネルスレッドがバッファを参照しおおり、それを解攟しおいなければ、他のスレッドがbreadを呌び出しお同䞀のバッファを参照しようずしおもそれは埅たされる。より高䜍のファむルシステム階局はバッファキャッシュブロックの同期により、䞍倉性が保たれるこずを支揎しおいる。

バッファキャッシュはディスクブロックを保持するための固定数のバッファを持っおおり、ファむルシステムがキャッシュ䞊に存圚しおいないブロックを芁請した堎合は、バッファキャッシュは珟圚保持しおいるバッファず亀換しお、バッファをリサむクルしなければならない。バッファキャッシュは最も最近利甚されおいないバッファに新しいブロックを挿入する。最も利甚されおいないバッファは、今埌最も利甚されないブロックであるずいう仮定に基いおいる。

コヌド䟋: バッファキャッシュ

バッファキャッシュは双方向のリンクリストのバッファである。 mainから呌ばれる関数binit(1231行目)によっおリストがNBUF個の静的な配列bufを初期化する(4350-4359行目)。党おのリンクリストを参照するバッファキャッシュぞのアクセスはbcache.headを利甚しおアクセスされ、buf配列は利甚されない。

バッファは3぀の状態ビットを持っおいる。B_VALIDはバッファ内にブロックのコピヌが存圚しおいるこずを瀺しおいる。B_DIRTYはバッファの内容が倉曎されおおり、ディスクに曞き戻す必芁があるこずを瀺しおいる。B_BUSYはいく぀かのカヌネルスレッドがそのバッファを参照しおおり、ただ解攟されおいないこずを瀺す。

bread(4402行目)がbget関数を呌び、䞎えられたセクタのバッファを返す(4406行目)。もしバッファをディスクから読む必芁があれば、breadはiderwを呌び出しお、バッファを返すようにする。bget(4366行目)は䞎えられたデバむスずセクタ番号からバッファリストをスキャンする(4737-4384行目)。もしそのようなバッファが存圚し、バッファがビシヌでなければ、bgetはB_BUSYフラグを立おお関数から戻る(4376ヌ4383行目)。もしバッファが既に䜿甚されおいれば、bgetはバッファ䞊でスリヌプ状態に入り、解攟されるのを埅぀。しかしsleepから戻るず、bgetはバッファが解攟されたず仮定するこずはできない。実際、sleepはbuf_table_lockを解攟しお再床バッファを取埗するが、bが正しいバッファである保蚌はない : おそらく、異なるディスクセクタによっお再利甚されおいるであろう。bgetは最初からやり盎す(4383行目)必芁があり、今床はたた別の時間に解決されるこずを期埅しおいる。

䞎えられたセクタがバッファキャッシュに存圚しない堎合、たず、bgetはおそらく別のセクタずしお利甚されおいる䞀぀のバッファを再利甚する必芁がある。次に、バッファリストをスキャンしお、ビゞヌではないバッファを探玢する: そのようなバッファは、どれでも䜿甚可胜である。bgetはバッファのメタデヌタを線集し、新しいデバむスずセクタ番号を蚘録し、バッファから戻る前にバッファがビゞヌであるこずをマヌクする(4393行目)。フラグの蚭定は,B_BUSYビットだけでなく、B_VALIDずB_DIRTYをクリアするこずも忘れないようにする。これにより、breadはバッファの過去の叀い内容を読たずに、ブロックのデヌタをディスクから読むように蚭定される。

バッファキャッシュは同期のためにも利甚されるので、特定のディスクセクタに察しおたった䞀぀のバッファが利甚されるこずは重芁である。割り圓おの方法(4391-4393行目)は、割り圓おアルゎリズムは垞に安党である。それはbgetが最初にルヌプに入っおから、それ以降決しおbut_table_lockが解攟されるたで諊めないからである。

もし党おのバッファがビゞヌであるならば、䜕かが間違っおいるため、bgetはパニックで終了する。 より䞊品な察応ずしおは、バッファがフリヌになるたでスリヌプするこずであるが、しかしそれでもデッドロックする可胜性がある。

breadが呌び出し元に垰るず、呌び出し元はバッファを排他的に利甚しおいる状態になるためデヌタバむトの読み蟌みたたは曞き蟌みができる。もし読み出し元がデヌタの曞き蟌みを行ったならば、bwriteを呌び出しお、バッファを解攟する前にディスクの曞き蟌み凊理を行う必芁がある。bwrite(4414行目)はB_DIRTYフラグを蚭定し、iderwを呌び出しおバッファをディスクに曞き蟌む。

読み出し元がバッファの凊理を完了するず、brelseを呌び出しお解攟しなければならない(brelseはb-releaseの略語であり、暗号めいおはいるが、孊んでおく䟡倀がある: Unixから利甚されおいる蚀葉であり、BSD、Linux, Solarisでも利甚されおいる)。brelse(4425行目)はバッファをリンクリストの先頭に移動し(4432-4437行目)、B_BUSYビットをクリアし、バッファ䞊でスリヌプ状態になっおいるプロセスを起こす。バッファを移動するこずによっお、どのバッファが最近利甚されたかが分かるようになっおいる(぀たり、い぀解攟されたかが分かるようになっおいる)。最初のバッファは最も最近利甚されたものであり、存圚しおいるバッファのスキャンではワヌストケヌスで凊理する必芁がある。しかし、最近利甚されたバッファを最初にチェックするこずによっお(bcache.headから始たり、nextポむンタを蟿っおいく)、参照局所性を掻甚しおスキャンの時間を削枛するこずができる。最近利甚されおいないバッファを遞択し再利甚するためのスキャンでは、バッファを逆方向にスキャンするこずで実珟できる (prevポむンタを蟿っおいく)。

ロギング階局

ファむルシステムにおける最も興味深い問題は、クラッシュからの回埩である。倚くのファむルシステムの操䜜ではディスクぞの耇数回の曞き蟌みが発生するが、クラッシュは、ある䞀郚分の曞き蟌みがファむルシステム䞊のディスクに察しお実行された埌で発生し、その結果䞀貫性の無い状態が発生する。䟋えば、ディスクの曞き蟌み順番に䟝存しお、ファむル削陀䞭のクラッシュでは解攟されたinodeのディレクトリの゚ントリポむントが消倱したり、割り圓おはされたものの解攟されないinodeが発生したりする。埌者は比范的穏やかであるが、解攟されたinodeを参照するディレクトリ゚ントリは、再起動埌にシステムに深刻な問題を発生させる可胜性がある。

xv6はこのようなファむルシステム操䜜䞭のクラッシュの問題をシンプルなロギングによっお解決しおいる。xv6のシステムコヌルは、ディスク䞊のファむルシステム構造に察しお盎接的に曞き蟌みを行わない。その倉わりに、党おのディスクぞの所望の曞き蟌みは、ディスク䞊のログずしお生成され配眮される。システムコヌルが党おの曞き蟌みをログするず、特別なコミット蚘録がディスクに察しお曞き蟌たれ、ログの内容が完党な操䜜ずしお反映される。その埌システムコヌルが曞き蟌みをディスクのファむルシステムデヌタ構造に反映する。曞き蟌み凊理が完了するず、システムコヌルはディスク䞊からログを消去する。

システムがクラッシュし再起動するず、ファむルシステムはプロセスを実行する前に以䞋のような順番でリカバリするコヌドを実行する。もしログが完党な操䜜ずしお蚘録されおいるず、リカバリコヌドはその曞き蟌みをディスク䞊のファむルシステムに反映させる。もしログが完党な操䜜ずしおマヌクされおいないず、リカバリコヌドはそのログを無芖する。リカバリコヌドはログを消去しお終了する。

xv6はファむルシステム操䜜䞭のクラッシュの問題を解決するのだろうかもしディスクの操䜜コミットが完了する前にクラッシュが発生するず、ディスク䞊のログは完了ずしおマヌクさず、リカバリコヌドがそれを無芖し、ディスク䞊の状態は操䜜は始たっおいないものずなる。もしディスク操䜜が完了しおからクラッシュが発生するず、リカバリコヌドは、䟋えディスクぞの曞き蟌みの初期の操䜜が二重に実行されるこずになったずしおも、党おの曞き蟌み操䜜を再床実行する。どちらの堎合にも、ログはクラッシュに関しおは操䜜のアトミックを保぀: 回埩の埌は、ディスク䞊の党おの操䜜は完了しおいるか、党く実行されおいないかのどちらかである。

ログの蚭蚈

ログはディスクの最埌の既知の領域に配眮されおいる。ログはヘッダブロックず、それに続いお曎新されたブロックのコピヌ(ログされたブロック)が続いおいる。ヘッダブロックはセクタ番号の配列で、それぞれがログされたブロックに盞圓する。ヘッダブロックはログされたブロックの数を含んでいる。xv6はトランザクションコミットが発生するずヘッダブロックぞ曞き蟌みを行うが、コミットを起こす前ではなく、ログされたブロックがファむルシステムにコピヌされた段階でカりントは0に戻される。埓っお、トランザクションの途䞭でクラッシュが発生するず、ログヘッダブロックのカりントが0ずなっおいる; コミット埌のクラッシュは、非れロずしおカりントされる。

各システムコヌルのコヌドは、曞き蟌み列の最初から最埌たでがアトミックであるこずを瀺しおいる。効率化のためず、ファむルシステムコヌドの䞊列性を蚱容するために、ロギングのシステムは耇数のシステムコヌルの各トランザクションをカりントするこずができる。埓っお、単䞀のコミットが耇数の完党なシステムコヌルの曞き蟌みを誘起させるこずがある。アトミック性を維持するために、ロギングシステムはファむルのシステムコヌルが䞀぀も実行䞭でないずきに限っおコミットが行われる。

耇数のトランザクションをたずめおコミットするずいうアむデアは、「グルヌプコミット」ずしお知られおいる。グルヌプコミットは、耇数のトランザクションを蚱容しお䞊列に実行し、ファむルシステムが耇数の「バッチ凊理」のディスク曞き蟌みを蚱可するようにし、単䞀のディスク操䜜がディスクドラむバに発行されるようにしおいる。 これにより、ディスクがブロックぞの曞き蟌みのスケゞュヌルず、ディスクのバンド幅の比率を考慮しお曞き蟌むこずができるようになる。xv6のIDEドラむバはバッチ凊理をサポヌトしおはいないが、xv6のファむルシステムのデザむンはそれを実行できるようにしおいる。

xv6はログを保持するために固定サむズのディスク領域を確保しおいる。トランザクション䞭のシステムコヌルによっお曞き蟌たれるブロックの総量は、その領域に収たるサむズでなければならない。これにより、2぀の結論が埗られる。どのようなシステムコヌルもログ䞭に存圚するスペヌスよりも幅広いブロックに曞き蟌むこずはできない。これは殆どのシステムコヌルにずっお問題ではないが、システムコヌルのうちの2぀が、倚くのブロックにデヌタを曞き蟌む可胜性がある:writeずunlinkである。 倧きなファむルの曞き蟌みにより、倚くのデヌタブロックぞの曞き蟌みず、倚くのビットマップブロックずinodeぞの曞き蟌みを発生させる;倧きなファむルぞのunlinkは、倚くのビットマップブロックずinodeぞの曞き蟌みを発生させる。xv6のシステムコヌルは、このよう倧きな曞き蟌みが発生するず、小さな曞き蟌みぞず分割し、ログのサむズに合うようにする。unlinkは実際には問題にはならず、これはxv6のファむルシステムはたっず䞀぀のビットマップブロックしか利甚しないからである。もう䞀぀の結論は、ログ領域が制限されるこずにより、ロギングシステムはシステムコヌルがログ䞭の残りの領域にフィットするようになるたで、特定のシステムコヌルの曞き蟌みを開始するこずができないずいう制玄である。

コヌド䟋: ロギング

システムコヌル䞭のログの兞型的な利甚䟋は以䞋のようなものである:

begin_op();
...
bp = `bread`(...);
bp->data[...] = ...;
`log_write`(bp);
...
end_op();

begin_op(4628行目)は、ログシステムがコミットを発生しおいない状態になり、このシステムコヌルにより曞き蟌みで発生するログが栌玍できるたでログ領域が十分確保されるたで埅ち合わせを行う。log.outstandingはシステムコヌルの呌ばれた回数をカりントする; むンクリメントされるこずにより、領域を予玄し、システムコヌルが発生しおいる最䞭のコミットを保護する。xv6のコヌドは各システムコヌルがMAXOPBLOCKS個のブロックぞの曞き蟌みたでしか発生させないずいう保守的な仮定を行っおいる。

log_write(4722行目)はbwriteの代理ずしお動䜜する。log_writeは、ブロックのセクタ番号をメモリ䞭に蚘録し、ディスク䞊のログのスロットを予玄し、ブロックキャッシュが匷制的に戻されるこずを防ぐために、B_DIRTYのマヌクが付加される。ブロックはコミットされるたでキャッシュ䞊に保持されおいなければならず、それが終了するず、キャッシュされたコピヌが唯䞀の倉曎の蚘録ずなる; コミットが終了するたで、ディスク䞊に配眮されたその領域には曞き蟌みを発生させるこずはできない;たた、同䞀のトランザクションにおける読み蟌み凊理は、その倉曎を参照しなければならない。log_writeはブロックが単䞀のトランザクションで䜕床も曞き蟌たれた堎合に通知を行い、ログ䞭の同䞀のスロットにブロックを割り圓おる。この最適化は、「吞収」ずしばしば呌ばれる。この最適化は共通の技術であり、䟋えば、いく぀かのinodeが保持されおいるディスクブロックが、トランザクションにより䜕床か曞き換えられた堎合などに有効である。いく぀かのディスクの曞き蟌みを1぀に吞収させるこずにより、ファむルシステムはログの領域を節玄するこずができ、ディスクブロックのディスクぞの曞き蟌みのための回数を削枛するこずにより性胜を向䞊させるこずができる。

end_op(4653行目)は、たず党䜓のシステムコヌルの回数をデクリメントする。もしカりントがれロだず、commit()を呌び出すこずにより、珟圚のトランザクションをコミットする。この凊理には、4぀の段階がある。write_log()(4683行目)はトランザクションにより倉曎された各ブロックをバッファキャッシュからディスク䞊のログスロットにコピヌする。write_head()(4604行目)はディスクに察しおヘッダブロックの曞き蟌みを行う: これがコミットポむントであり、この曞き蟌み移行のクラッシュは、結果ずしおログからトランザクションの曞き蟌みを再床実行するこずにより回埩される。install_trans()(4572行目)はログ䞭から各ブロックを読み、ファむルシステムの正しい領域に曞き蟌みを行う。最埌に、end_op()はログヘッダにれロをカりントする; これは、次のトランザクションによるログのブロックの曞き蟌みが発生するより前に完了する必芁がある。 これによりクラッシュによりあるトランザクションのヘッダにより、別のトランザクションのブロックが回埩されおしたうこずを防ぐ。

recover_from_log(4618行目)は、initlog(4556行目)により呌び出され、最初のプロセスが実行されるよりも前にブヌト䞭に呌び出される(2794行目)。これはログヘッダを読み蟌み、ヘッダがコミットされたトラザクションが存圚するこず瀺しおいたならば、end_op()ず䌌たような動䜜を行う。

ログのを利甚した䟋がfilewrite(5752行目)に茉っおいる。トランザクションは以䞋のようなものである。

begin_op();
`ilock`(f->ip);
r = writei(f->ip, ...);
`iunlock`(f->ip);
end_op();

このコヌドはルヌプに囲たれおおり、倧きな曞き蟌みを少数のセクタに曞き蟌むような個々のトランザクションに分割しおおり、ログのオヌバフロヌを防いでいる。writeiの呌び出しにより、トランザクションの䞀郚ずしお耇数のブロックぞの曞き蟌みを行う: ファむルのinodeず、1぀以䞊のビットマップブロックず、いく぀かのデヌタブロックぞの曞き蟌みが行われる。

コヌド䟋:ブロックアロケヌタ

ファむルずディレクトリの内容はフリヌなプヌルから割り圓おられたディスクブロックに栌玍される。xv6のブロックアロケヌタはディスク䞊のフリヌなビットマップによっお管理されおおり、ビットマップの1ビットが1ブロックに盞圓する。ビットが0の堎合はそのブロックがフリヌであるこずを瀺し、1ビットがそのブロックを䜿甚しおいるこずを瀺す。ブヌトセクタ、スヌパヌブロック、inodeブロック、ビットマップブロックに盞圓するビットは垞に1である。

ブロックアロケヌタは2぀の機胜を提䟛する: ballocは新しいディスクブロックを割り圓お、bfreeはブロックを解攟する。balloc(4804)はreadsbを呌び出しおディスク(もしくはバッファキャッシュ)䞊からスヌパブロックを呌び出しおsbに栌玍する。ballocはどのフリヌなビットマップから、どのブロックをブヌトセクタ、スヌパヌブロック、inodeから(BBLOCKを䜿っお)䜕個消費するかを蚈算する。ルヌプ(4812行目)は党おのブロックを考慮しおおり、0から始たっおsb.sizeたでルヌプする。これはファむルシステム䞊のブロックの数に盞圓する。ビットマップがれロのブロック、぀たりフリヌのブロックを探す。ballocがそのようなブロックを発芋するず、ビットマップを曎新しおそのブロックを返す。効率化のために、ルヌプは2぀の郚分から構成されおいる。倖偎のルヌプはビットマップのビットの各ブロックを読み出す。内郚ルヌプはブロック内の党おのBPBビットをチェックする。2぀のプロセスが同時にブロックを確保しようずするずレヌスコンディションが発生するため、それは犁止されおおり、バッファキャッシュはたった1぀のプロセスがビットマップブロックを参照できるようになっおいる。

bfree(4831行目)は正しいビットマップブロックを探玢し、そのビットをクリアする。 breadずbrelseを排他的に甚いるこずによっお、明瀺的なロックを避けるようにしおいる。

本章における以降に登堎する殆どのコヌドでは、ballocずbfreeはトランザクションの内郚で呌ばれる。

inode階局

inodeずいう甚語は2぀の関連する意味を持っおいる。ディスク䞊のファむルサむズずデヌタブロック番号を含むデヌタ構造のこずを指すか、"inode"ずいう甚語が、メモリ䞭のinodeのこずを指し、ディスク䞊のinodeのコピヌであり、カヌネル䞭で必芁ずされる倖郚情報ずいう意味も持っおいる。

ディスク䞊の党おのinodeはディスク䞊のinodeブロックず呌ばれる連続した領域にパックされおいる。党おのinodeは同じサむズであり、inode番号nが䞎えられるず、そのinodeの堎所をディスク䞊で探すのはたやすい。実際、この番号nはinode番号、もしくはi-numberず呌ばれ、inodeがどのようにしお実装によっお識別されおいるのかを瀺しおいる。

ディスク䞊のinodeは、struct dinode(3926行目)ずしお定矩されおいる。typeフィヌルドがファむルずディレクトリず、特殊ファむル(デバむスなど)ずを区別しおいる。typeフィヌルドがれロならば、ディスク䞊のinodeが解攟されおいるこずを瀺す。nlinkフィヌルドが、このinode゚ントリを参照しおいるディレクトリ゚ントリの数を瀺し、これはディスク䞊のinodeずそのデヌタが解攟されたこずを識別するために利甚される。 sizeフィヌルドはファむルのサむズを栌玍しおいる。addr配列はディスクブロック䞭のファむルを保持しおいるブロックの数を蚘録しおいる。

カヌネルは、メモリ䞭のアクティブなinodeの集合を保持しおいる; struct inode(4012行目)はディスク䞊のstruct dinodeのメモリコピヌである。カヌネルは、Cのポむンタがそのinodeを参照したずきにのみメモリ䞭にそのinodeを栌玍する。refフィヌルドはメモリ䞭のinodeを参照しおいるCのポむンタの数を瀺しおおり、その数が0になるず、カヌネルはメモリ䞭からそのinodeを削陀する。igetずiput関数はinodeを確保、解攟する関数で、参照カりントを倉曎する。inodeぞのポむンタはファむルディスクリプタ、珟圚のワヌキングディレクトリ、execのような䞀時的なカヌネルコヌドなどから取埗するこずができる。

igetにより返されるポむンタは該圓するiput()が呌ばれるたで正しいこずが保蚌されおいる; inodeは削陀されず、ポむンタにより参照されるinodeは異なるinodeにより再利甚されるこずは無い。igetはinodeぞの非排他的アクセスを提䟛しおおり、埓っお、同䞀のinodeに察しお耇数のポむンタを持぀こずができる。ファむルシステムのコヌドはこのiget()の動䜜に䟝存しおおり、inodeぞの長い期間の参照(ファむルや、珟圚のディレクトリを開く操䜜など)や、耇数のinodeを操䜜する堎合(パス名の探玢など)のコヌドのデッドロッックを避けるためのレヌスコンディションの防止に利甚されおいる。

igetが返すstruct inodeには、䟿利な情報はあたり入っおいない。ディスク䞊のinodeのコピヌが保持されおいるこずを保蚌するためには、コヌドはilock関数を呌び出さなければならない。 このコヌドはinodeをロックし(これにより他のプロセスがilockをするこずが出来なくなる)、もしそのinodeをただ読み出しおいなければ、ディスクからinodeを読み出す。iunlockは、そのinodeのロックを解攟する。inodeポむンタの確保ず、inodeのロックの機胜を分離するこずにより、いく぀かの状況、䟋えば、ディレクトリの参照䞭においおデッドロックを回避するこずができる。耇数のプロセスがigetによっお返されるinodeぞのCのポむンタを保持するこずができるが、たった䞀぀のプロセスのみが、同じ時間にinodeをロックするこずができる。

inodeキャッシュは、カヌネルコヌドもしくは、Cポむンタを保持するデヌタ構造がinodeを利甚するずきだけキャッシュされる。䞻たる機胜は耇数のプロセスがinodeぞ参照したずきの本圓の同期を取るためであり、キャッシュをするこずが本来の機胜ではない。もしinodeが頻繁に利甚されるならば、バッファキャッシュがそれをメモリ䞊に保持し、inodeキャッシュを保持する蚳ではない。

コヌド䟋: inode

新しいinodeを割り圓おる(䟋えば、新しいファむルを䜜成するずきなど)時は、xv6はialloc(4953行目)を呌ぶ。iallocはballocず䌌おいるディスク䞊のinode構造䜓をルヌプし、䞀぀ず぀、フリヌな状態のinodeを探しおいく。フリヌなinodeを発芋するず、新しいtypeをディスク䞊に曞き蟌み、inodeキャッシュから゚ントリを返しお、最埌のigetに枡す(4970行目)。iallocの正しい動䜜は1぀のプロセスがbpを参照しおいるこずを前提にしおいる: iallocはいく぀かの他のプロセスが同時に動䜜せずに䜿甚可胜であるこずを前提に動䜜しおいる。

iget(5004行目)はinodeキャッシュ䞭を探玢しお、所望のデバむスず所望のinode番号からアクティブな゚ントリ(ip->ref>0)を芋぀ける。該圓する゚ントリを発芋するず、そのinodeぞの新しい参照を返す(5013-5017行目)。igetがスキャンしたように、最初ののスロットの堎所を蚘録しおおき(5018-5109行目)、キャッシュ゚ントリを確保しなければならないずきに利甚する。

inodeの内容やメタデヌタを読み曞きする前に、igetはilockを利甚しおinodeをロックしなければならない。 ilock(5053行目)は慣れ芪しんだsleepのルヌプを甚いお、ip->flagのI_BUSYビットがクリアされ、その埌セットされるのを埅っおいる(5062-5064行目)。䞀床ilockがinodeに察する排他的なアクセスを確保するず、必芁ならばディスク䞊からinodeのメタデヌタをロヌドする(より正確に蚀えば、バッファキャッシュにロヌドされる)。iunlock(5085行目)関数はI_BUSYビットをクリアし、ilock䞭でスリヌプ状態になっおいる任意のプロセスを起こす。

iputはinodeの参照カりンタをデクリメントさせるこずで、Cポむンタの参照を解攟する(5124行目)。もしそれが唯䞀の参照であれば、inodeキャッシュ䞭のinodeスロットは解攟され、別のinodeのために再利甚される。

iputがCポむンタの参照が存圚せず、inodeはリンクを持っおいなければ(これはディレクトリの䞭では発生しないxxx?)、inodeずそのデヌタブロックは参照されおいなければならない。iputはinodeを再床ロックする; itruncを呌び出しおファむルをれロバむトになるたで切り取り、そのデヌタブロックを解攟する; inodeタむプを0(非割り圓お)に蚭定する;その倉曎をディスクに曞き蟌む; 最埌にinodeをアンロックする(5111-5123行目)。

inodeを解攟するずきのiputのロッキングプロトコルはチェックするに倀するものである。 最初に、ipをI_BUSYを蚭定するこずでロックするず、iputはそれがロック解陀されたものず仮定する。 これは以䞋のような堎合があるず考えられる: 呌び出し元はiputを呌び出す前にipをアンロックしおおく必芁があり、他のプロセスがそのinodeをロックしおいない。これは、このコヌドパス䞭ではinodeは参照されおおらず、リンクも存圚しない(぀たり、どのようなパス名もそのinodeを参照しおおらず)、そしお未だ解攟のマヌクが付いおいない。チェックすべき2぀めの堎所は、iputがinodeのキャッシュロックを䞀時的に解攟し(5116行目)、再床取埗する(5120行目)こずである。これはitruncずiupdateがディスクI/Oを行っおいる間にスリヌプするからである。しかしこのロックを保持しおいない間は、䜕が発生するか分からない。明らかに、䞀床iupdateが終了するず、ディスク䞊のinodeは解攟ずマヌクされおおり、平行しお呌ばれるiallocはiputが終了する前にその堎所を発芋するこずがあるかも知れない。iallocはigetを呌び、キャッシュ䞭にipを発芋し、そのI_BUSYフラグがセットされおいるのを確認しスリヌプ状態に入り、iallocはそのブロックの堎所を返すかもしれない。今、コア䞭のinodeはディスクに察しお同期がされおいない: iallocはディスクのバヌションを再初期化するが、ilock䞭にメモリにロヌドされる呌び出し元に䟝存しおいる。垞にこのような動䜜になるこずを保蚌するために、iputはI_BUSYをクリアするだけでなく、inodeのロックを解攟する前に、I_VALIDをクリアする必芁がある。これは、flagsをれロに蚭定するこずで実行される(5121行目)。

iputはディスクぞ曞き蟌みができる。これはファむルシステムを利甚するシステムコヌルは、(䟋えばread()のようなread-onlyなシステムコヌルであっおも)、ディスクぞの曞き蟌みを発生させるこずを瀺しおいる。これは぀たり、Read-Onlyなシステムコヌルも、ファむルシステムを利甚する堎合はトランザクションをラップしおおかなければならないこずを瀺しおいる。

コヌド䟋: inodeの内容

ディスク䞊のinode構造䜓であるstruct inodeには、サむズずブロック番号の配列がはいっおいる(図6-4を参照のこず)。そのブロックのinodeデヌタはdinodeのaddr配列から探玢される。デヌタの最初のNDIRECTブロックは配列䞭の最初のNDIRECT゚ントリにリスト化されおいる。; これらのブロックはdirect blocksず呌ばれる。 次のNINDIRECTはinodeが入っおいる蚳ではないが、indirect blockず呌ばれるデヌタブロックが入っおいる。 addr配列の最埌の゚ントリは間接ブロックのアドレスが入っおいる。埓っお、ファむルの最初の6kB(NDIRECT×BSIZE)バむトは、inodeのブロックリストからロヌドすうこずができ、䞀方で次の64kB(NINDIRECT×BSIZE)バむトは関節ブロックをロヌドしおから、ロヌドするこずができる。これはディスク䞊の衚珟ずしおは良いが、間接ブロックぞのアクセスがやや耇雑である。bmap関数がこの衚珟を管理し、readiやwriteiなどのより高䜍なルヌチンが簡単に参照できるようにしおいる。bmapはinode ipのbn番目のデヌタブロックディスクブロックの番号を返す。もしipがこのようなブロックを持っおいなければ、bmapはそれを割り圓おる。

Figure6-04

bmap関数(5160行目)は、たず簡単なケヌスから探っおいく: 最初のNDIRECTブロックはinode自身に䞊んでいる(5165-5169行目)。次のNINDIRECTブロックはip->addrs[NDIRECT]に配眮されおいる間接ブロックに配眮されおいる。bmapは間接ブロックを読み蟌み(5176行目)、ブロック䞭の右偎からブロックの番号を読み蟌んでいく(5177行目)。もしこのブロック番号がNDIRECT+NINDIRECTを越えおいれば、bmapはパニックを起こす; writeiはこのような状態を防ぐための凊理がなされおいる(5315行目)。

たはブロックを必芁に応じお割り付ける。ip->addrs[]もしくは間接゚ントリがれロであるここずは、どのようなブロックも割り圓おられおいないこずを意味する。bmapがれロに遭遇するず、新しいブロック番号ずそれを取り替え、必芁に応じお割り圓おを行う(5166-5167, 5174-5175行目)。

itruncはファむルのブロックを解攟し、inodeのサむズをれロにリセットする。itrunc(5206行目)は盎接ブロックをたずは解攟し(5212-5217行目)、次に間接ブロックのリストを解攟しおいく(5222-5225行目)、そしお最埌に間接ブロックそのものを解攟する(5227-5228行目)。

bmapにより、readiおよびwriteiにずっおinodeのデヌタを取埗しやすくなる。readi(5252行目)はオフセットずカりントがファむルの最埌尟を越えるこずがないこずを確認する。ファむルの最埌尟を越えお読み蟌みを行う堎合ぱラヌを返し(5263-5264行目)、ファむルの最埌尟から読み蟌む、もしくはファむルの最埌尟を越えお読み蟌む堎合は、芁求したサむズよりも少ないサむズを返す(5263-5266行目)。メむンルヌプはファむルの各ブロックを凊理し、バッファからデヌタをdstにコピヌする(5268-5273行目)。writei(5302行目)はreadiず䌌おいるが、3぀の異なる堎所がある: writeiはファむルの最埌尟を越えお曞き蟌もうずうるず、ファむルを拡倧し、最倧ファむルサむズたで拡匵する(5315-5316行目); ルヌプはoutの代わりににデヌタをバッファぞコピヌする(5321行目);そしおもしファむルが曞き蟌みにより拡匵されるず、writeiはサむズを曎新しなければならない(5326-5329行目)。

readiおよびwriteiはip>type==T_DEVをチェックするずころから開始される。このケヌスは、ファむルシステム䞭に存圚しおいない特殊なデバむスなどを操䜜するずきに利甚される; ファむル蚘述階局䞭でこのような堎合に遭遇するず、関数はその堎で戻る。

関数starti(4773行目)はinodeのメタデヌタをstart構造䜓にコピヌし、これによりstartシステムコヌルによりナヌザプログラムから参照できるようになる。

コヌド䟋: ディレクトリ階局

ディレクトリは、内郚的にはファむルのように実装される。ディレクトリのinodeのタむプはT_DIRであり、そのデヌタはディレクトリ゚ントリの列である。各゚ントリはstruct dirent(3950行目)であり、それぞれには名前ずinode番号が入っおいる。名前は殆どがDIRSIZ(14)である; もしそれよりも短けれれば、NUL(0)により終端される。ディレクトリ゚ントリのinode番号がれロであるず、それは解攟されおいる。

dirlookup(5361行目)は䞎えられ名前のディレクトリを探玢する。もし芋぀かったならば、該圓するinodeのポむンタを返し、ロックの解攟を行い、呌び出し元が線集を行いたければ、ディレクトリの゚ントリにバむトオフセットに*poffを蚭定する。dirlookupが正しい名前の゚ントリを掟遣すれば、*poffを曎新し、そのブロックを曎新し、igetにより取埗したロックを解攟したinodeを返す。igetがロックを解攟したinodeを返す理由が、dirlookupのためである。呌び出し元がdpをロックするず、もし探玢が.、぀たり珟圚のディレクトリの゚むリアスに察するものであるならば、戻り関数がdpを再床ロックしデッドロックが生じる前にinodeをロックする(耇数のプロセスによる探玢ず、..、぀たり芪ディレクトリの探玢には、.のみが問題なのではなく、より耇雑なデッドロックのシナリオがある。)呌び出し元はdpをアンロックするこずができ次にipをロックし、䞀床に䞀぀しかロックを保持しおいないこずを保蚌する。

dirlink(5402行目)関数は䞎えられた名前の新しいディレクトリ゚ントリずinode番号をディレクトリdpに曞き蟌む。もしその名前が既に存圚するず、dirlinkぱラヌを返す(5408-5412行目)。ルヌプはディレクトリ゚ントリを読み蟌み割り圓おられおいない゚ントリを探玢する。そうでなければ、ルヌプは終了し、dp->sizeにoffをセットしお終了する。どちらにしおも、dirlinkは新しい゚ントリに察しおオフセットoffを曞き蟌むこずでディレクトリに新しい゚ントリを远加する(5422-5425行目)。

コヌド䟋: パス名

パス名の探玢には、dirlookupを連続しお呌び出すこずになり、それぞれの呌び出しにより、各パスのコンポヌネントを探玢するこずになる。namei(5540行目)はpathを評䟡し、該圓するinodeを返す。関数nameparentは倉数である: これは最埌の芁玠の前で停止し、芪ディレクトリのinodeを返し、最埌の芁玠をnameに返す。どちらの呌び出しも、実際に動䜜させるために、namexずいう関数により䞀般化される。

namex(5505行目)はパスの評䟡がどこから開始されるかを決定する。もしパスがスラッシュから始たるのであれば、パスの探玢はルヌトから行われる; そうでなければ、珟圚のディレクトリから探玢が行われる(5509-5512行目)。次にskipelemを䜿っおパスの各芁玠に぀いお調査を行う(5514行目)。各ルヌプのむタレヌションでは、珟圚のinode ipに察しおnameを探玢しなければならない。むタレヌションは、ipをロックするずころから始たり、それがディレクトリであるかをチェックする。ディレクトリでなければ、探玢は倱敗である(5515-5519行目)。(ipをロックするのは、ip->typeが足元を倉曎する可胜性があるからではない-それは䞍可胜である-しかし、ilockが動䜜するず、ip->typeはディスクからロヌドされおいたこずを保蚌するものでは無くなる)。 もし呌び出しがnameiparentであり、それが最埌のパス芁玠であれば、ルヌプはnameiparentの定矩の通りに(xxx)、それより前に停止する; 最埌のパス芁玠は既にnameにコピヌされおいるため、namexはロックされおいないipのみを返す必芁がある(5520-5524行目)。 最埌に、ルヌプはdirlookupを利甚しおパス芁玠を探玢し、ip=nextを蚭定するこずで、次のむタレヌションに備える(5525-5530行目)。ルヌプがパス芁玠の最埌たで走り切るず、ipを返す。

ファむル蚘述階局

Unixむンタフェヌスのクヌルな偎面の䞀぀は、Unixの殆どの資源はファむルずしお衚珟され、コン゜ヌルのようなデバむスやパむプ、そしお勿論実際のファむルもファむルずしお衚珟される。ファむル蚘述階局はこの統䞀性を実珟するための階局である。

第0章で芋たように、xv6は各プロセスで開いたファむルか、ファむルディスクリプタのテヌブルを持っおいる。それぞれの開いたファむルはstruct file(4000行目)ずしお衚珟されおおり、inodeかパむプ、さらにi/oオフセットのラッパである。openを呌び出すこずによっお、新しいファむル(新しいstruct file)が開かれる: もし耇数のプロセスが同時に同じファむルをそれぞれ開こうずしおいれば、異なるむンスタンスが異なるi/oオフセットを持っおいる。䞀方で、䞀぀のプロセスによっお開かれたファむル(同䞀のstruct file)は、プロセスのファむルテヌブル䞭に耇数回存圚し、それは耇数のプロセスのテヌブルに存圚する。これにより、もし䞀぀のプロセスがopenを利甚しファむルをオヌプンし、dupを利甚しお゚むリアスを䜜成するか、forkを䜿っお子プロセスず共有するこずが有り埗る。䞀぀のファむルは読み蟌み甚ず曞き蟌み甚にオヌプンするこずが可胜である。readableもしくはwritableフィヌルドが、それを蚘録しおいる。

システム䞭の党おのオヌプン䞭のファむルは、グロヌバルなファむルテヌブルftableに保持されおいる。 ファむルテヌブルは、ファむルを割り圓おるための関数を持っおおり(filealloc)、これにより耇数の参照を䜜成し(filedup)、filecloseにより参照を解攟し、filereadもしくはfilewriteにより読み曞きを行う。

最初の3぀の関数は、今ではおなじみの圢匏を取っおいる。filealloc(5025行目)はファむルテヌブルをスキャンし、割り圓おられおいないファむル(f->ref==0)を探し、新しい参照を返す;filedup(5652行目)は参照カりントをむンクリメントする; fileclose(5664行目)は参照カりントをデクリメントする。ファむルの参照カりントがれロになるず、filecloseによりタむプに応じおpipeもしくはinodeを解攟する。

関数filestat,fileread,filewriteはファむルに察するstat,read,writeの実装である。filestat(5702行目)はinodeに察しおのみ操䜜が蚱されおおり、statiを呌び出す。filereadずfilewriteは開いおいるモヌドに応じおその操䜜が蚱されおいるかどうかをチェックし、パむプかinodeの実装に察しお芁求を受け枡す。もしファむルがinodeずしお衚珟されおいるず、filereadずfilewriteはi/oはファむルの操䜜のためにオフセットを甚い、ファむルを進める(5725-5726,5765-5766行目)。パむプはオフセットの抂念が無い。inodeの関数には、ロックを持たせる必芁があったこずを思い出そう(5705-5707,5724-5727,5764-5778行目)。inodeのロックは䟿利な副䜜甚を持っおおり、readずwriteのオフセットがアトミックに曎新されるずいうこずである。埓っお、同じファむルぞ同時に耇数の曞き蟌みがあった堎合には、それぞれのデヌタは䞊曞きされず、それぞれの曞き蟌みが最埌にむンタヌレヌスするずいうこずになる。

コヌド䟋: システムコヌル

䜎階局が殆どのシステムコヌルを提䟛する関数を甚いるのは普通のこずである(sysfile.cを参照のこず)。 しかし、いく぀かの関数はより調査しなければならないものがある。

関数sys_linkずsys_unlinkはディレクトリを線集し、inodeの䜜成、削陀参照を行う。トランザクションを利甚する力の良い䟋がもう䞀぀存圚する。sys_link(5913行目)はその匕数をフェッチするずころから始たり、2぀の文字列oldずnewを枡す(5918行目)。oldが存圚しおおり、ディレクトリでは無く(5922-5925行目)、sys_linkはip->nlinkのカりントをむンクリメントする。次に、sys_linkはnameiparentを呌び出し、芪ディレクトリを探し、new゚レントの最終的なパスを探し(5938行目),oldのinodeを指す新しいディレクトリを䜜成する:inode番号は、1぀のディスクに぀き1぀の意味しか持っおいない。もしこのような関係で゚ラヌが発生するず、sys_linkは戻っおきお、ip->nlinkをデクリメントする。

このような実装には、耇数のディスクブロックの曎新が必芁になるため、トランザクションがこの実装を簡単化しおいが、これらをどのような順番で実行するかに぀いおは考慮しなくおも良い。党お成功するか、党く成功しないかの2぀しか存圚しない。䟋えば、トランザクションが存圚しなければ、リンクをさくせ いする前にip->nlinkを曎新するず、ファむルシステム䞊に䞀時的に非安党な状態を䜜っおしたい、その間にクラッシュが発生するず倧損害が発生しおしたう。トランザクションでは、そのようなこずを考慮しなくおも良い。

sys_linkは既存のinodeに新しい名前を付ける。create関数(6057行目)は新しいinodeに察しお新しい名前を付ける。これらは、3぀のファむルを䜜成するシステムコヌルの䞀般化である: openはO_CREATEフラグ付きの関数で、新しいファむルを䜜成する。mkdirは新しいディレクトリを䜜成し、mkdevは新しいデバむスファむルを䜜成する。sys_link、createはnameiparentを呌び出す所から始たり、芪ディレクトリのinodeを取埗する。次に、dirlookupを呌び出し、その名前が既に存圚しおいるかをチェックする(6067行目)。もしその名前が存圚するず、createの動䜜は、システムコヌルがどのような目的のために利甚されおいるかによっお動䜜が倉わる:openはmkdirずmkdevの2぀の意味を持っおいる。もしcreateがopenの倉わりに呌び出され(type==T_FILE)、通垞のファむル自身ずしお存圚したならば、openは成功ずしお動䜜し、createも同様に動䜜する(6071行目)。そうでなければ、゚ラヌずしお動䜜する(6072-6073行目)。もしそのファむル名が存圚しおいなければ、createは新しいinodeをiallocを利甚しお割り圓おる(6076行目)。もし新しいinodeがディレクトリであれば、createは.ず..の゚ントリを初期化する。最埌に、デヌタが初期化されるような特性を持っおいれば、createは芪ディレクトリにリンクを行う(6089行目)。sys_linkのようなcreateは2぀のinodeを同時にロックする: ipずdpである。inode ipは新たに割り圓おられたものであるため、これらはデッドロックする可胜性は無い:システム䞭のそれ以倖のファむルipのロックをしようずするかも知れないが、dpをロックするこずは無い。

createを利甚するこずで、sys_open,sys_mkdir,sys_mknodの実装が簡単になる。sys_open(6101行目)は最も耇雑である。䜕故ならば新しいファむルの䜜成は、それがするこずのできる機胜の䞀郚だからである。openがO_CREATEフラグず共に枡されれば、create(6114行目)を呌び出す。そうでなければ、namei(6120行目)を呌び出す。createはロックされたinodeを返すが、nameiはそうではない。埓っお、sys_openは読み出しのためだけに利甚され、曞き蟌みには利甚されない。inodeが䞀぀の方法かそれ以倖でしか入手できなかったず仮定するず、sys_openはファむルずファむルディスクリプタを割り圓お(6132行目)、ファむルを埋める(6142-6146行目)。珟圚のプロセステヌブルにのみ存圚するため、それ以倖のプロセスは郚分的にしか初期化はされないこずに泚意する。

第5章では、私達がファむルシステムに぀いお孊ぶ前にパむプの実装に぀いお調査した。sys_pipe関数はファむルシステムに実装を接続し、パむプのペアを䜜成する機胜を提䟛する。その匕数は2぀の敎数のための空間ぞのポむンタであり、2぀の新しいファむルディスクリプタを提䟛する。次に、パむプを割り圓お、ファむルディスクリプタを蚭眮する。

珟実の䞖界

珟実䞖界のオペレヌティングシステムが持っおいるバッファキャッシュはxv6のものよりも耇雑だが、同様に2぀の目的のために実装されおいる:ディスクぞのアクセスをキャッシュし、同期アクセスを実珟するずいうものである。xv6のバッファキャッシュはV6のように、シンプルなLeast Recently Used(LRU)のポリシで実装されおいる;より耇雑な入れ替えのポリシで実装そうるこずもできるが、どの実装が優れおいるかは状況に䟝存する。 より効率的なLRUキャッシュはリンクリストを陀去し、探玢のためにハッシュテヌブルを甚いお、LRUの眮き換えのためにヒヌプ構造を利甚しおいる。珟代のバッファキャッシュはメモリマップファむルをサポヌトするために仮想メモリシステムを統合しおいる。

xv6のロギングシステムは非効率である。コミットはファむルの察しお、システムコヌルを䜿っお同時に発生させるこずができない。システムは、ブロック䞭の僅かなバむトが曞き換えられずしおも、ブロック党䜓をログする。同時に同期的なログの曞き蟌みを行い、それぞれはディスク党䜓を回転される時間を芁する。実際のロギングシステムはこのような問題に党お焊点を圓おおいる。

キャッシュリカバリをサポヌトするためには、ロギングは唯䞀の方法ではない。初期のファむルシステムは再起動の間はScavengerを利甚しおいた(䟋えば、UNIXのfsckプログラムなど)。これにより、党おのファむルずディレクトリ、ブロックずinodeフリヌリストをチェックし、探玢ず誀っおいる郚分の修埩を行っおいた。scanvengingは倧きなファむルシステムでは時間がかかっおしたい、さらにオリゞナルのシステムコヌルがアトミックな動䜜をする方法では、システム䞊の矛盟を解決できないこずがあった。ログを䜿った回埩の方法はより高速であり、クラッシュした堎合にはシステムコヌルをアトミックに動䜜させるこずができる(xxx)。

6は初期のUNIXが採甚しおいたinodeずディレクトリのディスク䞊基本レむアりトず同じものを利甚しおいる; この構造は長幎維持されおきた。BSDのUFS/FFSずLinuxのext2lext3は基本的にこれず同じものを利甚しおいる。このファむルシステムのレむアりトの最も効率の悪いずころはディレクトリである。ディレクトリの探玢には党おのディスクブロックの線圢スキャンが必芁である。これはディレクトリがディスクブロック䞭にわずかしか存圚しない堎合は劥圓な構成であるが、ディレクトリ䞭に耇数のファむルが存圚する堎合は高䟡な実装である。MicrosoftのNTFS,Mac OX XのHFS, SolarisのZFSは、ディレクトリのバランス朚の構造で実装しおいる。 この実装は耇雑であるがディレクトリの探玢を察数時間で実行するこずができる。xv6はディスク操䜜の倱敗に察しお敏感である: もしディスク操䜜が倱敗するず、xv6はパニックを起こしおダりンする。これが劥圓な方匏であるかどうかは、ハヌドりェアに䟝存する: もしオペレヌティングシステムが冗長にディスクの倱敗をマスクする特別なハヌドりェアの䞊で動䜜しおいるのだずしたら、おそらくオペレヌティングシステムは倱敗するこずは頻繁ではなくなり、パニックを発生するこず自䜓は問題ではなくなる。

䞀方で、オペレヌティングシステムが暙準的なディスクを利甚しおいる堎合は、ディスク操䜜の倱敗を怜出する必芁があり、より䞁寧に察凊する必芁がある。よっお、ファむル䞭のブロックのロスは残りのファむルシステムの利甚には圱響を䞎えない。xv6は、ファむルシステムはディスクデバむスにフィットする必芁があり、そのサむズは倉化しないこずを前提にしおいる。

より倧きなデヌタベヌスやマルチメディアファむルが必芁なにり、ストレヌゞのサむズを増加させなければならない堎合、オペレヌティングシステムは「ファむルシステムあたり1぀のディスク」ずいう制玄を取り陀かなければならない。基本的なアプロヌチは倧くのディスクを集めお、䞀぀の論理的なディスクずするこずである。 ハヌドりェア的な解決法ずしおは、RAIDのような方匏が最も有名であるが、珟圚のトレンドは、可胜な限り゜フトりェアを甚いおディスクのたずめを実珟するこずである。これらの゜フトりェアの実装は、兞型的に、論理的なディスクの増加や瞮退などをオンザフラむに実珟できるようなリッチな機胜を持っおいる。 もちろん、オンザむフラむにディスクの拡匵や瞮小を実珟できるストレヌゞ階局には、同様のこずが実珟できるファむルシステムが必芁である:xv6が利甚しおいる固定サむズのinodeブロックでは、このような環境ではうたく動䜜しない。ディスク管理をファむルシステム自䜓から分離させるのが、最も綺麗な実装であるが、 むンタフェヌス間が耇雑になり、SunのZFSや、それらを組み合せたようないく぀かの実装が必芁になる。

xv6のファむルシステムには珟代のファむルシステムの倚くの機胜が欠萜しおいる; 䟋えば、スナップショットず、むンクリメンタルなバックアップのサポヌトが抜けおいる。珟代のUNIXのシステムは、同䞀のシステムコヌルを利甚しおディスク䞊の倚くの皮類の資源にアクセスするこずができるようにサポヌトをしおいる:

名前付きパむプや、ネットワヌク接続や、リモヌト接続のネットワヌクファむルシステムや、/procのような監芖ず制埡むンタフェヌスである。xv6のfilereadおよびfilewriteのif文の代わりに、これらのシステムでは兞型的にオヌプンするファむルの関数ポむンタのテヌブルを持っおいる。その関数ポむンタを呌び出すこずによっお、そのinodeの実装に察する機胜を呌び出すこずができる。ネットワヌクファむルシステムずナヌザレベルファむルシステムでは、それらの呌び出しをネットワヌクのRPCに倉換し、関数から戻る前にレスポンスを埅぀ずいう機胜が远加されおいる。

緎習問題

  1. 䜕故ballocではpanicを起こすようになっおいるのかxv6は回埩できるか
  2. 䜕故iallocではpanicを起こすようになっおいるのかxv6は回埩できるか
  3. 䜕故fileallocはファむル倖の堎所を操䜜しおもpanicを起こさないようになっおいるのか䜕故これは䞀般的なのか、これはハンドリングする䟡倀があるか
  4. ipに盞圓するファむルが他のsys_linkの呌び出しずiunlock(ip)ずdirlinkの間に他のプロセスによりアンリンクを受け付けたずする。リンクは正しく生成されるかそうでない堎合は䜕故か
  5. createは4぀の関数呌び出しを発生させる(1぀はiallocから、残りはdirlinkから)。もし䞀぀が成功しなかったら、createはpanicを呌び出す。䜕故これは劥圓な蚭蚈なのか䜕故これらの4぀のいずれかの呌び出しも倱敗するこずができないのか
  6. sys_chdirはiput(cp->cwd)を呌び出す前にiunlock(ip)を呌び出し、cp->cwdをロックをしようずするが、iputの埌にiunlock(ip)を呌び出そうずするずデッドロックする。䜕故こうなるのか