Linux VFS

翻譯自Linux文檔中的vfs.txtnode

介紹

VFS(Virtual File System)是內核提供的文件系統抽象層,其提供了文件系統的操做接口,能夠隱藏底層不一樣文件系統的實現。linux

Directiry Entry Cache(dcache)

VFS經過open()stat()這些接口傳入的文件名搜索dcache,快速找到文件名對應的dentry。dentry的結構是struct dentry,這只是一個內存結構,不會持久化到磁盤中,僅僅是爲了提高性能而已。api

dentry cache是整個文件空間的視圖,可是大部分狀況下並不能同時將全部dentry都加載到內存。VFS會在執行lookup()時,建立還不在內存中的dentry。數組

The Inode Object

inode表明真實存儲在文件系統中的對象,好比文件、目錄、FIFO等等。inode的結構是struct inode。一個dentry只能指向一個inode,但一個inode能夠被多個dentry指向。緩存

對於塊設備的文件系統,inode存儲在磁盤上,並在須要的時候拷貝到內存中,修改inode後又會寫回磁盤進行持久化。對於僞文件系統,inode保存在內存中。網絡

VFS調用lookup()從指定路徑的第一級目錄的dentry開始查找對應的inode。lookup()的真實實現是由inode所在的底層文件系統提供的。併發

The File Object

打開一個文件實際就是建立一個file對象。file的結構是struct file,其有指向一個dentry的指針,和一組操做file的函數指針。這些信息是從inode中獲取的。在打開文件的最後,file會被加入當前進程的文件描述符表中。app

用戶的讀、寫、關閉文件都經過fd進行,內核會能夠根據fd獲取到對應的file對象,這些操做最終都會調用到底層文件系統提供的函數。框架

註冊和掛載文件系統

註冊和註銷文件系統的函數以下socket

#include <linux/fs.h>

extern int register_filesystem(struct file_system_type *);
extern int unregister_filesystem(struct file_system_type *);

當掛載文件系統時,VFS會調用底層文件系統指定的mount()函數,mount()會返回一個dentry做爲文件系統的根目錄。全部已掛載的文件系統,能夠在/proc/filesystems中看到。

struct file_system_type

該結構用於描述文件系統,自內核2.6.30,有以下定義

struct file_system_type {
    const char *name;
    int fs_flags;
    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 list_head fs_supers;
    struct lock_class_key s_lock_key;
    struct lock_class_key s_umount_key;
};
  • name:文件系統的名字,好比"ext2"等。
  • fs_flags:好比FS_REQUIRES_DEV、FS_NO_DCACHE等一些標誌。
  • mount:掛載文件系統時調用。
  • kill_sb:卸載文件系統時調用。
  • owner:在大部分狀況下,應當初始化爲THIS_MODULE。
  • next:應當初始化爲NULL。
  • s_lock_key,s_umount_key:用於檢查死鎖。

mount()的參數以下

  • struct file_system_type* fs_type:描述文件系統,其中部分底層文件系統初始化。
  • int flags:掛載的標誌,如FS_REQUIRES_DEV,FS_NO_DCACHE等。
  • const char *dev_name:掛載的設備名。
  • void *data:任意的選項,一般是ASCII字符串。

mount()成功時,要求加鎖並得到superblock的活動引用,返回dentry(能夠是子目錄的dentry),失敗時返回ERR_PTR(error)

grab_super()是獲取活動引用的函數,即讓spuer_block::a_active加1

mount()會建立一個superblock,其結構是struct superblockstruct superblock中有一個指向struct super_operations的指針s_op,其由一系列操做文件系統的函數指針組成。

VFS提供了以下方法掛載不一樣類型的文件系統

  • mount_bdev():掛載基於塊設備的文件系統。
  • mount_nodev():掛載不基於設備的文件系統。
  • mount_single():掛載共享實例的文件系統。

