APUE——文件系統的VFS以及相關結構體簡要分析

參考鏈接如下:
https://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/#fig2
https://blog.csdn.net/Ohmyberry/article/details/80427492
https://blog.csdn.net/shanshanpt/article/details/38943731
https://www.cnblogs.com/LittleHann/p/3865490.html
https://www.cnblogs.com/LittleHann/p/4305892.html
https://www.cnblogs.com/fengkang1008/p/4691231.html
https://www.cnblogs.com/linux-xin/p/8126999.html

1. 圖示文件系統

1.1經典的文件系統

在這裏插入圖片描述
需要注意幾點:

  1. dentry 本身目錄塊也屬於數據塊
  2. inode指向實際block,其屬於i節點區
  3. 目錄塊本身包含inode指針和文件名

1.2 對文件系統結構體分析

精力有限,湊合看看,結構體很長,選了一些,例如:
標準IO的fread函數, size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) ,實際調用read,其中需要注意FILE 與 fd 的差異,fd爲fd_array的數組索引號,實際存放file指針,通過file指針調用f_op的函數
在這裏插入圖片描述
在這裏插入圖片描述

2. VFS

如下兩張圖能直觀反映Linux下文件系統中VFS所起的作用,上接系統調用,下接真實文件系統,類似於autosar裏的RTE,使得不同的文件系統在Linux裏共存,且同一個API可以完成對文件系統的操作。
在這裏插入圖片描述在這裏插入圖片描述
VFS抽象了文件、目錄項 (dentry)、索引節點 (inode) 及掛載點,其主要有如下四個部分組成:

  1. 超級塊對象 (superblock object)
  2. 索引節點對象 (inode object)、
  3. 目錄項對象 (dentry object) 、
  4. 文件對象 (file object)

上述四個模塊在VFS中聯繫如下圖所示:
在這裏插入圖片描述

2.1 superblock

超級塊就是對所有文件系統的管理機構,主要管理block和inode等,記錄保存文件系統的類型、大小、狀態等等,每種文件系統都要把自己的信息掛到super_blocks這麼一個全局鏈表上,超級塊與文件系統一一對應,同一個文件系統類型可能含有多個超級塊。

  1. block 與inode 的總量;
  2. 未使用與已使用的inode / block 數量;
  3. block 與inode 的大小(block 爲1, 2, 4K,inode 爲128bytes 或256bytes);
  4. filesystem 的掛載時間、最近一次寫入資料的時間、最近一次檢驗磁盤(fsck) 的時間等檔案系統的相關信息;
  5. 一個valid bit 數值,若此檔案系統已被掛載,則valid bit 爲0 ,若未被掛載,則valid bit 爲1 。
    “文件系統”和“文件系統類型”不一樣!一個文件系統類型下可以包括很多文件系統即很多的super_block

具體代碼和解釋部分,參考https://www.cnblogs.com/LittleHann/p/3865490.html

struct super_block 
{
    /* 
    Keep this first 
    指向超級塊鏈表的指針,用於將系統中所有的超級塊聚集到一個鏈表中,該鏈表的表頭是全局變量super_blocks
    */
    struct list_head    s_list;

    /* 
    search index; _not_ kdev_t 
    設備標識符
    */        
    dev_t            s_dev;        

    //以字節爲單位的塊大小
    unsigned long        s_blocksize;

    //以位爲單位的塊大小
    unsigned char        s_blocksize_bits;

    //修改髒標誌,如果以任何方式改變了超級塊,需要向磁盤迴寫,都會將s_dirt設置爲1,否則爲0
    unsigned char        s_dirt;

    //文件大小上限 Max file size
    loff_t            s_maxbytes;     

    //文件系統類型
    struct file_system_type    *s_type; 

