Linux VFS 虛擬文件系統數據結構

 

虛擬文件系統‍node

虛擬文件系統
         虛擬文件系統(VFS)是linux內核和具體I/O設備之間的封裝的一層共通訪問接口,經過這層接口,linux內核能夠以同一的方式訪問各類I/O設備。linux

 

虛擬文件系統(VFS)是linux內核和存儲設備之間的抽象層,主要有如下好處:緩存

--簡化了應用程序的開發:應用經過統一的系統調用訪問各類存儲介質數據結構

--簡化了新文件系統加入內核的過程:新文件系統只要實現VFS的各個接口便可,不須要修改內核部分app

虛擬文件中的4個對象socket

 

       (1)  超級塊 (2)索引節點 (3)目錄項 (4)文件對象函數

超級塊

它通常存儲在磁盤的特定扇區中,可是對於那些基於內存的文件系統(好比proc,sysfs)ui

超級塊是在使用時建立在內存中的atom

超級塊的定義在<linux/fs.h>spa

 

/* 
 * 超級塊結構中定義的字段很是多,
 * 這裏只介紹一些重要的屬性
 */
struct super_block {
    struct list_head    s_list;               /* 指向全部超級塊的鏈表 */
    const struct super_operations    *s_op; /* 超級塊方法 */
    struct dentry        *s_root;           /* 目錄掛載點 */
    struct mutex        s_lock;            /* 超級塊信號量 */
    int            s_count;                   /* 超級塊引用計數 */
    struct list_head    s_inodes;           /* inode鏈表 */
    struct mtd_info        *s_mtd;            /* 存儲磁盤信息 */
    fmode_t            s_mode;                /* 安裝權限 */
};
 /*
 * 其中的 s_op 中定義了超級塊的操做方法
 * 這裏只介紹一些相對重要的函數
 */
struct super_operations {
    struct inode *(*alloc_inode)(struct super_block *sb); /* 建立和初始化一個索引節點對象 */
    void (*destroy_inode)(struct inode *);                /* 釋放給定的索引節點 */
    void (*dirty_inode) (struct inode *);                 /* VFS在索引節點被修改時會調用這個函數 */
    int (*write_inode) (struct inode *, int);             /* 將索引節點寫入磁盤,wait表示寫操做是否須要同步 */
    void (*drop_inode) (struct inode *);            /* 最後一個指向索引節點的引用被刪除後,VFS會調用這個函數 */
    void (*delete_inode) (struct inode *);                /* 從磁盤上刪除指定的索引節點 */
    void (*put_super) (struct super_block *);             /* 卸載文件系統時由VFS調用,用來釋放超級塊 */
    void (*write_super) (struct super_block *);           /* 用給定的超級塊更新磁盤上的超級塊 */
    int (*sync_fs)(struct super_block *sb, int wait);     /* 使文件系統中的數據與磁盤上的數據同步 */
    int (*statfs) (struct dentry *, struct kstatfs *);    /* VFS調用該函數獲取文件系統狀態 */
    int (*remount_fs) (struct super_block *, int *, char *); /* 指定新的安裝選項從新安裝文件系統時,VFS會調用該函數 */
    void (*clear_inode) (struct inode *);       /* VFS調用該函數釋放索引節點,並清空包含相關數據的全部頁面 */
    void (*umount_begin) (struct super_block *); /* VFS調用該函數中斷安裝操做 */
};

 索引節點

索引節點是VFS中的核心概念,它包含內核在操做文件或目錄時須要的所有信息。

一個索引節點表明文件系統中的一個文件(這裏的文件不只是指咱們平時所認爲的普通的文件,還包括目錄,特殊設備文件等等)。

索引節點和超級塊同樣是實際存儲在磁盤上的,當被應用程序訪問到時纔會在內存中建立。

索引節點定義在:<linux/fs.h>
/* 
 * 索引節點結構中定義的字段很是多,
 * 這裏只介紹一些重要的屬性
 */
