虛擬文件系統(tǒng)(VFS)作為內(nèi)核子系統(tǒng),為用戶空間程序提供了文件和文件系統(tǒng)相關(guān)的接口。系統(tǒng)中所有的文件系統(tǒng)不但依賴VFS共存,而且依靠VFS系統(tǒng)協(xié)同工作。通過VFS,程序可以利用標(biāo)準(zhǔn)Unix系統(tǒng)調(diào)用對不同的文件系統(tǒng),甚至不同介質(zhì)上的文件系統(tǒng)進(jìn)行讀寫操作:

一、通用文件系統(tǒng)接口
VFS使得用戶可以直接通過open、read、write等系統(tǒng)調(diào)用而無需考慮具體文件系統(tǒng)和實際物理介質(zhì)。
二、文件系統(tǒng)抽象層
為了支持多文件系統(tǒng),VFS提供了一個通用文件系統(tǒng)模型,該模型囊括了任何文件系統(tǒng)的常用功能集和行為,VFS定義了所有文件系統(tǒng)都支持的、基本的、概念上的接口和數(shù)據(jù)結(jié)構(gòu)。實際文件系統(tǒng)通過編程提供VFS所期望的抽象接口和數(shù)據(jù)結(jié)構(gòu),這樣,內(nèi)核就可以毫不費力地和任何文件系統(tǒng)協(xié)同工作,并且這樣提供給用戶空間的接口,也可以和任何文件系統(tǒng)無縫地連接在一起,完成實際工作。

三、Unix文件系統(tǒng)
Unix使用了四種和文件系統(tǒng)相關(guān)的傳統(tǒng)抽象概念:
- 文件
- 目錄項:/home/wolfman/butter中,/、home、wolfman、butter都是目錄項
- 索引節(jié)點
- 安裝點
Unix系統(tǒng)將文件的相關(guān)信息和文件本身區(qū)分開,文件相關(guān)信息(也就是元數(shù)據(jù))被存儲在一個單獨的數(shù)據(jù)結(jié)構(gòu)中,稱為索引節(jié)點(inode)。文件系統(tǒng)的控制信息存儲在超級塊中,超級塊是一種包含文件系統(tǒng)信息的數(shù)據(jù)結(jié)構(gòu)。
四、VFS對象及其數(shù)據(jù)結(jié)構(gòu)
VFS中有四個主要對象類型:
- 超級塊對象:代表一個具體的已安裝文件系統(tǒng),包含super_operations操作對象
- 索引節(jié)點對象:代表一個具體文件,包含inode_operations操作對象
- 目錄項對象:代表一個目錄項,是路徑組成部分,包含dentry_operations操作對象
- 文件對象:代表由進(jìn)程打開的文件,包含file_operations操作對象
操作對象作為一個結(jié)構(gòu)體指針來實現(xiàn),此結(jié)構(gòu)體中包含指向操作其父對象的函數(shù)指針。
五、超級塊對象
各種文件系統(tǒng)都必須實現(xiàn)超級塊對象,該對象用于存儲特定文件系統(tǒng)的信息,通常對應(yīng)于存在磁盤特定扇區(qū)的文件系統(tǒng)超級塊或文件系統(tǒng)控制塊。對于非基于磁盤的文件系統(tǒng),會在使用現(xiàn)場創(chuàng)建超級塊并將其保存到內(nèi)存中。
在文件系統(tǒng)安裝時,文件系統(tǒng)會調(diào)用alloc_super()函數(shù)從磁盤讀取文件系統(tǒng)超級塊,并將其信息填充到內(nèi)存中的超級塊對象中。
超級塊操作函數(shù)表:
struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *);
void (*free_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 (*freeze_super) (struct super_block *);
int (*freeze_fs) (struct super_block *);
int (*thaw_super) (struct super_block *);
int (*unfreeze_fs) (struct super_block *);
int (*statfs) (struct dentry *, struct kstatfs *);
int (*remount_fs) (struct super_block *, int *, char *);
void (*umount_begin) (struct super_block *);
int (*show_options)(struct seq_file *, struct dentry *);
int (*show_devname)(struct seq_file *, struct dentry *);
int (*show_path)(struct seq_file *, struct dentry *);
int (*show_stats)(struct seq_file *, struct dentry *);
#ifdef CONFIG_QUOTA
ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
struct dquot **(*get_dquots)(struct inode *);
#endif
int (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t);
long (*nr_cached_objects)(struct super_block *,
struct shrink_control *);
long (*free_cached_objects)(struct super_block *,
struct shrink_control *);
};
六、索引節(jié)點對象
索引節(jié)點對象包含了內(nèi)核在操作文件或目錄時需要的全部信息。索引節(jié)點對象必須在內(nèi)存中創(chuàng)建,以便文件系統(tǒng)使用。一個索引節(jié)點代表文件系統(tǒng)中的一個文件(索引節(jié)點僅當(dāng)文件被訪問時才在內(nèi)存中創(chuàng)建),可以是設(shè)備或管道等特殊文件。
索引節(jié)點操作:
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);
} ____cacheline_aligned;
七、目錄項對象
為了查找操作方便,VFS引入了目錄項概念。每個dentry代表路徑中的一個特定部分。目錄項對象沒有對應(yīng)的磁盤數(shù)據(jù)結(jié)構(gòu),VFS根據(jù)字符串形式的路徑名現(xiàn)場創(chuàng)建。
目錄項對象有三種有效狀態(tài):被使用、未被使用和負(fù)狀態(tài)。
內(nèi)核將目錄項對象緩存在目錄項緩存(dcache)中,dcache中包含三部分:
- 被使用的目錄項鏈表
- 最近被使用的雙向鏈表
- 散列表和相應(yīng)的散列函數(shù)用來快速地將給定路徑解析為相關(guān)目錄項對象
目錄項操作:
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 *);
} ____cacheline_aligned;
八、文件對象
文件對象表示進(jìn)程已打開的文件,因為多個進(jìn)程可以同時打開和操作同一個文件,所以同個文件也可能存在多個對應(yīng)的文件對象,但對應(yīng)的索引節(jié)點和目錄項對象是唯一的。文件對象沒有對應(yīng)的磁盤數(shù)據(jù)。
文件對象操作:
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iopoll)(struct kiocb *kiocb, bool spin);
int (*iterate) (struct file *, struct dir_context *);
int (*iterate_shared) (struct file *, struct dir_context *);
__poll_t (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
unsigned long mmap_supported_flags;
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
loff_t, size_t, unsigned int);
loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out,
loff_t len, unsigned int remap_flags);
int (*fadvise)(struct file *, loff_t, loff_t, int);
} __randomize_layout;