    /*
    struct super_operations 
    {
        //給定的超級塊下創建和初始化一個新的索引節點對象; 
        struct inode *(*alloc_inode)(struct super_block *sb);

        //用於釋放給定的索引節點; 
        void (*destroy_inode)(struct inode *);

        //VFS在索引節點髒(被修改)時會調用此函數,日誌文件系統(如ext3,ext4)執行該函數進行日誌更新; 
        void (*dirty_inode) (struct inode *);

        //用於將給定的索引節點寫入磁盤,wait參數指明寫操作是否需要同步; 
        int (*write_inode) (struct inode *, struct writeback_control *wbc);

        //在最後一個指向索引節點的引用被釋放後,VFS會調用該函數,VFS只需要簡單地刪除這個索引節點後,普通Uinx文件系統就不會定義這個函數了;
        void (*drop_inode) (struct inode *);

        //用於從磁盤上刪除給定的索引節點; 
        void (*delete_inode) (struct inode *);

        //在卸載文件系統時由VFS調用,用來釋放超級塊,調用者必須一直持有s_lock鎖;
        void (*put_super) (struct super_block *);

        //用給定的超級塊更新磁盤上的超級塊。VFS通過該函數對內存中的超級塊和磁盤中的超級塊進行同步。調用者必須一直持有s_lock鎖; 
        void (*write_super) (struct super_block *);

        //使文件系統的數據元與磁盤上的文件系統同步。wait參數指定操作是否同步; 
        int (*sync_fs)(struct super_block *sb, int wait);
        int (*freeze_fs) (struct super_block *);
        int (*unfreeze_fs) (struct super_block *);

         //VFS通過調用該函數獲取文件系統狀態。指定文件系統縣官的統計信息將放置在statfs中; 
        int (*statfs) (struct dentry *, struct kstatfs *);

        //當指定新的安裝選項重新安裝文件系統時,VFS會調用該函數。調用者必須一直持有s_lock鎖; 
        int (*remount_fs) (struct super_block *, int *, char *);

        //VFS調用該函數釋放索引節點,並清空包含相關數據的所有頁面; 
        void (*clear_inode) (struct inode *);

        //VFS調用該函數中斷安裝操作。該函數被網絡文件系統使用,如NFS; 
        void (*umount_begin) (struct super_block *);

        int (*show_options)(struct seq_file *, struct vfsmount *);
        int (*show_stats)(struct seq_file *, struct vfsmount *);
        #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);
        #endif
        int (*bdev_try_to_free_page)(struct super_block*,
        struct page*, gfp_t);
    };
    */
    const struct super_operations    *s_op;

    //磁盤限額方法
    const struct dquot_operations    *dq_op;

    //磁盤限額方法
    const struct quotactl_ops    *s_qcop;

    //導出方法
    const struct export_operations *s_export_op;

    //掛載標誌 
    unsigned long        s_flags;

    //文件系統魔數
    unsigned long        s_magic;

    //目錄掛載點,s_root將超級塊與全局根目錄的dentry項關聯起來,只有通常可見的文件系統的超級塊,才指向/(根)目錄的dentry實例。具有特殊功能、不出現在通常的目錄層次結構中的文件系統(例如管道或套接字文件系統),指向專門的項,不能通過普通的文件命令訪問。處理文件系統對象的代碼經常需要檢查文件系統是否已經裝載,而s_root可用於該目的,如果它爲NULL,則該文件系統是一個僞文件系統,只在內核內部可見。否則,該文件系統在用戶空間中是可見的
    struct dentry        *s_root;

    //卸載信號量
    struct rw_semaphore    s_umount;

    //超級塊信號量
    struct mutex        s_lock;

    //引用計數
    int            s_count;

    //尚未同步標誌
    int            s_need_sync;

    //活動引用計數
    atomic_t        s_active;
#ifdef CONFIG_SECURITY
    //安全模塊
    void                    *s_security;
#endif
    struct xattr_handler    **s_xattr;

    //all inodes 
    struct list_head    s_inodes;    

    //匿名目錄項 anonymous dentries for (nfs) exporting 
    struct hlist_head    s_anon;        