struct inode {
    struct hlist_node    i_hash;     /* 散列表,用於快速查找inode */
    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;    /* 硬連接數 */
    uid_t            i_uid;          /* 使用者id */
    gid_t            i_gid;          /* 使用組id */
    struct timespec        i_atime;    /* 最後訪問時間 */
    struct timespec        i_mtime;    /* 最後修改時間 */
    struct timespec        i_ctime;    /* 最後改變時間 */
    const struct inode_operations    *i_op;  /* 索引節點操做函數 */
    const struct file_operations    *i_fop;    /* 缺省的索引節點操做 */
    struct super_block    *i_sb;              /* 相關的超級塊 */
    struct address_space    *i_mapping;     /* 相關的地址映射 */
    struct address_space    i_data;         /* 設備地址映射 */
    unsigned int        i_flags;            /* 文件系統標誌 */
    void            *i_private;             /* fs 私有指針 */
};
 
/*
 * 其中的 i_op 中定義了索引節點的操做方法
 * 這裏只介紹一些相對重要的函數
 */
struct inode_operations {
    /* 爲dentry對象創造一個新的索引節點 */
    int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
    /* 在特定文件夾中尋找索引節點,該索引節點要對應於dentry中給出的文件名 */
    struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
    /* 建立硬連接 */
    int (*link) (struct dentry *,struct inode *,struct dentry *);
    /* 從一個符號連接查找它指向的索引節點 */
    void * (*follow_link) (struct dentry *, struct nameidata *);
    /* 在 follow_link調用以後,該函數由VFS調用進行清除工做 */
    void (*put_link) (struct dentry *, struct nameidata *, void *);
    /* 該函數由VFS調用,用於修改文件的大小 */
    void (*truncate) (struct inode *);
};

經過 inode (inode->i_ino)號和相關超級塊 inode->i_sb可獲取邏輯塊組號:方式以下

block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);

 

目錄項

  和超級塊和索引節點不一樣,目錄項並非實際存在於磁盤上的。

在使用的時候在內存中建立目錄項對象,其實經過索引節點已經能夠定位到指定的文件,

可是索引節點對象的屬性很是多,在查找,比較文件時,直接用索引節點效率不高,因此引入了目錄項的概念。

每一個目錄項對象都有三種狀態:被使用,未使用和負狀態

- 被使用:對應一個有效的索引節點,而且該對象由一個或多個使用者

- 未使用:對應一個有效的索引節點,可是VFS當前並無使用這個目錄項

- 負狀態:沒有對應的有效索引節點(可能索引節點被刪除或者路徑不存在了)

目錄項的目的就是提升文件查找,比較的效率,因此訪問過的目錄項都會緩存在slab中。

目錄項定義在<linux/dcache.h>

/* 目錄項對象結構 */
struct dentry {
    atomic_t d_count;       /* 使用計數 */
    unsigned int d_flags;   /* 目錄項標識 */
    spinlock_t d_lock;        /* 單目錄項鎖 */
    int d_mounted;          /* 是否登陸點的目錄項 */
    struct inode *d_inode;    /* 相關聯的索引節點,經過這個索引節點就能夠讀取到文件數據 */
    struct hlist_node d_hash;    /* 散列表 */
    struct dentry *d_parent;    /* 父目錄的目錄項對象 */
    struct qstr d_name;         /* 目錄項名稱 */
    struct list_head d_lru;        /* 未使用的鏈表 */
    /*
     * d_child and d_rcu can share memory
     */
    union {
        struct list_head d_child;    /* child of parent list */
         struct rcu_head d_rcu;
} d_u;
    struct list_head d_subdirs;    /* 子目錄鏈表 */
    struct list_head d_alias;    /* 索引節點別名鏈表 */
    unsigned long d_time;        /* 重置時間 */
    const struct dentry_operations *d_op; /* 目錄項操做相關函數 */
    struct super_block *d_sb;    /* 文件的超級塊 */
    void *d_fsdata;            /* 文件系統特有數據 */
    unsigned char d_iname[DNAME_INLINE_LEN_MIN];    /* 短文件名 */
};
 
/* 目錄項相關操做函數 */
struct dentry_operations {
    /* 該函數判斷目錄項對象是否有效。VFS準備從dcache中使用一個目錄項時會調用這個函數 */
    int (*d_revalidate)(struct dentry *, struct nameidata *);
    /* 爲目錄項對象生成hash值 */
    int (*d_hash) (struct dentry *, struct qstr *);
    /* 比較 qstr 類型的2個文件名 */
    int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
    /* 當目錄項對象的 d_count 爲0時,VFS調用這個函數 */
    int (*d_delete)(struct dentry *);
    /* 當目錄項對象將要被釋放時,VFS調用該函數 */
    void (*d_release)(struct dentry *);
    /* 當目錄項對象丟失其索引節點時(也就是磁盤索引節點被刪除了),VFS會調用該函數 */
    void (*d_iput)(struct dentry *, struct inode *);
    char *(*d_dname)(struct dentry *, char *, int);
};

 

