第13章 虛擬文件系統(tǒng)

虛擬文件系統(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)行讀寫操作:


image.png

一、通用文件系統(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)無縫地連接在一起,完成實際工作。


image.png

三、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;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容