    //被分配文件鏈表,列出了該超級塊表示的文件系統上所有打開的文件。內核在卸載文件系統時將參考該列表,如果其中仍然包含爲寫入而打開的文件,則文件系統仍然處於使用中,卸載操作失敗,並將返回適當的錯誤信息
    struct list_head    s_files;

    /* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */
    struct list_head    s_dentry_lru; 

    //unused dentry lru of dentry on lru 
    int            s_nr_dentry_unused;

    //指向了底層文件系統的數據所在的相關塊設備
    struct block_device    *s_bdev;
    struct backing_dev_info *s_bdi;
    struct mtd_info        *s_mtd;

    //該類型文件系統
    struct list_head    s_instances;

    //限額相關選項 Diskquota specific options 
    struct quota_info    s_dquot;     

    int            s_frozen;
    wait_queue_head_t    s_wait_unfrozen;

    //文本名字 Informational name 
    char s_id[32];                 

    //Filesystem private info 
    void             *s_fs_info;
    fmode_t            s_mode;

    /*
     * 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;

    /*
     * Filesystem subtype.  If non-empty the filesystem type field
     * in /proc/mounts will be "type.subtype"
     */
    char *s_subtype;

    /*
     * Saved mount options for lazy filesystems using
     * generic_show_options()
     */
    char *s_options;
};

2.2 inode

inode保存着文件大小,設備標識符,用戶標識符,用戶組標識符,文件模式,擴展屬性,文件讀取或修改的時間戳,鏈接數量,指向存儲該內容的磁盤區塊的指針,文件分類等等。

inode分磁盤inode和vfs內存inode,內核會把磁盤inode讀入內存,加上點東西形成內存inode,i_links_count爲磁盤inode計數,i_nlink爲內存inode計數。

inode 號(inode 是文件元數據的一部分但其並不包含文件名,inode 號即索引節點號)是文件的唯一標識而非文件名。文件名僅是爲了方便人們的記憶和使用,系統或程序通過 inode 號尋找正確的文件數據塊。

inode需要注意兩個參數,i_count 與 i_nlink,i_count 爲引用計數,i_nlink爲硬鏈接計數,每個dentry都有一個唯一的inode,而每個inode則可能有多個dentry,由於硬鏈接是有着相同 inode 號僅文件名不同的文件,因此硬鏈接存在以下幾點特性:

  1. 文件有相同的 inode 及 data block;
  2. 只能對已存在的文件進行創建;
  3. 不能交叉文件系統進行硬鏈接的創建;
  4. 不能對目錄進行創建,只可對文件創建;
  5. 刪除一個硬鏈接文件並不影響其他有相同 inode 號的文件。
    軟鏈接與硬鏈接不同,若文件用戶數據塊中存放的內容是另一文件的路徑名的指向,則該文件就是軟連接
    在這裏插入圖片描述
struct inode 
{    
    /*
    哈希表 
    */
    struct hlist_node    i_hash;

    /*
    索引節點鏈表(backing dev IO list)
    */
    struct list_head    i_list;     
    struct list_head    i_sb_list;

    /*
    目錄項鍊表
    */
    struct list_head    i_dentry;

    /*
    節點號
    */
    unsigned long        i_ino;

    /*
    引用記數
    */
    atomic_t        i_count;

    /*
    硬鏈接數
    */
    unsigned int        i_nlink;

    /*
    使用者id
    */
    uid_t            i_uid;

    /*
    使用者所在組id
    */
    gid_t            i_gid;

    /*
    實設備標識符
    */
    dev_t            i_rdev;

    /*
    版本號
    */
    u64            i_version;

    /*
    以字節爲單位的文件大小
    */
    loff_t            i_size;
#ifdef __NEED_I_SIZE_ORDERED
    seqcount_t        i_size_seqcount;
#endif
    /*
    最後訪問時間
    */
    struct timespec        i_atime;

    /*
    最後修改(modify)時間
    */
    struct timespec        i_mtime;