這些函數都有一個入參int (*fill_super)(struct super_block *, void *, int),其用於初始化struct superblock的部分字段。

fill_super的參數以下

  • struct super_block *sb:指向superblock。
  • void *data:mount的參數,一般是一個ASCII字符串,須要自行解析。
  • int silent:肯定是否輸出錯誤信息。

The Superblock Object

一個superblock表明了一個已掛載的文件系統。

struct super_operations

VFS經過struct super_operations操做superblock

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 *, int);
        void (*drop_inode) (struct inode *);
        void (*delete_inode) (struct inode *);
        void (*put_super) (struct super_block *);
        int (*sync_fs)(struct super_block *sb, int wait);
        int (*freeze_fs) (struct super_block *);
        int (*unfreeze_fs) (struct super_block *);
        int (*statfs) (struct dentry *, struct kstatfs *);
        int (*remount_fs) (struct super_block *, int *, char *);
        void (*clear_inode) (struct inode *);
        void (*umount_begin) (struct super_block *);

        int (*show_options)(struct seq_file *, struct dentry *);

        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);
    int (*nr_cached_objects)(struct super_block *);
    void (*free_cached_objects)(struct super_block *, int);
};

除非有額外的說明,不然VFS不會加鎖調用這些函數。這意味着這些函數阻塞時,不會影響到其餘線程。全部的函數只會在進程上下文調用,不會在中斷上下文中調用。

  • alloc_inode:用於分配和初始化inode。若是未指定該方法,則使用純粹的struct inode。一般狀況下,文件系統會自定義本身的inode結構,除了包含struct inode外,還會包含和底層文件系統相關的字段。
  • destroy_inode:銷燬inode,必須和alloc_inode一塊兒實現。
  • dirty_inode:將inode標記爲髒inode
  • write_inode:將inode寫回到磁盤。該函數第二個參數用於指定是否同步寫入,並不是全部文件系統都檢查這個標誌
  • drop_inode:用於判斷是否從內存中移除inode。當inode引用計數爲0時調用該函數,調用前會鎖定inode->i_lock。drop_inode應該是NULL(正常UNIX文件系統語義)或者是generic_delete_inode()(不考慮引用計數,強制清除inode,適用於不想緩存inode的文件系統)。
  • delete_inode:從內存中移除inode。v2.6.39的struct super_operations就沒有這個字段,取而代之的是evict_inode
  • put_super:釋放superblock調用前鎖定superblock lock
  • sync_fs:將superblock關聯的髒數據寫回到存儲。第二個參數用於指明是否等待數據寫完後再返回。
  • freeze_fs:鎖定文件系統並迫使它進入一致狀態。目前被邏輯卷管理(LVM)會用到該函數。
  • unfreeze_fs:解鎖文件系統,使其能夠從新寫入。
  • statfs:獲取文件系通通計信息。
  • remount_fs:從新掛載文件系統,主要用於更新掛載參數。調用前鎖定**kernel lock
  • clear_inode:標記再也不使用該inode。v2.6.39的struct super_operations就沒有這個字段
  • umount_begin:卸載文件系統
  • show_options:用於在/proc/ /mounts裏輸出掛載選項
  • quota_read:讀quota file
  • quota_write:寫quota file
  • nr_cached_objects:返回可釋放的對象個數。
  • free_cache_objects:清理對象。須要和nr_cached_objects一塊兒定義

若是這些函數內會執行批量任務,那麼必須包含支持從新調度的函數,這使得VFS無需擔憂這些函數長時間處理的問題。

設置inode時必須初始化struct inodei_op字段,其指向struct inode_operations,該結構包含了一系列操做inode的函數。

struct xattr_handler

當文件系統須要支持擴展屬性時,能夠指定superblocks_xattr字段,其指向一個一NULL結尾的struct xattr_handler數組。擴展屬性是一個name-value對。

