1 VFS文件系統
計算機體系由三個部分組成:計算,存儲,網絡,體現了物體流轉的三個方面。
生產酒也是這樣的一個過程,首先是收割小麥,將小麥發酵蒸餾形成了酒(處理),然后裝在瓶子里,運送到倉庫。(傳輸),最后將所有的酒存在倉庫里。(存儲)
物理設備比如磁盤可以看作是土地,有了土地之后並不能使用,它還無法存儲酒。需要在土地上建立倉庫,並給每一個倉庫通電,編號。這個過程可以理解為建立
文件系統的過程。文件系統建立以后就可以往里面存酒了。什么樣的酒存在哪個倉庫需要有管理人員記錄在本子上。當然倉庫在存儲的時候不一定存的都是酒,也
可能存一些箱子,或者是小麥。
虛擬文件系統可以理解為建造倉庫的標准。建造倉庫的時候我們會告訴施工隊,我要實現的功能有哪些,比如我希望倉庫可以存儲5噸的貨物,倉庫要有一個自動門,汽車來到驗證車牌就可以放行,倉庫內部的溫度要保存在5度左右。只要滿足這幾個要求和標准即可。至於施工隊用什么樣的材料建造房屋。用的是鐵門還是合金門,都不是使用倉庫的人要考慮的。
而在VFS則定義了虛擬文件系統,這個東西類似於建造倉庫的標准。有了標准之后,所有的文件系統都需要按照這種標准來構建文件系統。

從上圖可以看到,虛擬機文件系統介於具體的文件系統和C語言標准庫之間,有了VFS之后,所有按照VFS接口開發的文件系統都可以接入linux,標准的接口提供了標准
的操作,接口的設計理念一致貫穿於整個編程科學的發展。面向對象的設計理念又深化了這個理念。
2 VFS關鍵數據結構及關系
上面是虛擬文件系統的一個概覽,虛擬文件系統定義了通用接口,所有具體的文件系統都必須實現這樣的結構。task_struct是進程描述符,里面記錄了進程的相關信息。一個進程可能操作某個文件,因此,task_struck成員內部有一個files,filtes對應files_struct里面記錄了所有打開的file,file於file之間通過雙向鏈表組織起來。
file內部包含了d_dentry,也就是目錄項,通過目錄項可以找到對應的inode,inode記錄了文件的權限和屬性。進程在操作文件的時候需要用到權限和屬性,比如判斷某個進程對某個文件是不是有寫權限。inode里面有一個成員i_mapping,對應address_space。其中的
i_mapping域指向一個address_space結構。這樣,一個文件就對應一個address_space結構,
一個 address_space與一個偏移量能夠確定一個page cache 或swap cache中的一個頁面。至於什么是page cache和swap cache可以
查閱相關知識。以上就是VFS組件相互之間的關系,下面看看每一個具體的組件。
super_block
是文件系統的心臟,它存儲了文件系統的全局信息,如硬盤已用空間,數據塊可用空間。
文件系統所有的inode信息都鏈接到超級塊上, 如下所示是超級塊的數據結構,下面節選某些關鍵信息。
struct super_block { struct list_head s_list; /* Keep this first */ dev_t s_dev; /* search index; _not_ kdev_t */ unsigned char s_blocksize_bits; unsigned long s_blocksize; loff_t s_maxbytes; /* Max file size */ struct file_system_type *s_type; ……….. unsigned int s_quota_types; /* Bitmask of supported quota types */ struct quota_info s_dquot; /* Diskquota specific options */ struct sb_writers s_writers; void *s_fs_info; /* Filesystem private info */ /* Granularity of c/m/atime in ns (cannot be worse than a second) */ u32 s_time_gran; #ifdef CONFIG_FSNOTIFY __u32 s_fsnotify_mask; struct fsnotify_mark_connector __rcu *s_fsnotify_marks; #endif char s_id[32]; /* Informational name */ uuid_t s_uuid; /* UUID */ unsigned int s_max_links; fmode_t s_mode; struct mutex s_vfs_rename_mutex; /* Kludge */ char *s_subtype; const struct dentry_operations *s_d_op; /* default d_op for dentries */ struct user_namespace *s_user_ns; struct list_head s_inodes; /* all inodes */ spinlock_t s_inode_wblist_lock; struct list_head s_inodes_wb; /* writeback inodes */ }
- s_blocksize 代表文件系統的大小。
- s_inodes 代表了所有的inode。
- s_type 代表文件系統的類型。
Inode
代表一個具體的文件, 包含文件的大小,創建時間,文件的塊大小等數據,以及對文件的讀寫函數,文件的讀寫緩存信息等。看一下inode的數據結構,這里只保留一些關鍵的數據信息,其他一些都去掉了。
struct inode { umode_t i_mode; unsigned short i_opflags; kuid_t i_uid; kgid_t i_gid; unsigned int i_flags; const struct inode_operations *i_op; struct super_block *i_sb; struct address_space *i_mapping; unsigned long i_ino; { const unsigned int i_nlink; unsigned int __i_nlink; }; /* Misc */ unsigned long i_state; struct rw_semaphore i_rwsem; unsigned long dirtied_when; /* jiffies of first dirtying */ unsigned long dirtied_time_when; struct hlist_node i_hash; struct list_head i_io_list; /* backing dev IO list */ struct list_head i_lru; /* inode LRU list */ struct list_head i_sb_list; struct list_head i_wb_list; /* backing dev writeback list */ const struct file_operations *i_fop; /* former ->i_op->default_file_ops */ 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; }; __u32 i_generation; void *i_private; /* fs or device private pointer */
。。。。。。。
}
鏈表其實是linux中組織數據非常常用的數據結構,這個結構里面有三個鏈表頭。
- i_mapping是非常重要的一個結構,描述了文件地址。
- block_device描述對應的塊設備(塊設備可以被看作一個文件系統來處理)。
- inode_operations,該結構體定義了對文件的操作(如下代碼所示),可以看到有獲取屬性,設置屬性,重新命名等操作
- i_mode,主要作用是用來區分文件類型,比如塊設備,字符設備,目錄, socket等。
- super_block,指向超級塊,也就是表示文件系統基本信息的數據結構。
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 timespec64 *, int); int (*atomic_open)(struct inode *, struct dentry *, struct file *, unsigned open_flag, umode_t create_mode); int (*tmpfile) (struct inode *, struct dentry *, umode_t); int (*set_acl)(struct inode *, struct posix_acl *, int); }
dentry
目錄項對象,代表一個目錄項。目錄項反應了文件系統的樹狀結構,目前主流的操作系統基本都是用樹狀結構來組織文件的。linux也不例外。dentry表示一個目錄項,目錄項下面又有子目錄。

