仮想ファイルシステムのデータ構造
ファイル システム タイプによって物理構造は異なりますが、仮想ファイル システムは統一されたデータ構造を定義します。
(1) スーパーブロック。ファイル システムの最初のブロックは、ファイル システム全体の情報を記述するスーパー ブロックです。ファイル システムがマウントされると、スーパー ブロックのコピーがメモリ内に作成されます (構造体 super_block)。
(2) 仮想ファイル システムは、メモリ内でディレクトリをツリーとして編成します。ファイル システムがメモリ内のディレクトリ ツリー内のディレクトリにマウントされている場合、プロセスはファイル システムにのみアクセスできます。ファイル システムがマウントされるたびに、仮想ファイル システムはマウント記述子、つまりマウント構造を作成し、ファイル システムのスーパー ブロックを読み取り、メモリ内にスーパー ブロックのコピーを作成します。
(3) スーパーブロックの形式はファイルシステムごとに異なるため、ファイルシステムタイプ file_system_type を仮想ファイルシステムに登録し、スーパーブロックを読み込んで解析するマウントメソッドを実装する必要があります。
(4) インデックスノード。各ファイルはインデックス ノードに対応し、各インデックス ノードには一意の番号が付けられます。カーネルがストレージ デバイス上のファイルにアクセスすると、メモリ内に i ノードのコピー、つまり構造体 i ノードが作成されます。
(5) ディレクトリ項目。ファイルシステムはディレクトリをファイルの一種とみなし、ディレクトリのデータはディレクトリエントリで構成され、各ディレクトリエントリにはサブディレクトリまたはファイルの名前と対応するインデックスノード番号が格納されます。カーネルがストレージ デバイス上のディレクトリ エントリにアクセスすると、ディレクトリ エントリのコピー、つまり dentry 構造体がメモリ内に作成されます。
(6) プロセスがファイルを開くと、仮想ファイル システムはファイルのオープン インスタンス (ファイル構造) を作成し、プロセスのオープン ファイル テーブルにインデックスを割り当てます。このインデックスはファイル記述子と呼ばれ、最後にファイル記述子と呼ばれます。 file 記述子とファイル構造のマップが、開いているファイル テーブルに追加されます。
超早い
ファイル システムの最初のブロックはスーパー ブロックで、ファイル システムの全体的な情報を記述するために使用されます。ファイル システムをメモリ内のディレクトリ ツリーのディレクトリにマウントすると、ファイル システムのスーパー ブロックが読み取られ、スーパー ブロックのコピーがメモリ内に作成されます。構造体 super_block の主なメンバーは次のとおりです。次のように:
include/linux/fs.h
struct super_block {
struct list_head s_list;
dev_t s_dev;
unsigned char s_blocksize_bits;
unsigned long s_blocksize;
loff_t s_maxbytes;
struct file_system_type *s_type;
const struct super_operations *s_op;
…
unsigned long s_flags;
unsigned long s_iflags; /*内部 SB_I_* 标志 */
unsigned long s_magic;
struct dentry *s_root;
…
struct hlist_bl_head s_anon;
struct list_head s_mounts;
struct block_device *s_bdev;
struct backing_dev_info *s_bdi;
struct mtd_info *s_mtd;
struct hlist_node s_instances;
…
void *s_fs_info;
…
};
(1) メンバー s_list は、すべてのスーパー ブロック インスタンスをグローバル リンク リスト super_blocks にリンクするために使用されます。
(2) メンバー s_dev と s_bdev は、ファイル システムが配置されているブロック デバイスを保存します。前者はデバイス番号を保存し、後者はメモリ内の block_device インスタンスを指します。
(3) メンバ s_blocksize はブロック長、メンバ s_blocksize_bits はブロック長の底 2 の対数です。
(4) メンバ s_maxbytes は、ファイル システムがサポートする最大ファイル長です。
(5) メンバ s_flags はフラグビットです。
(6) メンバ s_type はファイル システム タイプを指します。
(7) メンバ s_op はスーパーブロック演算セットを指します。
(8) メンバ s_magic はファイル システム タイプのマジック ナンバーであり、ファイル システム タイプごとに固有のマジック ナンバーが割り当てられます。
(9) メンバ s_root は、ルート ディレクトリの構造体 dentry を指します。
(10) メンバ s_fs_info は、特定のファイルシステムのプライベート情報を指します。
(11) メンバ s_instances は、同じファイル システム タイプのすべてのスーパー ブロック インスタンスをリンクするために使用され、リンク リストのヘッド ノードは構造体 file_system_type のメンバ fs_supers です。
スーパー ブロック操作セットのデータ構造は構造体 super_operations で、主なメンバーは次のとおりです。
include/linux/fs.h
struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *);
void (*dirty_inode) (struct inode *, int flags);
int (*write_inode) (struct inode *, struct writeback_control *wbc);
int (*drop_inode) (struct inode *);
void (*evict_inode) (struct inode *);
void (*put_super) (struct super_block *);
int (*sync_fs)(struct super_block *sb, int wait);
…
int (*statfs) (struct dentry *, struct kstatfs *);
int (*remount_fs) (struct super_block *, int *, char *);
void (*umount_begin) (struct super_block *);
…
};
(1) メンバー alloc_inode は、インデックス ノードのメモリの割り当てと初期化に使用されます。
(2) メンバ destroy_inode は、メモリ内のインデックス コンタクトを解放するために使用されます。
(3) メンバ dirty_inode は、インデックス ノードをダーティとしてマークするために使用されます。
(4) メンバー write_inode は、インデックス ノードをストレージ デバイスに書き込むために使用されます。
(5) メンバ Drop_inode は、インデックス ノードの参照カウントが 0 になったときに呼び出すために使用されます。
(6) メンバ evict_inode は、ストレージ デバイス上のファイル システムからインデックス ノードを削除するために使用されます。
(7) スーパーブロックを解放するにはメンバ put_super を使用します。
(8) メンバ sync_fs は、ファイル システムの変更されたデータをストレージ デバイスに同期するために使用されます。
(9) メンバ statfs は、ファイルシステムの統計情報を読み取るために使用されます。
(10) メンバ remount_fs は、ファイルシステムを再マウントするときに呼び出すために使用されます。
(11) メンバ umount_begin は、ファイル システムをアンマウントするときに呼び出すために使用されます。
マウント記述子
ファイル システムがメモリ内のディレクトリ ツリー内のディレクトリにマウントされている場合、プロセスはファイル システムにのみアクセスできます。ファイル システムがマウントされるたびに、仮想ファイル システムはマウント記述子、つまりマウント構造を作成します。マウント記述子は、ファイル システムのマウント インスタンスを記述するために使用されます。同じストレージ デバイス上のファイル システムは複数回マウントでき、毎回異なるディレクトリにマウントされます。マウント記述子の主なメンバーは次のとおりです。
fs/mount.h
struct mount {
struct hlist_node mnt_hash;
struct mount *mnt_parent;
struct dentry *mnt_mountpoint;
struct vfsmount mnt;
union {
struct rcu_head mnt_rcu;
struct llist_node mnt_llist;
};
#ifdef CONFIG_SMP
struct mnt_pcp __percpu *mnt_pcp;
#else
int mnt_count;
int mnt_writers;
#endif
struct list_head mnt_mounts;
struct list_head mnt_child;
struct list_head mnt_instance;
const char *mnt_devname;
struct list_head mnt_list;
…
struct mnt_namespace *mnt_ns;
struct mountpoint *mnt_mp;
struct hlist_node mnt_mp_list;
…
}
ファイルシステム 2 をディレクトリ "/a" にマウントし、ディレクトリ a がファイルシステム 1 に属しているとします。ディレクトリ a はマウント ポイントと呼ばれ、ファイル システム 2 のマウント インスタンスはファイル システム 1 のマウント インスタンスの子であり、ファイル システム 1 のマウント インスタンスはファイル システム 2 のマウント インスタンスの親です。
(1) メンバ mnt_parent は、親、つまりファイル システム 1 のマウント インスタンスを指します。
(2) メンバ mnt_mountpoint はマウントポイントとなるディレクトリ、つまりファイルシステム 1 のディレクトリ a を指し、ディレクトリ a の dentry インスタンスのメンバ d_flags はフラグビット DCACHE_MOUNTED を設定します。
(3) メンバ mnt の種類は以下のとおりです。
struct vfsmount {
struct dentry *mnt_root;
struct super_block *mnt_sb;
int mnt_flags;
};
mnt_root はファイル システム 2 のルート ディレクトリを指し、mnt_sb はファイル システム 2 のスーパー ブロックを指します。
(4) メンバー mnt_hash は、マウント記述子をグローバル ハッシュ テーブル mount_hashtable に追加するために使用されます。キーワードは {親マウント記述子, マウント ポイント} です。
(5) メンバ mnt_mounts は子リストの先頭ノードです。
(6) メンバ mnt_child は、父親の子リストに参加するために使用されます。
(7) メンバー mnt_instance は、スーパー ブロックのマウント インスタンスのリンク リストにマウント記述子を追加するために使用されます。同じストレージ デバイス上のファイル システムは複数回マウントでき、毎回異なるディレクトリにマウントされます。
(8) メンバ mnt_devname はストレージ デバイスの名前を指します。
(9) メンバー mnt_ns はマウント名前空間を指します。
(10) メンバー mnt_mp はマウント ポイントを指します。タイプは次のとおりです。
struct mountpoint {
struct hlist_node m_hash;
struct dentry *m_dentry;
struct hlist_head m_list;
int m_count;
};
m_dentry はマウント ポイントとしてディレクトリを指し、m_list は同じマウント ポイントの下にあるすべてのマウント記述子をリンクするために使用されます。同じマウント ポイントに複数のマウント記述子があるのはなぜですか? これは名前空間のマウントに関係しています。
(11) メンバ mnt_mp_list は、同じマウントポイントのマウント記述子リンクリストにマウント記述子を追加するために使用され、リンクリストの先頭ノードはメンバ mnt_mp のメンバ m_list です。
ファイルシステムの種類
各ファイルシステムのスーパーブロックの形式は異なるため、各ファイルシステムはファイルシステムタイプ file_system_type を仮想ファイルシステムに登録し、スーパーブロックを読み取って解析するためのマウントメソッドを実装する必要があります。構造体 file_system_type は次のとおりです。
include/linux/fs.h
struct file_system_type {
const char *name;
int fs_flags;
#define FS_REQUIRES_DEV 1
#define FS_BINARY_MOUNTDATA 2
#define FS_HAS_SUBTYPE 4
#define FS_USERNS_MOUNT 8
#define FS_RENAME_DOES_D_MOVE 32768
struct dentry *(*mount) (struct file_system_type *, int, const char *, void *);
void (*kill_sb) (struct super_block *);
struct module *owner;
struct file_system_type * next;
struct hlist_head fs_supers;
…
};
(1) メンバ名はファイルシステムタイプの名前です。
(2) ファイルシステムのマウント時にスーパーブロックを読み取り、解析するためにメソッド mount が使用されます。
(3) ファイルシステムのアンマウント時にスーパーブロックを解放するメソッド kill_sb を使用します。(4) 複数のストレージ デバイス上のファイル システムのタイプは同じである可能性があり、同じファイル システム タイプのスーパーブロックをリンクする
ためにメンバ fs_supers が使用されます。
インデックスノード
ファイルシステムでは、各ファイルがインデックスノードに相当し、インデックスノードには2種類の情報が記述されます。
(1) ファイルの長さ、ファイルを作成したユーザーの識別子、最終アクセス時刻、最終変更時刻など、メタデータ (メタデータ) とも呼ばれるファイルの属性。すぐ。
(2) ファイルデータの保存場所。
各 i ノードには一意の番号があります。
カーネルがストレージ デバイス上のファイルにアクセスすると、インデックス ノードのコピーがメモリ内に作成されます。構造体 i ノードの主なメンバーは次のとおりです。
include/linux/fs.h
struct inode {
umode_t i_mode;
unsigned short i_opflags;
kuid_t i_uid;
kgid_t i_gid;
unsigned int i_flags;
#ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
const struct inode_operations *i_op;
struct super_block *i_sb;
struct address_space *i_mapping;
…
unsigned long i_ino;
union {
const unsigned int i_nlink;
unsigned int __i_nlink;
};
dev_t i_rdev;
loff_t i_size;
struct timespec i_atime;
struct timespec i_mtime;
struct timespec i_ctime;
spinlock_t i_lock;
unsigned short i_bytes;
unsigned int i_blkbits;
blkcnt_t i_blocks;
…
struct hlist_node i_hash;
struct list_head i_io_list;
…
struct list_head i_lru;
struct list_head i_sb_list;
struct list_head i_wb_list;
union {
struct hlist_head i_dentry;
struct rcu_head i_rcu;
};
u64 i_version;
atomic_t i_count;
atomic_t i_dio_count;
atomic_t i_writecount;
#ifdef CONFIG_IMA
atomic_t i_readcount;
#endif
const struct file_operations *i_fop;
struct file_lock_context *i_flctx;
struct address_space i_data;
struct list_head i_devices;
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;
char *i_link;
unsigned i_dir_seq;
};
…
void *i_private;
};
i_mode はファイルの種類とアクセス権、i_uid はファイルを作成したユーザーの識別子、i_gid はファイルを作成したユーザーが属するグループ識別子です。
i_ino は i ノードの番号です。
i_size はファイルの長さ、i_blocks はファイル内のブロック数、つまりファイル長をブロック長で割った商、i_bytes はファイル長をブロック長で割った余り、i_blkbits のべき乗です。
i_atime (アクセス時刻) はファイルが最後にアクセスされた時刻、i_mtime (変更時刻) はファイル データが最後に変更された時刻、i_ctime (変更時刻) はファイル インデックス ノードが最後に変更された時刻です。
i_sb は、ファイルが属するファイル システムのスーパーブロックを指します。
i_mapping は、ファイルのアドレス空間を指します。
i_count は inode の参照カウント、i_nlink はハード リンクのカウントです。
ファイル タイプがキャラクタ デバイス ファイルまたはブロック デバイス ファイルの場合、i_rdev はデバイス番号、i_bdev はブロック デバイスを指し、i_cdev はキャラクタ デバイスを指します。
ファイルは以下の種類に分かれています。
(1)通常のファイル(通常のファイル):私たちが通常ファイルと呼んでいるものであり、狭義のファイルです。
(2) ディレクトリ: ディレクトリは、データがディレクトリ エントリで構成されている特別なファイルであり、各ディレクトリ エントリには、
サブディレクトリまたはファイルの名前と、対応するインデックス ノード番号が格納されます。
(3) シンボリックリンク(ソフトリンクとも呼ばれます):この種のファイルのデータは、別のファイルのパスになります。
(4) キャラクターデバイスファイル。
(5) デバイスファイルをブロックします。
(6) 名前付きパイプ (FIFO)。
(7)ソケット(ソケット)。
キャラクター デバイス ファイル、ブロック デバイス ファイル、名前付きパイプ、およびソケットは、inode のみを持ち、データを持たない特別なファイルです。キャラクタ デバイス ファイルとブロック デバイス ファイルはデバイス番号の保存に使用され、デバイス番号は i ノードに直接保存されます。
カーネルは 2 種類のリンクをサポートします。
(1) ソフト リンク。シンボリック リンクとも呼ばれ、このファイルのデータは別のファイルのパスです。
(2) ハードリンクはファイルに複数の名前を付けることに相当し、複数のファイル名は同じインデックス ノードに対応し、インデックス ノードのメンバ i_nlink がハード リンク数になります。
インデックス ノードのメンバー i_op はインデックス ノード操作セット inode_operations を指し、メンバー i_fop はファイル操作セット file_operations を指します。2 つの違いは、inode_operations はディレクトリの操作 (ディレクトリ内のファイルの作成または削除) とファイル属性に使用され、file_operations はファイル データにアクセスするために使用されます。インデックス ノード操作セットのデータ構造は inode_operations 構造で、主なメンバーは次のとおりです。
include/linux/fs.h
struct inode_operations {
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
int (*permission) (struct inode *, int);
struct posix_acl * (*get_acl)(struct inode *, int);
int (*readlink) (struct dentry *, char __user *,int);
int (*create) (struct inode *,struct dentry *, umode_t, bool);
int (*link) (struct dentry *,struct inode *,struct dentry *);
int (*unlink) (struct inode *,struct dentry *);
int (*symlink) (struct inode *,struct dentry *,const char *);
int (*mkdir) (struct inode *,struct dentry *,umode_t);
int (*rmdir) (struct inode *,struct dentry *);
int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
int (*rename) (struct inode *, struct dentry *, struct inode *, struct dentry *, unsigned int);
int (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (const struct path *, struct kstat *, u32, unsigned int);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,u64 len);
int (*update_time)(struct inode *, struct timespec *, int);
int (*atomic_open)(struct inode *, struct dentry *, struct file *, unsigned open_flag, umode_t create_mode, int *opened);
int (*tmpfile) (struct inode *, struct dentry *, umode_t);
int (*set_acl)(struct inode *, struct posix_acl *, int);
} ____cacheline_aligned;
lookup メソッドは、ディレクトリ内のファイルを検索するために使用されます。
システム コール open および creat は create メソッドを呼び出して通常のファイルを作成し、システム コール link は link メソッドを呼び出してハード リンクを作成し、システム コール symlink は symlink メソッドを呼び出してシンボリック リンクを作成し、システム コール mkdir はmkdir メソッドを呼び出してディレクトリを作成し、システム コール mknod は mknod メソッドを呼び出して、キャラクター デバイス ファイル、ブロック デバイス ファイル、名前付きパイプ、およびソケットを作成します。
システムコール unlink はハードリンクを削除するために unlink メソッドを呼び出し、システムコール rmdir はディレクトリを削除するために rmdir メソッドを呼び出します。
システムコール rename は、rename メソッドを呼び出してファイルの名前を変更します。
システム コール chmod は setattr メソッドを呼び出してファイルの属性を設定し、システム コール stat は getattr メソッドを呼び出してファイル属性を読み取ります。
システム コール listxattr は、listxattr メソッドを呼び出して、ファイルのすべての拡張属性をリストします。
カタログエントリー
ファイルシステムはディレクトリをファイルとみなして、このファイルのデータはディレクトリエントリで構成され、各ディレクトリエントリにはサブディレクトリまたはファイルの名前と、対応するインデックスノード番号が格納されます。
カーネルがストレージ デバイス上のディレクトリ エントリにアクセスすると、ディレクトリ エントリのコピーがメモリ内に作成されます。構造体 dentry の主なメンバは次のとおりです。
include/linux/dcache.h
struct dentry {
/* RCU查找访问的字段 */
unsigned int d_flags;
seqcount_t d_seq;
struct hlist_bl_node d_hash;
struct dentry *d_parent;
struct qstr d_name;
struct inode *d_inode;
unsigned char d_iname[DNAME_INLINE_LEN];
/* 引用查找也访问下面的字段 */
struct lockref d_lockref;
const struct dentry_operations *d_op;
struct super_block *d_sb;
unsigned long d_time;
void *d_fsdata;
union {
struct list_head d_lru;
wait_queue_head_t *d_wait;
};
struct list_head d_child;
struct list_head d_subdirs;
/*
* d_alias和d_rcu可以共享内存
*/
union {
struct hlist_node d_alias;
struct hlist_bl_node d_in_lookup_hash;
struct rcu_head d_rcu;
} d_u;
};
d_name はファイル名を格納し、qstr は文字列ラッパーで、文字列のアドレス、長さ、およびハッシュ値を格納します。ファイル名が比較的短い場合は、ファイル名を d_iname に格納します。d_inode はファイルのインデックス ノードを指します。
d_parent は親ディレクトリを指し、d_child はこのディレクトリを親ディレクトリのサブディレクトリ リストに追加するために使用されます。
d_lockref は参照カウントされます。
d_op は、ディレクトリ エントリ操作のコレクションを指します。
d_subdirs はサブディレクトリのリンクされたリストです。
d_hash は、ディレクトリ エントリをハッシュ テーブル dentry_hashtable に追加するために使用されます。
d_lru は、スーパー ブロックの最も最近使用されていない (LRU) リスト s_dentry_lru にディレクトリ エントリを追加するために使用され、
ディレクトリ エントリの参照カウントが 0 に減少したときに、ディレクトリ エントリをスーパー ブロックの LRU リストに追加します。
d_alias は、同じファイルのすべてのハード リンクに対応するディレクトリ エントリをリンクするために使用されます。
ファイル「/a/b.txt」を例に、ディレクトリエントリとインデックスノードの関係を図に示します。
ディレクトリ エントリ操作セットのデータ構造は dentry_operations 構造で、そのコードは次のとおりです。
include/linux/dcache.h
struct dentry_operations {
int (*d_revalidate)(struct dentry *, unsigned int);
int (*d_weak_revalidate)(struct dentry *, unsigned int);
int (*d_hash)(const struct dentry *, struct qstr *);
int (*d_compare)(const struct dentry *, unsigned int, const char *, const struct qstr *);
int (*d_delete)(const struct dentry *);
int (*d_init)(struct dentry *);
void (*d_release)(struct dentry *);
void (*d_prune)(struct dentry *);
void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(const struct path *, bool);
struct dentry *(*d_real)(struct dentry *, const struct inode *, unsigned int);
} ____cacheline_aligned;
d_revalidate は、ネットワーク ファイル システムにとってディレクトリ エントリが有効であることを確認するために重要です。
d_hash はハッシュ値の計算に使用されます。
d_compare は、2 つのディレクトリ エントリのファイル名を比較するために使用されます。
d_delete は、ディレクトリエントリの参照カウントが 0 になったときに、ディレクトリエントリのメモリを解放できるかどうかを判断するために使用されます。
d_release は、ディレクトリ エントリのメモリを解放する直前に呼び出すために使用されます。
d_iput は、ディレクトリ エントリに関連付けられた i ノードを解放するために使用されます。
ファイルのインスタンスを開き、ファイルテーブルを開きます
プロセスがファイルを開くと、仮想ファイル システムはファイルのオープン インスタンスを作成します。 ファイル構造の主なメンバーは次のとおりです。
include/linux/fs.h
struct file {
union {
struct llist_node fu_llist;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
struct inode *f_inode;
const struct file_operations *f_op;
spinlock_t f_lock;
atomic_long_t f_count;
unsigned int f_flags;
fmode_t f_mode;
struct mutex f_pos_lock;
loff_t f_pos;
struct fown_struct f_owner;
const struct cred *f_cred;
…
void *private_data;
…
struct address_space *f_mapping;
} __attribute__((aligned(4)));
(1) f_path はディレクトリツリー内のファイルの場所を格納します。種類は次のとおりです。
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
};
mnt は、ファイルが属するファイル システムのマウント記述子のメンバ mnt を指し、dentry はファイルに対応するディレクトリ エントリです。
(2) f_inode はファイルのインデックス ノードを指します。
(3) f_op はファイル操作コレクションを指します。
(4) f_count はファイル構造の参照カウントです。
(5) f_mode はアクセスモードです。
(6) f_pos はファイル オフセット、つまりプロセスが現在アクセスしている位置です。
(7) f_mapping はファイルのアドレス空間を指します。
ファイルの開いているインスタンスとインデックス ノードの関係を図に示します。
ファイル システム情報構造の主なメンバーは次のとおりです。
include/linux/fs_struct.h
struct fs_struct {
…
struct path root, pwd;
};
メンバー root はプロセスのルート ディレクトリを格納し、メンバー pwd はプロセスの現在の作業ディレクトリを格納します。
システムコール chroot が最初に呼び出されると、ディレクトリ "/a" がプロセスのルート ディレクトリとして設定され、その後子プロセスが作成され、子プロセスは親プロセスのファイル システム情報を継承し、子プロセスが参照できるディレクトリの範囲は、ルート サブツリーとしてのディレクトリ "/a" に制限されます。子プロセスがファイル「/b.txt」(ファイルパスは「/」で始まる絶対パス)をオープンするとき、実際のファイルパスは「/a/b.txt」になります。
システム コール chdir が呼び出され、ディレクトリ "/c" がプロセスの現在の作業ディレクトリとして設定されているとします。子プロセスがファイル "d.txt" を開くと、ファイル パスは相対パスであり、 「/」で始まる)、実際のファイルパスは「/c/d.txt」です。
オープン ファイル テーブルはファイル記述子テーブルとも呼ばれます。データ構造は図に示されています。構造体 files_struct はオープン ファイル テーブルのラッパーです。主なメンバーは次のとおりです:
include/linux/fdtable.h
struct files_struct {
atomic_t count;
…
struct fdtable __rcu *fdt;
struct fdtable fdtab;
spinlock_t file_lock ____cacheline_aligned_in_smp;
unsigned int next_fd;
unsigned long close_on_exec_init[1];
unsigned long open_fds_init[1];
unsigned long full_fds_bits_init[1];
struct file __rcu * fd_array[NR_OPEN_DEFAULT];
};
メンバー数は、構造体 files_struct の参照数です。
メンバ fdt は、開いているファイル テーブルを指します。
プロセスが作成されたばかりの場合、メンバー fdt はメンバー fdtab を指します。一定期間実行した後、プロセスによって開かれたファイルの数が NR_OPEN_DEFAULT を超えると、開いているファイル テーブルが拡張され、fdtable 構造が再配布され、メンバ fdt は新しい fdtable 構造を指します。
オープンファイルテーブルのデータ構造は次のとおりです。
include/linux/fdtable.h
struct fdtable {
unsigned int max_fds;
struct file __rcu **fd;
unsigned long *close_on_exec;
unsigned long *open_fds;
unsigned long *full_fds_bits;
struct rcu_head rcu;
};
(1) メンバ max_fds は、オープン ファイル テーブルの現在のサイズ、つまりメンバ fd が指すファイル ポインタ配列のサイズです。プロセスでオープンされているファイルの数が増加するにつれて、オープン ファイル テーブルは徐々に拡張されます。
(2) メンバ fd はファイルポインタ配列を指します。プロセスが open を呼び出してファイルを開くと、返されるファイル記述子はファイル ポインターの配列のインデックスになります。
(3) メンバー close_on_exec は、新しいプログラムをロードするために execve() を実行するときにどのファイル記述子を閉じる必要があるかを示すビットマップを指します。
(4) メンバ open_fds は、どのファイル記述子が割り当てられているかを示すファイル記述子ビットマップを指します。