struct xattr_handler {
    const char *name;
    const char *prefix;
    int flags;      /* fs private flags */
    bool (*list)(struct dentry *dentry);
    int (*get)(const struct xattr_handler *, struct dentry *dentry,
           struct inode *inode, const char *name, void *buffer,
           size_t size);
    int (*set)(const struct xattr_handler *, struct dentry *dentry,
           struct inode *inode, const char *name, const void *buffer,
           size_t size, int flags);
};
  • name:該結構中的函數用於處理該名字表明的屬性,好比"system.posix_acl_access"。指定name,則prefix必須是NULL
  • prefix:該結構中的函數用於處理該前綴表明的屬性,好比"user."。指定了prefix,則name必須是NULL
  • list:肯定是否應爲特定的dentry列出與此處理函數匹配的屬性。
  • get:獲取擴展屬性的值。該方法在getxattr(2)流程中調用
  • set:設置擴展屬性的值。若是新值爲NULL,則移除擴展屬性。該函數在setxattr(2)removexattr(2)流程中調用

當文件系統沒有xattr的處理方法或者沒有匹配的屬性時,會返回-EOPNOTSUPP

The Inode Object

一個inode表明了一個文件系統對象

struct inode_operations

struct inode_operations描述了VFS如何操做inode

struct inode_operations {
    int (*create) (struct inode *,struct dentry *, umode_t, bool);
    struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
    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 (*readlink) (struct dentry *, char __user *,int);
    const char *(*get_link) (struct dentry *, struct inode *,
                 struct delayed_call *);
    int (*permission) (struct inode *, int);
    int (*get_acl)(struct inode *, 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);
    void (*update_time)(struct inode *, struct timespec *, 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);
};

除非有額外說明,不然全部方法都不會持鎖調用。

  • create:建立inode,由open(2)creat(2)調用。只有當須要支持常規文件時,才實現該函數。入參dentry必須沒有指向任何inode的,最後須要調用d_instantiate()將新建立的inode加入到這個dentry中。
  • lookup:查找inode。
    • 須要查找的inode的名字記錄在dentry中。若是找到了則須要調用d_add()將inode加入到dentry中,而且inode的引用計數要加1。若是inode不存在,則須要將NULL加入到dentry,表明dentry是無效的。
    • 當錯誤發生時,必須返回錯誤。若是隻是將dentry指向NULL,VFS會執行create(2), mknod(2), mkdir(2)去建立inode,但這些函數仍是會由於lookup失敗的緣由而再次失敗。
    • 若是須要從新設置dentry的操做函數,能夠給d_dop賦值。這些函數被調用時,會使用目錄inode的信號量。
  • link:建立硬連接。和create同樣,該函數中須要調用d_instantiate()
  • unlink:支持刪除inode。
  • symlink:建立符號連接。
  • mkdir:建立目錄。該函數中也須要調用d_instantiate()
  • rmdir:刪除目錄。
  • mknod:建立設備inode、命名管道或socket。該函數中也須要調用d_instantiate()
  • rename:重命名。當文件系統不支持某些方法和flag時,必須返回-EINVAL。當前如下flag已經實現
    • RENAME_NOREPLACE:若是新文件名已經存在,則應當返回-EEXIST。可是目前VFS已經檢查了新文件名是否存在。
    • RENAME_EXCHANGE:兩個文件必須都存在,只是交換一下文件名。
  • get_link:獲取符號連接所指向的inode。
  • readlink:讀取符號連接所指向的inode的文件名。正常狀況下,文件系統只須要實現get_link。
  • permission:檢查類POSIX文件系統的訪問權限。可能會在rcu-walk模式下調用。若是在rcu-walk模式下,文件系統必須在不阻塞或者不存儲inode的狀況下檢查權限。若是在rcu-walk模式下,發生一個沒法處理的狀況,則返回-ECHILD,此後會在ref-walk模式下再嘗試一次。
  • setattr:設置文件屬性,由chmod(2)和相關的系統調用觸發。
  • getattr:獲取文件屬性,由stat(2)和相關的系統調用觸發。
  • listxattr:由VFS調用列出一個文件的全部擴展屬性,由listxattr(2)觸發。
  • update_time:更新inode的時間(atime、ctime、mtime)或i_version。若是該方法未定義,那麼VFS會自行更新inode,而後調用mark_inode_dirty_sync()標記該inode爲髒inode
  • atomic_open:在open操做的最後調用。使用這個可選的方法,文件系統能夠原子性地查找、建立、打開一個文件。若是該方法想讓調用者去打開文件,則應當經過finish_no_open()通知調用者。該方法只在最後一步是無效或者須要lookup時才調用。緩存有效的dentry須要在f_op->open()中完成。若是文件建立成功了,則須要在file->f_mode設置FMODE_CREATED。若是指定了O_EXCL,該方法須要在文件存在時返回失敗。返回成功就設置FMODE_CREATED
  • tmpfile:在open(O_TMPFILE)的最後被調用。這是一個可選的方法,等同於在一個指定的目錄下,原子性地建立、打開、刪除一個文件。