    /*
    最後改變(change)時間
    */
    struct timespec        i_ctime;

    /*
    文件的塊數
    */
    blkcnt_t        i_blocks;

    /*
    以位爲單位的塊大小
    */ 
    unsigned int        i_blkbits;
    
    /*
    使用的字節數
    */
    unsigned short          i_bytes;

    /*
    訪問權限控制
    */
    umode_t            i_mode;
    
    /*
    自旋鎖 
    */
    spinlock_t        i_lock;     
    struct mutex        i_mutex;

    /*
    索引節點信號量
    */
    struct rw_semaphore    i_alloc_sem;

    /*
    索引節點操作表
    索引節點的操作inode_operations定義在linux/fs.h
    struct inode_operations 
    {
        /*
        1. VFS通過系統調用create()和open()來調用該函數,從而爲dentry對象創建一個新的索引節點。在創建時使用mode制定初始模式
        */
        int (*create) (struct inode *, struct dentry *,int); 
        /*
        2. 該函數在特定目錄中尋找索引節點,該索引節點要對應於dentry中給出的文件名
        */
        struct dentry * (*lookup) (struct inode *, struct dentry *); 
        /*
        3. 該函數被系統調用link()調用,用來創建硬連接。硬鏈接名稱由dentry參數指定,連接對象是dir目錄中ld_dentry目錄想所代表的文件
        */
        int (*link) (struct dentry *, struct inode *, struct dentry *); 
        /*
        4. 該函數被系統調用unlink()調用,從目錄dir中刪除由目錄項dentry制動的索引節點對象
        */
        int (*unlink) (struct inode *, struct dentry *); 
        /*
        5. 該函數被系統調用symlik()調用,創建符號連接,該符號連接名稱由symname指定,連接對象是dir目錄中的dentry目錄項
        */
        int (*symlink) (struct inode *, struct dentry *, const char *); 
        /*
        6. 該函數被mkdir()調用,創建一個新路徑。創建時使用mode制定的初始模式
        */
        int (*mkdir) (struct inode *, struct dentry *, int); 
        /*
        7. 該函數被系統調用rmdir()調用,刪除dir目錄中的dentry目錄項代表的文件
        */
        int (*rmdir) (struct inode *, struct dentry *); 
        /*
        8. 該函數被系統調用mknod()調用,創建特殊文件(設備文件、命名管道或套接字)。要創建的文件放在dir目錄中,其目錄項問dentry,關聯的設備爲rdev,初始權限由mode指定
        */
        int (*mknod) (struct inode *, struct dentry *, int, dev_t); 
        /*
        9. VFS調用該函數來移動文件。文件源路徑在old_dir目錄中,源文件由old_dentry目錄項所指定,目標路徑在new_dir目錄中,目標文件由new_dentry指定
        */
        int (*rename) (struct inode *, struct dentry *, struct inode *, struct dentry *); 
        /*
        10. 該函數被系統調用readlink()調用,拷貝數據到特定的緩衝buffer中。拷貝的數據來自dentry指定的符號鏈接,最大拷貝大小可達到buflen字節
        */
        int (*readlink) (struct dentry *, char *, int); 
        /*
        11. 該函數由VFS調用,從一個符號連接查找他指向的索引節點,由dentry指向的連接被解析
        */
        int (*follow_link) (struct dentry *, struct nameidata *); 
        /*
        12. 在follow_link()調用之後,該函數由vfs調用進行清楚工作
        */
        int (*put_link) (struct dentry *, struct nameidata *); 
        /*
        13. 該函數由VFS調用,修改文件的大小,在調用之前,索引節點的i_size項必須被設置成預期的大小
        */
        void (*truncate) (struct inode *);
        
