UNIXファイルシステム
今日のUnixファイルシステム(UNIXファイルシステム、UFS)は、バークレー高速ファイルシステムで始まりました。そして、ファイルシステム、UNIXのファイルシステムのすべては、ブロック(ブロック)は、ディスクの読み取りと書き込みの単位としてです。一般に、ブロックのサイズは512バイトまたは4キロバイトです。ファイル・システムのすべてのデータ構造は、ハード・ディスク上のブロック単位で記憶されている、いくつかの典型的なデータブロックは、前記スーパーブロック、iノード、データ・ブロック、ディレクトリブロックと間接ブロック。
スーパーブロックは、タイプ、サイズ、状態、ファイルシステムのデータ構造に関する他の情報のファイルシステムとして、ファイルシステム全体(メタデータ)に関するメタ情報を含みます。スーパーブロックファイルシステムは、このように多くのスーパーブロックのコピーを保存しますUNIXのファイルシステムを実現し、非常に重要です。
抽象iノードは、UNIXファイル・システムのファイルを表すために使用されるデータ構造です。iノードは、抽象的にだけでなく、「ファイル」ハードドライブ、ディレクトリ上のデータの集合を意味し、そのようなIOなどの外部機器を表すために、inodeデータ構造を使用します。iノードは、このようなように、所有者、アクセス許可、ファイルの種類、およびなど、ファイルのメタ情報が含まれています。ファイルシステム内のすべてのファイルの場合、ファイルシステムは、1つのまたは複数のディスク・ブロックを占めることができるiノードリストを、保持しています。
データブロックは、実際のファイルデータを格納するために使用されます。そこにいくつかのファイルシステムストアディレクトリディレクトリブロックや間接ブロックするために使用することができるが、Unixファイルシステムブロックでこれらのファイルをデータとして扱われ、そのiノードによって機能させるには、上のファイルシステム、彼らの唯一の違いは、iノードでありますプロパティレコードが異なります。
おそらく同じXv6ファイルシステムの設計とUnixが、詳細は簡素化以上です。基本となる実装では、xv6は、Linuxを使用して実装し、同様のアイデアは、デバイスとIOモードの様々なサポートするために、段階的に上向きのパッケージの層を層別化。Xv6ファイルシステムが床にログインし、ディスクIO層が含まれ、その導入後にiノード層、ファイル層とシステムコールレベルは、実装が続きます、
ディスクIO Xv6
ディスクIOでXv6 ide.c
プログラムされたIOのためのディスクベースのIDEである、達成することは簡単です。Xv6ディスクは、以下に代表されるデータ構造体の要求を読み書き
struct buf {
int flags;
uint dev;
uint blockno;
struct sleeplock lock;
uint refcnt;
struct buf *prev; // LRU cache list
struct buf *next;
struct buf *qnext; // disk queue
uchar data[BSIZE];
};
ここで、当該ドメインである必要はIDEディスク用flags
(DIRTY、VALID)、 dev
(機器)、 blockno
(ディスクブロック番号)とnext
(キューポインタの次のメンバーを指します)。
ディスクを実現するためのアイデアを読み書きはこれです:Xv6は(ディスク操作処理要求キューを維持しますidequeue
)。処理ディスク読み取りおよび書き込み要求を、要求がキューに追加されます場合は、プロセスがスリープ状態になります(iderw()
)。いつでも、キューの先頭には、現在進行中のディスクの読み取りおよび書き込み要求を表します。ディスク操作の読み取りおよび書き込みが完了すると、それが割り込み、割り込みハンドラを(トリガーされますideintr()
)キューウェイクアップ要求は、プロセスの開始に対応し、待ち行列の先頭の要求を削除します。後続の要求があった場合、キューの先頭に移動され、ディスク要求処理が開始されます。
ディスク宣言要求キューは、次のように、当然のことながら、彼らのロック訪問しなければならないです。
static struct spinlock idelock;
static struct buf *idequeue;
ide.c
に対応する機能としての機能
関数名 | 機能 |
---|---|
idewait() |
ディスクがアイドル状態に入るのを待ちます |
ideinit() |
IDEディスクIOを初期化します |
idestart() |
ディスクの読み取りを開始し、書き込み要求を |
iderw() |
上側のディスク・ファイル・システムは、IOインタフェースを呼び出します |
ideintr() |
ディスク要求完了割り込みハンドラは関数を呼び出すと |
オペレーティングシステムが起動すると、main()
関数呼び出しideinit()
にide
ディスクの初期化、初期化関数は、IDEのロックを初期化し、ディスク割り込み制御を設定し、第二のディスクをチェックします。
iderw()
関数は、トップレベルファイル・システム・モジュールのためのインタフェースを提供します。iderw()
両方の読み取りのため、書き込みが決定することにより簡単に使用することができるbuf->flag
要求が読み出しであることを決定またはダーティビット、有効ビットに書き込むことができます。要求キューが空の場合、現在の状態のディスクが動作していないことを証明する、あなたは呼び出す必要がありidestart()
、ディスク要求キューを初期化する機能を、割り込みを設定します。要求が書き込み要求であれば、それはidestart()
ディスクにデータを書き込むための命令を発行します。その後、iderw()
スリープ状態に、発信者になります。
ディスクの読み取りまたは書き込み操作が完了すると、それが入るように、割り込みをトリガする機能を、関数が呼び出されます、ディスク関連の中断を処理する関数を。では、現在の要求が読み出し要求である場合、関数、今ディスクバッファ内のデータを読み込む準備ができています。最後に、要求キューがある場合は、現在の要求を待って眠っているプロセスを覚ますだろう、と呼ばれる新しい要求を処理します。trap.c
trap()
trap()
ideintr()
ideintr()
ideintr()
idestart()
機能とバッファ・キャッシュの実装
ファイルシステムでは、バッファ・キャッシュは、中間層ディスクとメモリのファイルシステムの相互作用を務めていました。ディスクの読み込みが非常に遅いので、最近の記憶でディスクキャッシュ頻繁にアクセスされるブロックとして、それは非常に有益です。
バッファキャッシュbio.c内のデータ構造(rev11バージョン)としてXv6、バッファキャッシュに実装
struct {
struct spinlock lock;
struct buf buf[NBUF];
// Linked list of all buffers, through prev/next.
// head.next is most recently used.
struct buf head;
} bcache;
このデータ構造は、の維持に固定された長さの配列でstruct buf
構成される二重リンクリスト、およびバッファ・キャッシュのリスト構造へのアクセスを保護するためのロックを有します。これは、リスト構造へのアクセスが注目する価値があるとstruct buf
アクセス構造は、異なるロックで必要とされています。
キャッシュが初期化されると、システムコールbinit()
キャッシュを初期化します。binit()
関数は、キャッシュロック睡眠の各要素を初期化し、戻って二重にリンクされたリストから、接続されています。初めに、キャッシュ内のすべてのブロックは空です。
上位層のファイルシステムbread()
とbwrite()
ディスクブロックの読み取りおよび書き込みキャッシュします。キャッシュ上のすべての操作はであり、上のファイルシステムなしで、自動的に行わ参加。bread()
bwrite()
bread()
最初に呼び出すbget()
機能、bget()
キャッシュ内に要求されたディスクブロックするかどうかの機能チェックを。キャッシュ場合、ディスクブロックは、直接対応するキャッシュに戻ります。キャッシュ内にない場合は、必要性の下に使用するiderw()
ディスクからキャッシュにロードするために、このディスク・ブロックの機能をして、ディスクブロックを返します。
bget()
いくつかのトリッキーを達成するために機能します。ソースコード検索のキャッシュブロックは非常に簡単ですが、慎重に複数のプロセスが同時に同期メカニズムディスクブロックにアクセスする際に考慮しなければなりません。睡眠ロックがない場合には、コンテンツ内に待機している間にXv6 rev7バージョンでは、バッファの変更を待って避けるために、あなたはロックから起床時に適したディスク・ブロックを見つけるためにディスクバッファが、rev11バージョンを再スキャンする必要がありますそれは、対応するキャッシュブロックを見つけた場合により実現睡眠ロックに、単純にバッファキャッシュのロックを解除し、あなたが関連付けられている現在のキャッシュブロックをロックすることができます睡眠を得ます。
bwrite()
キャッシュ内のデータへの直接の機能は、ディスクに書き込まれます。バッファ・キャッシュ層は、ときに呼び出すへの書き込みを遅らせる任意の操作を実行しようとしないbwrite()
ファイルシステムの上位層によって制御されているディスクに書き込ま。
アッパーファイル・システム・コールは、brelse()
バッファゾーンは、もはや使用されているリリースしないように機能します。brelse()
主に二重にリンクされたリストの操作に関連する機能は、ここでは説明しません。
機能と実装層をログに記録します
異常を削除され、このようなシステムの電源として、そのようなファイルシステムを扱うことができるようにするためにログファイルシステム層を追加すると、ディスク矛盾上のファイルシステムを避けるのが好き。ログ・アイデアは、ディスクのログ領域に書き込まれ、そしてのみログにあるつのトランザクションに分割されている総稼働ディスクファイルシステムにそのような層、上層を達成するために、各トランザクションは最初のデータとそれに対応するディスク番号であろうあります後の書き込み領域、データ領域を完了し、データが実記憶領域をログ書きます。ログイン時にオフに書かれている場合、これらが存在しないようにリアルタイムエリアでオフに書かれている場合は、この設計では、ファイルシステムは、書き込まれる、データ領域には、ログ・ファイル・システムを回復するために使用することができます。だから、それはファイルシステムのファイルへの損傷を避けることができます。
ファイルシステム実装Xv6のrev7では、複数のプロセスが同時に実行トランザクション層ログ、まだ異なるrev11を達成することはできません、同時にログにトランザクションレイヤを実行する複数のプロセスを可能にします。実装の詳細に基づくrev11バージョンの以下の議論。
上層ログにファイルシステムを使用するときは、最初に呼び出す必要がありますbegin_op()
機能を。begin_op()
この関数は、新たなトランザクション情報を記録します。レイヤーを使用してログインした後、上位システムは、呼び出す必要がありますend_op()
機能を。実行時のトランザクションがないときにのみ、ログが実際のディスク書き込みを実行します。中実ディスクの書き込み操作commit()
機能、あなたが見ることができるcommit()
だけで機能をend_op()
、エンドlog.outstanding==0
(およびブート時間)と呼ばれるとき。commit()
この関数は最初に呼び出されますwrite_log()
、ディスク上のログ領域に書き込まれたディスクブロックをキャッシュする機能を、ディスクログヘッダー領域に書き込みます。ログのヘッダー更新中のディスクのデータ領域がある場合にのみ、この時間ログの更新が完了しています。ログ領域の更新後、commit()
関数呼び出しはinstall_trans()
、この後に呼び出さ本物のディスク書き込みステップ、完了するためにwrite_head()
、現在のログデータをクリアする機能を。
ハードディスクのファイルシステムのレイアウトXV6
Xv6オペレーティングシステムのハードドライブでは、複数のハードディスクを順次ブロックとして格納されます。これらのインデックスは、直接整数を用いて行ったディスクブロックされています
[boot block | super block | log | inode blocks | free bit map | data blocks]
最初のディスクブロックは、ブートブロックがブート時にメモリにロードされ、ディスクブロック番号は0です。第二のスーパーブロックは、次の文のXv6、ハードブロック数が1を占めます
struct superblock {
uint size; // Size of file system image (blocks)
uint nblocks; // Number of data blocks
uint ninodes; // Number of inodes.
uint nlog; // Number of log blocks
uint logstart; // Block number of first log block
uint inodestart; // Block number of first inode block
uint bmapstart; // Block number of first free map block
};
スーパーブロックは、ファイルシステムのメタ情報に格納されます。オペレーティングシステムは、最初のログ・ブロック、iノードブロック、ビットマップブロックとブロックのデータブロックサイズと位置の残りの部分を知っているスーパーブロックを読み取る必要があります。順次記憶スーパーブロックログ複数のブロックの後、ブロックのiノード、複数のビットマップの複数のブロック。ディスク・ストレージのデータ・ブロック・ブロックの残りの部分。
XV6ファイル
すべてのinodeデータ構造を持つ(ディレクトリを含む)Xv6ファイルは、inodeファイルのすべてがディスクに保存されると言うことにします。システムとプロセスは、iノードの使用を必要とするとき、iノードはiノードキャッシュにロードされます。iノードがiノード情報よりも少し実行時のメモリに格納されますが、ディスク上に格納されています。次の文のデータメモリinode構造体。
// in-memory copy of an inode
struct inode {
uint dev; // Device number
uint inum; // Inode number
int ref; // Reference count
struct sleeplock lock; // protects everything below here
int valid; // inode has been read from disk?
short type; // copy of disk inode
short major;
short minor;
short nlink;
uint size;
uint addrs[NDIRECT+1];
};
どのinode.type
ファイルの種類を示します。Xv6は、このタイプは、通常のファイル、ディレクトリ、またはファイルが特殊であることができます。
カーネルはiノードキャッシュメモリ、キャッシュデータ構造次の文を維持します
struct {
struct spinlock lock;
struct inode inode[NINODE];
} icache;
ノードiノードのために、以下のように基本的な操作があります
関数名 | 機能 |
---|---|
iinit() |
スーパーブロック、iノードに関連するロックの初期化を読みます |
ialloc() |
ディスク上のinodeを割り当て |
iupdate() |
メモリはディスクiノードに書き込まれます |
iget() |
iノードを取得、キャッシュを更新します |
iput() |
0として参照iノードを参照メモリに保存し、inodeを解放 |
ilock() |
指定されたロックのinodeを取得します。 |
iunlock() |
指定されたiノードのロックを解除します |
readi() |
iノードデータを読み込みます |
writei() |
iノードにデータを書き込みます |
bmap() |
n番目のデータブロックのディスクiノードのリターンアドレス |
12(iノードNDIRECT
システムXv6 140 * 512B = 70キロバイトでサポートされる最大ファイルサイズまで追加)ディスクブロック直接マッピング、ディスクブロック128間接マップを、。
Xv6システムファイルディスクリプタ
Unixシステムでは、この文は、より正確に、「すべてはファイルディスクリプタである」、「すべてはファイルである、」よく知られた設計思想です。他のIOソケットと同様であって、抽象ファイルシステムのファイルとディレクトリについて上述したinodeデータ構造、および抽象的に加えて、ファイルディスクリプタ、だけでなく、抽象パイプとなっている一般的なI / Oインターフェース。
次のようにXv6、文書のデータ構造が示されています
struct file {
enum { FD_NONE, FD_PIPE, FD_INODE } type;
int ref; // reference count
char readable;
char writable;
struct pipe *pipe;
struct inode *ip;
uint off;
};
このことから、ファイルのデータ構造はiノードのいずれかを表すことができ、あなたはまた、パイプを示すことができます。ファイルのデータ構造の複数の同一のinodeを抽象化することができるが、オフセット異なっていてもよいです。
システムがグローバルファイルディスクリプタ・テーブル内のすべての開いているファイルであるftable
、ftable
次のように文のデータ構造であります
struct {
struct spinlock lock;
struct file file[NFILE];
} ftable;
Xv6が100同時オープン(までサポート見ることができるNFILE
から)ファイルstruct proc
には、各プロセスでXv6を見ることができ、16(に開くことができますNOFILE
)ファイル。
基本的な操作ファイルのデータ構造は、前記filealloc()
、filedup()
、fileclose()
、fileread()
、filewrite()
とfilestat()
。命名インタフェースは、一貫したスタイルとUnixを提供し、その基本的な機能の名前から見ることは容易です。
inodeファイルの種類については、操作の実現には、次のような、iノードに依存してiread()
、iwrite()
基本的な操作。
Xv6ファイル関連のシステムコール
システムコールのほとんどを達成するための層の実現の使用は、比較的簡単です。ファイル関連のシステムのXv6サポートリスト次のように呼び出します
名前 | 機能 |
---|---|
sys_link() |
既存のiノードの新しい名前を作成します。 |
sys_unlink() |
既存のiノードの名前を削除するには、iノードを削除することができます |
sys_open() |
指定されたファイル記述子を開きます |
sys_mkdir() |
新しいディレクトリを作成します。 |
sys_mknod() |
新しいファイルを作成します。 |
sys_chdir() |
変更カレントディレクトリのプロセス |
sys_fstat() |
変更ファイルの統計情報 |
sys_read() |
ファイルディスクリプタを読みます |
sys_write() |
ファイルディスクリプタを書きます |
sys_dup() |
参照ファイルディスクリプタを増やします |
大半のシステムコールのセマンティクスは、UNIXの標準と同じです。