The Address Space Object

address space對象用於組織和管理page cache中的page。它能夠追蹤一個文件使用的page以及一個文件被映射到進程空間的page。它能夠根據地址查找page,追蹤被標記爲Dirty或Writeback的page。

VM能夠調用writepage()來清理髒頁,或者設置PagePrivate後調用releasepage()來釋放乾淨的頁以便從新使用。若是乾淨的頁沒有設置PagePrivate或者沒有外部引用,就會在沒有通知address space的狀況下被釋放掉。爲了實現這些功能,page須要經過lru_cache_add()方法放到LRU中,當page被使用時須要調用mark_page_active()

page使用一個基數樹(radix tree)來保存。這棵樹維護了全部page的PG_DiryPG_Writeback狀態,所以這些頁能被快速找到。

mpage_writepages()是默認的writepages,它使用Dirty標記查找髒頁並寫回。若是沒有使用mpage_writepages()(多是address space提供了本身的writepage),那麼PAGECACHE_TAG_DIRTY標記就不會被使用。write_inode_now()sync_inode()使用PAGECACHE_TAG_DIRTY來檢查writepages是否將整個address space寫回到存儲中。

filemap*wait*sync_page*會使用Writeback標記,並經過filemap_fdatawait_range()等待全部寫回操做完成。

address space的處理方法能夠經過page::private附加一些額外的信息到page中。若是附加了信息,那麼必須設置PG_Private。VM會調用address space的額外函數來處理這些數據。

address space做爲存儲和應用之間的中間層。每次從存儲讀取一頁的數據到address space中,供應用讀取。應用能夠將任意大小的數據寫入address space,但最後以頁爲單位寫入到存儲中。

讀進程只須要readpage。寫進程則複雜一點,須要write_begin/write_end將數據寫入到address space,還須要writepagewritepages將數據寫到存儲中。

address space增刪頁受inode::i_mutex的保護。

當數據被寫入到page中時,須要設置PG_Dirtyset_page_dirty())。當須要writepages時會設置PG_Writeback並清除PG_Dirty。標記爲PG_Writeback的page能夠在任意時間寫回到存儲,一旦寫回完成後,該標誌被清除。

寫回會使用struct writeback_controlwritepagewritepages提供寫回的請求和限制信息,該結構也用於返回寫回的結果。

處理寫回的錯誤

大部分使用緩存IO的應用都會週期性地調用同步接口(如fsync()fdatasync()msync()sync_file_rage())來保證數據被寫回到存儲中。若是寫回時發生錯誤,這些接口應當返回錯誤,但僅在第一次返回錯誤,後續應當返回0,除非又有新的數據寫入且再次調用同步接口時又發生寫回錯誤。