struct dentry { /* RCU lookup touched fields */ unsigned int d_flags; /* protected by d_lock */ seqcount_t d_seq; /* per dentry seqlock */ struct hlist_bl_node d_hash; /* lookup hash list */ struct dentry *d_parent; /* parent directory */ struct qstr d_name; struct inode *d_inode; /* Where the name belongs to - NULL is * negative */ unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */ /* Ref lookup also touches following */ struct lockref d_lockref; /* per-dentry lock and refcount */ const struct dentry_operations *d_op; struct super_block *d_sb; /* The root of the dentry tree */ unsigned long d_time; /* used by d_revalidate */ void *d_fsdata; /* fs-specific data */ union { struct list_head d_lru; /* LRU list */ wait_queue_head_t *d_wait; /* in-lookup ones only */ }; struct list_head d_child; /* child of parent list */ struct list_head d_subdirs; /* our children */ /* * d_alias and d_rcu can share memory */ union { struct hlist_node d_alias; /* inode alias list */ struct hlist_bl_node d_in_lookup_hash; /* only for in-lookup ones */ struct rcu_head d_rcu; } d_u; }
- d_inode 這個成員志向inode,
- super_block 指向對應的超級塊。
- dentry_operations 這個成員指向dentry對應的操作,如下所示
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 *); }
File
文件對象,代表進程打開的文件,描述的是進程和文件之間的關系。一個文件,在不同的進程中有不同的文件對象,如下圖是文件的數據結構

從文件操作的角度來看文件系統不同結構體之間的關系。
文件系統的主要目的是解決用戶對文件的操作,包括創建,刪除,修改,存放。操作文件需要在內核中創建相對應的進程,有了進程之后,需要知道進程操作的文件是什么。那么反應進程和文件之間關系的就是file。file是實際發生操作時創建出來的對象,存儲在內存里。file內部保存着文件的dentry,也就是目錄項,除此之外還保存着file_operation,也就是對文件的讀寫,打開等操作。通過dentry還可以找到inode,那么就可以對inode執行mkdir等操作。super_block代表了一個文件系統,以及文件了解一個文件系統所需的基本數據。
參考資料:
《深入linux內核架構》
《linux內核探秘》