        /*
        該函數用來檢查inode所代表的文件是否允許特定的訪問模式,如果允許特定的訪問模式,返回0,否則返回負值的錯誤碼。多數文件系統都將此區域設置爲null,使用VFS提供的通用方法進行檢查,這種檢查操作僅僅比較索引
及誒但對象中的訪問模式位是否和mask一致,比較複雜的系統, 比如支持訪問控制鏈(ACL)的文件系統,需要使用特殊的permission()方法
        */
        int (*permission) (struct inode *, int);
        
        /*
        該函數被notify_change調用,在修改索引節點之後,通知發生了改變事件
        */
        int (*setattr) (struct dentry *, struct iattr *);
        
        /*
        在通知索引節點需要從磁盤中更新時,VFS會調用該函數
        */
        int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *);
        
        /*
        該函數由VFS調用,向dentry指定的文件設置擴展屬性,屬性名爲name,值爲value
        */
        int (*setxattr) (struct dentry *, const char *, const void *, size_t, int);
        
        /*
        該函數被VFS調用,向value中拷貝給定文件的擴展屬性name對應的數值
        */
        ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
        
        /*
        該函數將特定文件所有屬性別表拷貝到一個緩衝列表中
        */
        ssize_t (*listxattr) (struct dentry *, char *, size_t);
        
        /*
        該函數從給定文件中刪除指定的屬性
        */
        int (*removexattr) (struct dentry *, const char *);        
    };
    */
    const struct inode_operations    *i_op;

    /*
    默認的索引節點操作
    former ->i_op->default_file_ops
    */
    const struct file_operations    *i_fop;     

    /*
    相關的超級塊
    */
    struct super_block    *i_sb;

    /*
    文件鎖鏈表
    */
    struct file_lock    *i_flock;

    /*
    相關的地址映射
    */
    struct address_space    *i_mapping;

    /*
    設備地址映射
  address_space結構與文件的對應:一個具體的文件在打開後,內核會在內存中爲之建立一個struct inode結構,其中的i_mapping域指向一個address_space結構。這樣,一個文件就對應一個address_space結構,一個 address_space與一個偏移量能夠確定一個page cache 或swap cache中的一個頁面。因此,當要尋址某個數據時,很容易根據給定的文件及數據在文件內的偏移量而找到相應的頁面
    */
    struct address_space    i_data;
#ifdef CONFIG_QUOTA
    /*
    節點的磁盤限額
    */
    struct dquot        *i_dquot[MAXQUOTAS];
#endif
    /*
    塊設備鏈表
    */
    struct list_head    i_devices;
    union 
    {
        //管道信息
        struct pipe_inode_info    *i_pipe;
        //塊設備驅動
        struct block_device    *i_bdev;
        struct cdev        *i_cdev;
    };
    
    /*
    索引節點版本號
    */
    __u32            i_generation;

#ifdef CONFIG_FSNOTIFY
    /*
    目錄通知掩碼
    all events this inode cares about
    */
    __u32            i_fsnotify_mask;  
    struct hlist_head    i_fsnotify_mark_entries; /* fsnotify mark entries */
#endif

#ifdef CONFIG_INOTIFY
    struct list_head    inotify_watches; /* watches on this inode */
    struct mutex        inotify_mutex;    /* protects the watches list */
#endif
    /*
    狀態標誌
    */
    unsigned long        i_state;

    /*
    首次修改時間
    jiffies of first dirtying
    */
    unsigned long        dirtied_when;     
    
    /*
    文件系統標誌
    */
    unsigned int        i_flags;

    /*
    寫者記數
    */
    atomic_t        i_writecount;
#ifdef CONFIG_SECURITY
    /*
    安全模塊
    */
    void            *i_security;
#endif
#ifdef CONFIG_FS_POSIX_ACL
    struct posix_acl    *i_acl;
    struct posix_acl    *i_default_acl;
#endif
    void            *i_private; /* fs or device private pointer */
};

2.3 dentry 目錄