理想狀況下,內核應當以文件爲單位,返回其寫回的錯誤。但實際上page cache並無以文件爲單位追蹤髒頁,所以當發生寫回錯誤時,內核沒法知道是哪一個文件發生的寫回錯誤。當前當發生寫回錯誤時,內核給全部打開的文件都返回錯誤,即便這個文件並無寫入或者已經寫回成功了。

想使用該框架的文件系統應當調用mapping_set_error()來記錄錯誤。在寫回數據後,文件系統還應當調用file_check_and_advance_wb_err()來確保struct file::f_wb_err記錄的是正確的寫回錯誤。

struct address_space_operations

VFS使用該結構操做文件的page cache。

struct address_space_operations {
    int (*writepage)(struct page *page, struct writeback_control *wbc);
    int (*readpage)(struct file *, struct page *);
    int (*writepages)(struct address_space *, struct writeback_control *);
    int (*set_page_dirty)(struct page *page);
    int (*readpages)(struct file *filp, struct address_space *mapping,
            struct list_head *pages, unsigned nr_pages);
    int (*write_begin)(struct file *, struct address_space *mapping,
                loff_t pos, unsigned len, unsigned flags,
                struct page **pagep, void **fsdata);
    int (*write_end)(struct file *, struct address_space *mapping,
                loff_t pos, unsigned len, unsigned copied,
                struct page *page, void *fsdata);
    sector_t (*bmap)(struct address_space *, sector_t);
    void (*invalidatepage) (struct page *, unsigned int, unsigned int);
    int (*releasepage) (struct page *, int);
    void (*freepage)(struct page *);
    ssize_t (*direct_IO)(struct kiocb *, struct iov_iter *iter);
    /* isolate a page for migration */
    bool (*isolate_page) (struct page *, isolate_mode_t);
    /* migrate the contents of a page to the specified target */
    int (*migratepage) (struct page *, struct page *);
    /* put migration-failed page back to right list */
    void (*putback_page) (struct page *);
    int (*launder_page) (struct page *);