文件對象

      文件對象表示進程已打開的文件,從用戶角度來看,咱們在代碼中操做的就是一個文件對象。

文件對象反過來指向一個目錄項對象(目錄項反過來指向一個索引節點)

其實只有目錄項對象才表示一個已打開的實際文件,雖然一個文件對應的文件對象不是惟一的,但其對應的索引節點和目錄項對象倒是惟一的。

文件對象的定義在: <linux/fs.h>

//路徑及結構體,包含目錄項
struct path {
	struct vfsmount *mnt; //文件系統掛載使用
	struct dentry *dentry;
};

/* 
 * 文件對象結構中定義的字段很是多,
 * 這裏只介紹一些重要的屬性
 */
struct file {
    union {
        struct list_head    fu_list;    /* 文件對象鏈表 */
        struct rcu_head     fu_rcuhead; /* 釋放以後的RCU鏈表 */
    } f_u;
    struct path        f_path;            /* 包含的目錄項 */
    const struct file_operations    *f_op; /* 文件操做函數 */
    atomic_long_t        f_count;        /* 文件對象引用計數 */
};
/*
 * 其中的 f_op 中定義了文件對象的操做方法
 * 這裏只介紹一些相對重要的函數
 */
struct file_operations {
    /* 用於更新偏移量指針,由系統調用lleek()調用它 */
    loff_t (*llseek) (struct file *, loff_t, int);
    /* 由系統調用read()調用它 */
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    /* 由系統調用write()調用它 */
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    /* 由系統調用 aio_read() 調用它 */
    ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    /* 由系統調用 aio_write() 調用它 */
    ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    /* 將給定文件映射到指定的地址空間上,由系統調用 mmap 調用它 */
    int (*mmap) (struct file *, struct vm_area_struct *);
    /* 建立一個新的文件對象,並將它和相應的索引節點對象關聯起來 */
    int (*open) (struct inode *, struct file *);
    /* 當已打開文件的引用計數減小時,VFS調用該函數 */
    int (*flush) (struct file *, fl_owner_t id);
};

 5 四個對象之間關係圖

 

VFS 通用文件模型中包含如下四種元數據結構:

  1. 超級塊對象(superblock object),用於存放已經註冊的文件系統的信息。好比ext2,ext3等這些基礎的磁盤文件系統,還有用於讀寫socket的socket文件系統,以及當前的用於讀寫cgroups配置信息的 cgroups 文件系統等。

  2. 索引節點對象(inode object),用於存放具體文件的信息。對於通常的磁盤文件系統而言,inode 節點中通常會存放文件在硬盤中的存儲塊等信息;對於socket文件系統,inode會存放socket的相關屬性,而對於cgroups這樣的特殊文件系統,inode會存放與 cgroup 節點相關的屬性信息。這裏面比較重要的一個部分是一個叫作 inode_operations 的結構體,這個結構體定義了在具體文件系統中建立文件,刪除文件等的具體實現。

  3. 文件對象(file object),一個文件對象表示進程內打開的一個文件,文件對象是存放在進程的文件描述符表裏面的。一樣這個文件中比較重要的部分是一個叫 file_operations 的結構體,這個結構體描述了具體的文件系統的讀寫實現。當進程在某一個文件描述符上調用讀寫操做時,實際調用的是 file_operations 中定義的方法。 對於普通的磁盤文件系統,file_operations 中定義的就是普通的塊設備讀寫操做;對於socket文件系統,file_operations 中定義的就是 socket 對應的 send/recv 等操做;而對於cgroups這樣的特殊文件系統,file_operations 中定義的就是操做 cgroup 結構體等具體的實現。

  4. 目錄項對象(dentry object),在每一個文件系統中,內核在查找某一個路徑中的文件時,會爲內核路徑上的每個份量都生成一個目錄項對象,經過目錄項對象可以找到對應的 inode 對象,目錄項對象通常會被緩存,從而提升內核查找速度。

相關文章
相關標籤/搜索