技術

[xv6 #60] Chapter 5 – File system – Inodes

テキストの69〜70ページ

本文

inodeという用語は関連する2つの意味を持つ。
ひとつは、ファイルのサイズとデータブロックのセクタ番号のリストを含むディスク上のデータ構造を指す。
もうひとつは、ディスク上のinodeのコピーはもちろん、カーネルで必要となる追加の情報を含む、メモリ上のデータ構造を指す。

すべてのディスク上のinodeは、inodeブロックと呼ばれるディスクの連続したエリアの中にまとめられる。
それぞれのinodeは同じサイズなので、nという番号を与え、ディスク上のn番目のinodeを捜すのは簡単である。
実際、この番号nは、inode番号もしくはi-numberと呼ばれ、実装中でinodeを特定する方法となる。

ディスク上のinodeは、dinode構造体として定義される。
typeというフィールドは、ファイルか、ディレクトリか、特殊なファイル(デバイス)かを区別するためにある。
typeがゼロだったら、そのディスク上のinodeは空きであるということを意味する。

fs.h

// On-disk file system format. 
// Both the kernel and user programs use this header file.

// Block 0 is unused.  Block 1 is super block.
// Inodes start at block 2.

#define ROOTINO 1  // root i-number
#define BSIZE 512  // block size

// File system super block
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
};

#define NDIRECT 12
#define NINDIRECT (BSIZE / sizeof(uint))
#define MAXFILE (NDIRECT + NINDIRECT)

// On-disk inode structure
struct dinode {
  short type;           // File type
  short major;          // Major device number (T_DEV only)
  short minor;          // Minor device number (T_DEV only)
  short nlink;          // Number of links to inode in file system
  uint size;            // Size of file (bytes)
  uint addrs[NDIRECT+1];   // Data block addresses
};

// Inodes per block.
#define IPB           (BSIZE / sizeof(struct dinode))

// Block containing inode i
#define IBLOCK(i)     ((i) / IPB + 2)

// Bitmap bits per block
#define BPB           (BSIZE*8)

// Block containing bit for block b
#define BBLOCK(b, ninodes) (b/BPB + (ninodes)/IPB + 3)

// Directory is a file containing a sequence of dirent structures.
#define DIRSIZ 14

struct dirent {
  ushort inum;
  char name[DIRSIZ];
};

カーネルは、よく使われるinodeのまとまりをメモリ上に保持する。
inode構造体は、ディスク上のdinode構造体のメモリ上のコピーとして使われる。
カーネルは、Cポインタがあるinodeを参照してるときだけ、そのinodeをメモリに格納する。
そして、参照カウントがゼロになったら、メモリからそのinodeを破棄する。
iget関数、iput関数は、ひとつのinodeへのポインタを獲得したり解放したりしつつその参照カウントを変更する。
あるinodeに対するポインタは、ファイルディスクリプタや、現在の作業ディレクトリや、execのような一時的なカーネルのコードによって生成される。

file.h

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;
};


// in-core file system types

struct inode {
  uint dev;           // Device number
  uint inum;          // Inode number
  int ref;            // Reference count
  int flags;          // I_BUSY, I_VALID

  short type;         // copy of disk inode
  short major;
  short minor;
  short nlink;
  uint size;
  uint addrs[NDIRECT+1];
};

#define I_BUSY 0x1
#define I_VALID 0x2

// device implementations

struct devsw {
  int (*read)(struct inode*, char*, int);
  int (*write)(struct inode*, char*, int);
};

extern struct devsw devsw[];

#define CONSOLE 1

iget関数が返すinode構造体は、有用な内容を何も含んでいないだろう。
ディスク上のinodeのコピーを保持することを保証するため、ilock関数を呼ばなければならない。
この関数は、inodeをロックし(他のプロセスが同じinodeに対してilock出来ないようにするために)、そして読み取り済みでなければ、ディスクからinodeを読み取る。
iunlock関数は、inodeに対するロックを解放する。
inodeポインタの獲得を、ロックから分離することは、いくつかの場合にデッドロックを避ける助けになる。
例えば、ディレクトリを探索する場合である。
複数のプロセスは、iget関数によって返されたあるinodeへのCポインタを保持することが出来るが、一度に一つのプロセスだけがそのinodeをロック出来る。

inodeキャッシュは、カーネルのコードやCポインタを保持するデータ構造のために、inodeをキャッシュするのみである。
inodeキャッシュの主な役割は、複数のプロセスによるアクセスを確実に同期化することであり、キャッシュすることではない。
あるinodeが頻繁に利用される場合、inodeキャッシュによってキャッシュされなくても、バッファキャッシュによって、メモリ上に適切に保持される。

感想

inodeの概要です。
ヘッダのソースは載せてますが、各関数のソースは後の節で説明があるはずなので、今回は載せてません。

メモリ上にinodeの複製を持つのは、主にキャッシュの為ではなく、同じinodeに対するアクセスを同期化する目的の為のようです。

コメントを残す

メールアドレスが公開されることはありません。



※画像をクリックして別の画像を表示