    int (*is_partially_uptodate) (struct page *, unsigned long,
                    unsigned long);
    void (*is_dirty_writeback) (struct page *, bool *, bool *);
    int (*error_remove_page) (struct mapping *mapping, struct page *page);
    int (*swap_activate)(struct file *);
    int (*swap_deactivate)(struct file *);
};
  • writepage:基於數據完整性(sync)或者釋放內存(flush)的緣由,VM會調用該方法將髒頁寫回到存儲。寫回過程:清除PG_Dirty,設置PageLockedtrue,而後writepage開始寫回,並設置PG_Writeback,而後在完成寫回後解鎖page。
    • 若是wbc->sync_modeWB_SYNC_NONE,那麼writepage在沒法完成寫回指定頁的狀況下,返回AOP_WRITEPAGE_ACTIVATE,這樣VM就不會一直爲該頁調用writepage了。
  • readpage:VM調用該方法從存儲中讀取一頁的數據。該頁會被鎖定,但須要在讀取完成後,標記爲uptodate並解鎖。若是readpage須要解鎖,也能夠解鎖,並返回AOP_TRUNCATED_PAGE。在這種狀況下,VM會從新安置和加鎖該頁,再次調用readpage
  • writepages:VM調用該方法將address space相關聯的頁都寫回到存儲。若是若是wbc->sync_modeWBC_SYNC_ALL,那麼writeback_control就會指定一組必須寫回的頁。若是是WBC_SYNC_NONE的話,則只要求儘量寫入nr_to_write頁。若是該方法未定義,那麼會用mpage_writepages來替代。該方法會從address space中獲取全部標記爲DIRTY的頁,而後傳遞給writepage
  • set_page_dirty:VM調用該方法設置髒頁。只有在address space中有私有數據的page且在頁變髒時須要更新這些私有數據時,才須要該方法。若是定義了該方法,那麼它必須設置PageDirty標誌和基數樹中的PAGECACHE_TAG_DIRTY標誌。
  • readpages:VM調用該方法讀取address space相關聯的頁。這個方法本質上是屢次調用readpage。readpages只用於預讀,所以讀取錯誤被忽略了。
  • write_begin:由generic_perform_write()調用,用於告知文件系統準備在指定偏移處寫len字節的數據。address space應預留一些資源保證完成此次寫入操做。若是寫入要更新頁中的一部分,那麼須要將整頁塊讀取到內存中,即讀改寫。文件系統經過pagep返回page cache中已鎖定的page。調用者會將數據寫入到該page中。須要支持實際寫入的數據長度小於傳給write_begin的長度的場景。可經過fsdata保存須要傳遞給write_end的數據。
  • write_end:該函數必須在成功調用write_begin和拷貝數據後被調用。len是傳遞給write_begin的len,copied則是實際拷貝的長度。文件系統必須釋放page的鎖和引用,並更新struct inode::i_size。失敗返回小於0,不然返回實際拷貝到page cache的長度。
  • bmap:VFS調用該方法將邏輯塊偏移映射爲物理塊序號。該方法用於ioctl(FIBMAP)和swap文件。爲了swap一個文件,文件必須固定地映射到一個塊設備上。swap系統會經過bmap來找到文件所在的塊,而後直接存儲,而不經過文件系統。
  • invalidatepage:若是page設置了PagePrivate,那麼當部分或所有的page從address space中被移除時,就會調用invalidatepage。
  • releasepage:釋放標記爲PagePrivate的page,該方法須要移除page的私有數據,並清除PagePrivate。若是該方法失敗了則返回0。releasepage用於兩種場景
    • VM發現一個沒有活動用戶的乾淨page,並向釋放該page。若是釋放成功,那麼該頁就會從address space中移除,變爲自由的page。
    • 須要讓address space中的部分或所有page失效。這種場景由fadvise(POSIX_FADV_DONTNEED)觸發,或者文件系統明確請求(好比當nfs和9fs認爲cache的數據已經與存儲不一致時,會調用invalidate_inode_pages2()
  • freepage:一旦page在page cache中不可見時,爲了容許清除私有數據,就會調用該方法。由於該方法可能被內存回收者調用,因此該方法不能假設原始的address space還存在,也不該當阻塞。
  • direct_IO:由generic read/write類函數調用來執行direct IO。
  • isolate_page:VM在須要隔離一個可移動的非LRU的page時調用。若是成功了,VM會經過__SetPageIsolated將該頁標記爲PG_isolated
  • migrate_page:該方法用於壓縮物理內存使用量。若是VM想從新放置page(多是內存卡的故障信號觸發的),就會傳遞一個老page和一個新page給這個方法。該方法須要傳輸私有數據,並更新全部引用。
  • putback_page:當已隔離的頁遷移失敗時,VM會調用該方法。
  • launder_page:在釋放頁前調用。該方法將髒頁寫回存儲並避免從新弄髒該頁,在整個過程當中,都會加鎖。
  • is_partially_uptodate:塊大小不等於頁大小,一頁可能包含多個塊。若是VM讀取到所需的塊數據,那麼就無需等待整個頁讀取完畢。
  • is_dirty_writeback:當VM想回收page時調用。VM會根據dirty和writeback的值決定是否須要停頓回收頁,以便能完成某些IO。一般狀況下,VM可使用PageDirtyPageWriteback,可是某些文件系統會有更復雜的狀態(好比NFS的unstable pages須要避免被回收),或者由於鎖的問題沒有設置這些標誌。該方法能夠向VM代表該頁是髒頁或正在寫回的頁,讓VM中止回收該頁。
  • error_remove_page:若是truncation是正常的話,一般設置爲generic_error_remove_page()。主要用於處理內存失敗。實現該方法,意味着你會處理那些頁,除非你已經鎖定或者增長了引用計數。
  • swap_activate:當文件使用了swapon時調用,用於分配空間並將塊的查詢信息保存在內存中。返回0表明成功,也意味着該文件能夠被當作備份的交換空間。
  • swap_deactivate:當swap_activate成功後,調用該方法使得該文件變爲swapoff。

The File Object

一個file對象表明進程打開的一個文件。

struct file_operations

VFS使用struct file_operations操做一個打開的文件。

自v4.18,struct file_operations定義以下

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 (*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 *);
    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);
    int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t, u64);
    int (*dedupe_file_range)(struct file *, loff_t, struct file *, loff_t, u64);
    int (*fadvise)(struct file *, loff_t, loff_t, int);
};