目錄項是描述文件的邏輯屬性,只存在於內存中,並沒有實際對應的磁盤上的描述,更確切的說是存在於內存的目錄項緩存,爲了提高查找性能而設計。注意不管是文件夾還是最終的文件,都是屬於目錄項,所有的目錄項在一起構成一顆龐大的目錄樹。例如:open一個文件/home/xxx/yyy.txt,那麼/、home、xxx、yyy.txt都是一個目錄項,VFS在查找的時候,根據一層一層的目錄項找到對應的每個目錄項的inode,那麼沿着目錄項進行操作就可以找到最終的文件。
目錄也是一種文件(所以也存在對應的inode)

struct dentry 
{
    //目錄項引用計數器 
    atomic_t d_count;

    /*
    目錄項標誌 protected by d_lock 
    #define DCACHE_AUTOFS_PENDING 0x0001    // autofs: "under construction"  
    #define DCACHE_NFSFS_RENAMED  0x0002    // this dentry has been "silly renamed" and has to be eleted on the last dput() 
    #define    DCACHE_DISCONNECTED 0x0004        //指定了一個dentry當前沒有連接到超級塊的dentry樹
    #define DCACHE_REFERENCED    0x0008      //Recently used, don't discard.  
    #define DCACHE_UNHASHED        0x0010        //該dentry實例沒有包含在任何inode的散列表中
    #define DCACHE_INOTIFY_PARENT_WATCHED    0x0020 // Parent inode is watched by inotify 
    #define DCACHE_COOKIE        0x0040        // For use by dcookie subsystem 
    #define DCACHE_FSNOTIFY_PARENT_WATCHED    0x0080 // Parent inode is watched by some fsnotify listener 
    */
    unsigned int d_flags;    

    //per dentry lock    
    spinlock_t d_lock;        

    //當前dentry對象表示一個裝載點,那麼d_mounted設置爲1,否則爲0
    int d_mounted;

    /*
    文件名所屬的inode,如果爲NULL,則表示不存在的文件名
    如果dentry對象是一個不存在的文件名建立的,則d_inode爲NULL指針,這有助於加速查找不存在的文件名,通常情況下,這與查找實際存在的文件名同樣耗時
    */
    struct inode *d_inode;         
    /*
    The next three fields are touched by __d_lookup.  Place them here so they all fit in a cache line.
    */
    //用於查找的散列表 lookup hash list 
    struct hlist_node d_hash;    

    /*
    指向當前的dentry實例的父母了的dentry實例 parent directory
    當前的dentry實例即位於父目錄的d_subdirs鏈表中,對於根目錄(沒有父目錄),d_parent指向其自身的dentry實例
    */  
    struct dentry *d_parent;

    /*
    d_iname指定了文件的名稱,qstr是一個內核字符串的包裝器,它存儲了實際的char*字符串以及字符串長度和散列值,這使得更容易處理查找工作
    要注意的是,這裏並不存儲絕對路徑,而是隻有路徑的最後一個分量,例如對/usr/bin/emacs只存儲emacs,因爲在linux中,路徑信息隱含在了dentry層次鏈表結構中了
    */    
    struct qstr d_name;

    //LRU list
    struct list_head d_lru;        
    /*
     * d_child and d_rcu can share memory
     */
    union 
    {
        /* child of parent list */
        struct list_head d_child;
        //鏈表元素,用於將dentry連接到inode的i_dentry鏈表中    
         struct rcu_head d_rcu;
    } d_u;

    //our children 子目錄/文件的目錄項鍊表
    struct list_head d_subdirs;    

    /*
    inode alias list 鏈表元素,用於將dentry連接到inode的i_dentry鏈表中 
    d_alias用作鏈表元素,以連接表示相同文件的各個dentry對象,在利用硬鏈接用兩個不同名稱表示同一文件時,會發生這種情況,對應於文件的inode的i_dentry成員用作該鏈表的表頭,各個dentry對象通過d_alias連接到該鏈表中
    */
    struct list_head d_alias;    

    //used by d_revalidate 
    unsigned long d_time;

