本篇將在前面的基礎上,再次通過剖析虛擬文件系統以及更下層文件系統結構,主要是基於結構篇深度剖析。
Linux內核的應用層就是以文件系統爲核心而展開的。文件系統在整個內核架構中具有基礎架構性質,字符設備、塊設備這些設備驅動的概念都要依靠文件系統來實現。
進階篇說到了存儲設備分區,引導區、超級塊區、inode table區以及數據塊區。
引導區主要是爲計算機開機服務的。我們這裏略過,先看
1、超級塊 super block
超級塊(super block)代表了整個文件系統本身,代表一個具體的已安裝文件系統,通常。超級塊是對應文件系統自身的控制塊結構,超級塊保存了文件系統設定的文件塊大小,超級塊的操作函數,文件系統內所有的inode也都要鏈接到超級塊的鏈表頭,當然還包括其餘的信息,通過超級塊對象,我們可以找到這些必要的信息。
超級塊是文件系統的心臟,是與文件系統相關的,在超級塊中保存了全局文件信息,如硬盤已用空間、數據塊可用空間、inode節點信息等,一個文件系統中有哪些資源都記錄在其中。
struct super_block {
struct list_head s_list; /* Keep this first */
dev_t s_dev; /* search index; _not_ kdev_t */
unsigned long s_blocksize;//指定了文件系統的塊大小
unsigned char s_blocksize_bits;
unsigned char s_dirt;
unsigned long long s_maxbytes; /* Max file size *///最大文件的尺寸
struct file_system_type *s_type;//指向邋file_system_type結構的指針
struct super_operations *s_op;//超級塊操作函數集
struct dquot_operations *dq_op;
struct quotactl_ops *s_qcop;
struct export_operations *s_export_op;
unsigned long s_flags;
unsigned long s_magic;//魔術數字
struct dentry *s_root;//文件系統根目錄項
struct rw_semaphore s_umount;
struct mutex s_lock;//互斥鎖 SMP safe
int s_count;
int s_syncing;
int s_need_sync_fs;
atomic_t s_active;
void *s_security;
struct xattr_handler **s_xattr;
struct list_head s_inodes; /* all inodes *///指向文件系統內的所有inode,通過它可以遍歷所有inode對象
struct list_head s_dirty; /* dirty inodes *///所有dirty的inode對象
struct list_head s_io; /* parked for writeback */
struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */
struct list_head s_files;
struct block_device *s_bdev;//指向文件系統存在的塊設備指針
struct list_head s_instances;
struct quota_info s_dquot; /* Diskquota specific options */
int s_frozen;
wait_queue_head_t s_wait_unfrozen;
char s_id[32]; /* Informational name */
void *s_fs_info; /* Filesystem private info */
/*
* The next field is for VFS *only*. No filesystems have any business
* even looking at it. You had been warned.
*/
struct mutex s_vfs_rename_mutex; /* Kludge */
/* Granularity of c/m/atime in ns.
Cannot be worse than a second */
u32 s_time_gran;
};
超級塊的內容需要讀取具體文件系統在硬盤上的超級塊結構獲得,超級塊是具體文件系統超級塊的內存抽象。
創建、管理和撤銷超級塊對象的代碼位於文件fs/super.c 中。超級塊對象通過 alloc_super() 函數創建並初始化。在文件系統安裝時,文件系統會調用該函數以便從磁盤讀取文件系統超級塊,並且將其信息填充到內存的超級塊對象中。
從其結構可以看出,超級塊包含了與文件系統相關的參數信息:包括inode表容量、文件系統中邏輯塊的大小、以邏輯塊計。文件系統的大小等。
2、目錄項 dentry
首先有必要區分一下目錄跟目錄項。在Unix語義中,一切皆文件,所以目錄也會被當成一個文件來處理,也就不存在目錄對象,那麼目錄項又是什麼?目錄項代表的是路徑中的一個組成部分,他可能包括一個普通文件,換言之,目錄可以包含子目錄(子文件)、目錄是可以層層嵌套的,所以形成了文件路徑,而文件路徑中的每一部分,就是所謂的目錄項。
對文件系統而言,通常,文件和目錄是按樹狀結構來保存的,目錄項就是反應文件系統的這種樹狀關係,在VFS裏,目錄本身也是一個文件,只是有點特殊。每個文件至少都有一個dentry,這個dentry鏈接到上級目錄的dentry,根目錄有一個dentry結構,而根目錄裏的文件和目錄都鏈接到這個dentry,然後二級目錄裏的文件和目錄,同樣通過dentry鏈接到二級目錄,這樣層層鏈接,就形成了一棵dentry樹,從樹頂可以遍歷整個文件系統的所有目錄和文件。也就是說,因爲這些關係,在Linux環境下你可以從根目錄"/"出發,找到整個文件系統中的任何目錄和文件,從文件你也可以cd .. 返回上層目錄。
上面的數據結構其實很簡單,簡單的說來就是,一個節點裏面含有指向父目錄集和子目錄集的指針。
爲了加快對dentry的查找(如果VFS層遍歷路徑名中所有的元素並將它們逐個解析成目錄項對象,還要到達最深層目錄,將是一個非常費力的工作),內核使用了hash表來緩存dentry,成爲dentry cache。前面的進階篇裏有說到。file結構中的f_dentry指針就是指向dentry cache中的dentry的。
struct dentry {
atomic_t d_count;
unsigned int d_flags; /* protected by d_lock */
spinlock_t d_lock; /* per dentry lock */
struct inode *d_inode; /* Where the name belongs to - NULL is
* negative *///指向一個inode結構,這個inode和dentry共同描述了一個普通文件或者目錄文件
/*
* The next three fields are touched by __d_lookup. Place them here
* so they all fit in a cache line.
*/
struct hlist_node d_hash; /* lookup hash list *///dentry cache的hash鏈表
struct dentry *d_parent; /* parent directory *///指向父dentry
struct qstr d_name;//名字
struct list_head d_lru; /* LRU list */
/*
* d_child and d_rcu can share memory
*/
union {
struct list_head d_child; /* child of parent list *///dentry自身的鏈表頭
struct rcu_head d_rcu;
} d_u;
struct list_head d_subdirs; /* our children *///子項(可能是目錄,可能是文件)的鏈表頭
struct list_head d_alias; /* inode alias list */
unsigned long d_time; /* used by d_revalidate */
struct dentry_operations *d_op;//操作函數集
struct super_block *d_sb; /* The root of the dentry tree */
void *d_fsdata; /* fs-specific data */
#ifdef CONFIG_PROFILING
struct dcookie_struct *d_cookie; /* cookie, if any */
#endif
int d_mounted;//指示dentry是否是一個掛載點,如果是掛載點,該成員不爲零
unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */
};
構建dentry樹,重要的結構自然就是d_subdirs、d_child以及d_parent成員了。
d_subdirs 是子項的鏈表頭,所有的子項都要鏈接到這個鏈表,d_child是dentry自身的鏈表頭,需要鏈接到父dentry的d_subdirs成員。d_parent則是指向父dentry結構的指針。當移動文件的時候,需要把一個dentry結構從舊的父dentry的鏈表上脫離,然後鏈接到新的父dentry的d_subdirs成員,這樣dentry結構之間就構成了一棵目錄樹。
3、索引節點inode
inode代表一個文件,inode保存了文件的大小,創建時間,文件的塊大小等參數,以及對文件的讀寫函數、文件的讀寫緩存等信息。
索引節點對象包含了內核在操作文件或目錄時需要的全部信息。僅當文件被訪問時,纔在內存中創建。
一個真實的文件可以有多個dentry,因爲指向文件的路徑可以有多個(考慮到文件的鏈接),但是inode只有一個。
struct inode {
struct hlist_node i_hash;
struct list_head i_list;//用於描述inode當前狀態的鏈表
struct list_head i_sb_list;//用於鏈接到超級塊中的inode鏈表
struct list_head i_dentry;//用於鏈接到dentry鏈表
unsigned long i_ino;//inode號
atomic_t i_count;//引用計數,遞增遞減均爲原子操作
umode_t i_mode;//文件類型
unsigned int i_nlink;
uid_t i_uid;//用戶。組等等相關的
gid_t i_gid;
dev_t i_rdev;
loff_t i_size;
struct timespec i_atime;//三個時間戳
struct timespec i_mtime;
struct timespec i_ctime;
unsigned int i_blkbits;//文件塊的位數
unsigned long i_blksize;
unsigned long i_version;
blkcnt_t i_blocks;
unsigned short i_bytes;
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
struct mutex i_mutex;
struct rw_semaphore i_alloc_sem;
struct inode_operations *i_op;//inode操作函數集
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct super_block *i_sb;//鏈接到超級塊
struct file_lock *i_flock;
struct address_space *i_mapping;//緩存文件的內容,加快文件的讀操作
struct address_space i_data;//指向文件數據塊的指針
......
};
4、文件對象 file
文件對象的作用是描述進程和文件交互的關係,需要注意的是,硬盤上並不存在這樣一個文件結構,進程打開一個文件,內核就動態創建一個文件對象,同一個文件,在不同的進程中有不同的文件對象。
文件和文件對象是兩個不同的概念,“文件”指的是硬盤上具體存在的文件,而“文件對象”是在內存裏存在的文件結構。
struct file {
/*
* fu_list becomes invalid after file_free is called and queued via
* fu_rcuhead for RCU freeing
*/
union {
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u;
struct dentry *f_dentry;//文件對應的dentry結構
struct vfsmount *f_vfsmnt;//文件所屬於的文件系統的vfsmount對象
const struct file_operations *f_op;//操作函數集
atomic_t f_count;
unsigned int f_flags;
mode_t f_mode;
loff_t f_pos;//進程對文件操作的位置
struct fown_struct f_owner;
unsigned int f_uid, f_gid;
struct file_ra_state f_ra;//文件預讀位置
unsigned long f_version;
void *f_security;
/* needed for tty driver, and maybe others */
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
spinlock_t f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;//文件的讀寫緩存
};
VFS是具體文件系統的抽象,VFS是依靠超級塊、dentry、inode以及文件這些結構來發揮作用。