除非額外說明,不然這些函數都不會持鎖調用。

  • read_iter:支持將文件數據讀取到非連續的內存中
  • write_iter:支持將非連續內存中的數據寫入到文件中。
  • iterate:讀取目錄內容
  • iterate_shared:當文件系統支持併發的目錄迭代時,使用該函數讀取目錄內容
  • compat_ioctl:在64位內核上兼容32位的系統調用
  • open:建立一個新的struct file,並初始化strutc file::private_data
  • flush:由close(2)調用
  • release:當文件的引用計數爲0時調用
  • fasync:文件爲非阻塞模式時,由fcntl(2)調用
  • lock:由fcntl(2)調用,執行F_GETLKF_SETLKF_SETLKW命令
  • fallocate:預分配block

Directory Entry Cache (dcache)

dentry屬於VFS和單個文件系統,與設備驅動無關。每一個dentry都有一個指向其父dentry的指針,以及一個子dentry的hash鏈表。

struct dentry_operations

struct dentry_operations是可選的,能夠設置爲NULL,或是使用VFS默認的函數。

v2.6.22中,其定義以下

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_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 *);
};
  • d_revalidate:當在cache中找到dentry時,判斷dentry是否有效,返回正數表明還有效,返回0或負數表明無效。大多數本地文件系統將其設爲NULL,由於它們的dentry老是有效的。網絡文件系統則不一樣,由於服務端的變動,客戶端可能感知不到。
  • d_weak_revalidate
  • d_hash:計算hash值,根據hash值加入父dentry的hash表中
  • d_compare:比較dentry的名字,必須是常熟且冪等
  • d_delete:判斷是否刪除dentry。返回1表示當即刪除,返回0表明緩存。d_delete必須是常熟且冪等
  • d_init:當分配dentry後調用,初始化dentry
  • d_release:釋放dentry
  • d_iput:歸還inode引用(在d_release前調用)。若是是NULL,VFS會調用iput(),不然須要自行調用iput()
  • d_dname:當須要生成dentry的路徑名時調用。對於僞文件系統(sockfs,pipefs)延遲生成路徑名來講頗有用。由於沒有加鎖,因此d_dname不能修改dentry自己

Directory Entry Cache API

如下是操做dentry的函數

  • dget():獲取一個已存在的dentry的引用
  • dput():歸還一個dentry的引用。若是引用計數爲0,且該dentry還在其父dentry的hash表中,則調用d_delete()檢查該dentry是否還應該緩存。若是須要緩存,則放入LRU鏈表中。
  • d_drop():從父dentry的hash表中刪除dentry
  • d_delete():刪除一個dentry,若是沒有其餘引用,則該dentry變爲一個無效的dentry(即指向NULL inode),d_iput()就會被調用。若是還有引用,則調用d_drop()
  • d_add():將dentry加入到其父dentry的hash表中,而後調用d_instantiate()
  • d_instantiate():將dentry加入到inode的dentry鏈表中,並更新dentry指向的inode(即struct dentry::d_inode)。inode的引用計數也會增長。
  • d_lookup():查找dentry,若是找到了則增長引用計數後返回。
相關文章
相關標籤/搜索