    /*
    d_op指向一個結構,其中包含了各種函數指針,提供對dentry對象的各種操作,這些操作必須由底層文件系統實現
    struct dentry_operations 
    {
        //在把目錄項對象轉換爲一個文件路徑名之前,判定該目錄項對象是否依然有效
        int (*d_revalidate)(struct dentry *, struct nameidata *);

        //生成一個散列值,用於目錄項散列表
        int (*d_hash) (struct dentry *, struct qstr *);
        
        //比較兩個文件名
        int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);

        //當對目錄項對象的最後一個引用被刪除,調用該方法
        int (*d_delete)(struct dentry *);

        //當要釋放一個目錄項對象時,調用該方法
        void (*d_release)(struct dentry *);

        //當一個目錄對象變爲負狀態時,調用該方法
        void (*d_iput)(struct dentry *, struct inode *);
        char *(*d_dname)(struct dentry *, char *, int);
    };
    */        
    const struct dentry_operations *d_op;

    //The root of the dentry tree dentry樹的根,超級塊
    struct super_block *d_sb;    

    //fs-specific data 特定文件系統的數據
    void *d_fsdata;            

    /*
    短文件名small names存儲在這裏
    如果文件名由少量字符組成,則只保存在d_iname中,而不是dnanme中,用於加速訪問
    */ 
    unsigned char d_iname[DNAME_INLINE_LEN_MIN];    
};

2.4 file結構

件結構體代表一個打開的文件,系統中的每個打開的文件在內核空間都有一個關聯的struct file。它由內核在打開文件時創建,並傳遞給在文件上進行操作的任何函數。在文件的所有實例都關閉後,內核釋放這個數據結構

struct file 
{
    /*
     * fu_list becomes invalid after file_free is called and queued via
     * fu_rcuhead for RCU freeing
     */
    union 
    {
        /*
        定義在 linux/include/linux/list.h中 
        struct list_head 
        {
            struct list_head *next, *prev;
        };
        用於通用文件對象鏈表的指針,所有打開的文件形成一個鏈表
        */
        struct list_head    fu_list;
        /*
        定義在linux/include/linux/rcupdate.h中  
        struct rcu_head 
        {
            struct rcu_head *next;
            void (*func)(struct rcu_head *head);
        };
        RCU(Read-Copy Update)是Linux 2.6內核中新的鎖機制
        */
        struct rcu_head     fu_rcuhead;
    } f_u;
    
    /*
    定義在linux/include/linux/namei.h中
    struct path 
    {
        /*
        struct vfsmount *mnt的作用是指出該文件的已安裝的文件系統,即指向VFS安裝點的指針
        */
        struct vfsmount *mnt;
        /*
        struct dentry *dentry是與文件相關的目錄項對象,指向相關目錄項的指針
        */
        struct dentry *dentry;
    };
    */
    struct path        f_path;
#define f_dentry    f_path.dentry
#define f_vfsmnt    f_path.mnt

    /*
   指向文件操作表的指針
    定義在linux/include/linux/fs.h中,其中包含着與文件關聯的操作,例如
    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 (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    int (*readdir) (struct file *, void *, filldir_t);
    unsigned int (*poll) (struct file *, struct poll_table_struct *);
    int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
    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 *, struct dentry *, int datasync);
    int (*aio_fsync) (struct kiocb *, 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 **);
     };
    當打開一個文件時,內核就創建一個與該文件相關聯的struct file結構,其中的*f_op就指向的是具體對該文件進行操作的函數
    例如用戶調用系統調用read來讀取該文件的內容時,那麼系統調用read最終會陷入內核調用sys_read函數,而sys_read最終會調用於該文件關聯的struct file結構中的f_op->read函數對文件內容進行讀取
    */
    const struct file_operations    *f_op;
    spinlock_t        f_lock;  /* f_ep_links, f_flags, no IRQ */

    /*
    typedef struct { volatile int counter; } atomic_t;
    volatile修飾字段告訴gcc不要對該類型的數據做優化處理,對它的訪問都是對內存的訪問,而不是對寄存器的訪問
    f_count的作用是記錄對文件對象的引用計數,也即當前有多少個進程在使用該文件
    */
    atomic_long_t        f_count;

    /*
    當打開文件時指定的標誌,對應系統調用open的int flags參數。驅動程序爲了支持非阻塞型操作需要檢查這個標誌
    */
    unsigned int         f_flags;

    /*
    對文件的讀寫模式,對應系統調用open的mod_t mode參數。如果驅動程序需要這個值,可以直接讀取這個字段。
    mod_t被定義爲:
    typedef unsigned int __kernel_mode_t;
    typedef __kernel_mode_t         mode_t;
    */
    fmode_t            f_mode;

    /*
    當前的文件指針位置,即文件的讀寫位置
    loff_t被定義爲:
    typedef long long       __kernel_loff_t;
    typedef __kernel_loff_t         loff_t;
    */
    loff_t            f_pos;

    /*
    struct fown_struct在linux/include/linux/fs.h被定義 
    struct fown_struct 
    {
        rwlock_t lock;          /* protects pid, uid, euid fields */
        struct pid *pid;        /* pid or -pgrp where SIGIO should be sent */
        enum pid_type pid_type; /* Kind of process group SIGIO should be sent to */
        uid_t uid, euid;        /* uid/euid of process setting the owner */
        int signum;             /* posix.1b rt signal to be delivered on IO */
    };
    該結構的作用是通過信號進行I/O時間通知的數據
    */
    struct fown_struct    f_owner;
    const struct cred    *f_cred;

    /*
    struct file_ra_state結構被定義在/linux/include/linux/fs.h中 
    struct file_ra_state 
    {
        pgoff_t start;                  /* where readahead started */
        unsigned long size;             /* # of readahead pages */
        unsigned long async_size;       /* do asynchronous readahead when
                           there are only # of pages ahead */
                           
        unsigned long ra_pages;         /* Maximum readahead window */
        unsigned long mmap_hit;         /* Cache hit stat for mmap accesses */
        unsigned long mmap_miss;        /* Cache miss stat for mmap accesses */
        unsigned long prev_index;       /* Cache last read() position */
        unsigned int prev_offset;       /* Offset where last read() ended in a page */
    };
    該結構標識了文件預讀狀態,文件預讀算法使用的主要數據結構,當打開一個文件時,f_ra中出了perv_page(默認爲-1)ra_apges(對該文件允許的最大預讀量)這兩個字段外,其他的所有西端都置爲0
    */
    struct file_ra_state    f_ra;
    
    /*
    記錄文件的版本號,每次使用後都自動遞增
    */
    u64            f_version;
#ifdef CONFIG_SECURITY
    /*
    #ifdef CONFIG_SECURITY
        void                    *f_security;
    #endif
    如果在編譯內核時配置了安全措施,那麼struct file結構中就會有void *f_security數據項,用來描述安全措施或者是記錄與安全有關的信息。
    */
    void            *f_security;
#endif
    /*
    系統在調用驅動程序的open方法前將這個指針置爲NULL。驅動程序可以將這個字段用於任意目的,也可以忽略這個字段。驅動程序可以用這個字段指向已分配的數據,但是一定要在內核釋放file結構前的release方法中清除它
    */
    void            *private_data;

#ifdef CONFIG_EPOLL
    /*
    被用在fs/eventpoll.c來鏈接所有鉤到這個文件上。其中
        1) f_ep_links是文件的事件輪詢等待者鏈表的頭
        2) f_ep_lock是保護f_ep_links鏈表的自旋鎖
    */
    struct list_head    f_ep_links;
    struct list_head    f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */

    /*
    struct address_space被定義在/linux/include/linux/fs.h中,此處是指向文件地址空間的指針 
    */
    struct address_space    *f_mapping;
#ifdef CONFIG_DEBUG_WRITECOUNT
    unsigned long f_mnt_write_state;
#endif
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章