linux 文件系统之VFS

linux 文件系统之VFS

简介

基本概念

VFS

VFS(Vritual Filesystem) 是给用户空间程序提供统一的文件和文件系统访问接口的内核子系统。借助 VFS,即使文件系统的类型不同(比如 NTFS 和 ext3),也可以实现文件系统之间交互(移动、复制文件等)

  • 从用户空间程序的角度来看,VFS 提供了一个统一的抽象、接口。这使得用户空间程序可以对不同类型的文件系统发起统一的系统调用,而不需要关心底层的文件系统类型。
  • 从文件系统的角度来看,VFS 提供了一个基于 Unix-style 文件系统的通用文件模型(common file model),可以用来表示任何类型文件系统的通用特性和操作。底层文件系统提供 VFS 规定的接口和数据结构,从而实现对 linux 的支持。

image-20191031142923710

VFS 的通用数据模型主要包括 4 种对象类型:

  • Superblock: 存储了挂载的文件系统的元信息,对于基于磁盘的文件系统,通常在磁盘上有一个对应的 superblock.
  • Inode: 一个文件对应一个 inode,每个 inode 都有一个 inode number 唯一标识。inode 通常
  • Dentry: 用于将 inode(表示文件)和目录项(表示文件路径)关联起来。inode 中是没有文件路径的,所以需要 dentry 来讲文件路径和文件关联起来。不同的磁盘文件系统使用各自的方式来存储 dentry。路径上的每一个单独的组件,都是一个 dentry。VFS 中没有目录对象,目录只是一种文件。
  • File: 用于存储进程和打开的文件之间交互的信息,这个信息只会存在于内核空间中,在磁盘上没有对应的信息。

image-20191031143245259

虚拟文件系统主要模块相互作用

Superblock

inode

根目录的 inode 号为 0,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct inode_operations
{
    int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
    struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
    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 *,int);
    int (*rmdir) (struct inode *,struct dentry *);
    int (*mknod) (struct inode *,struct dentry *,int,dev_t);
    int (*rename) (struct inode *, struct dentry *,
                   struct inode *, struct dentry *);
    int (*readlink) (struct dentry *, char __user *,int);
    void * (*follow_link) (struct dentry *, struct nameidata *);
    void (*put_link) (struct dentry *, struct nameidata *, void *);
    void (*truncate) (struct inode *);
    int (*permission) (struct inode *, int);
    int (*setattr) (struct dentry *, struct iattr *);
    int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
    int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
    ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
    ssize_t (*listxattr) (struct dentry *, char *, size_t);
    int (*removexattr) (struct dentry *, const char *);
    void (*truncate_range)(struct inode *, loff_t, loff_t);
    long (*fallocate)(struct inode *inode, int mode, loff_t offset,
                      loff_t len);
    int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
    u64 len);
};

dentry

dentry 是 directory entry 的简称,dentry 是路径上具体的一个组件,一个路径上的每一个组件都是一个 dentry,如路径/bin/vi.txt 中,共有 3 个 dentry,分别是 /, bin, vi.txt。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct dentry
{
    atomic_t                 d_count;      /* usage count */
    unsigned int             d_flags;      /* dentry flags */
    spinlock_t               d_lock;       /* per-dentry lock */
    int                      d_mounted;    /* is this a mount point? */
    struct inode             *d_inode;     /* associated inode */
    struct hlist_node        d_hash;       /* list of hash table entries */
    struct dentry            *d_parent;    /* dentry object of parent */
    struct qstr              d_name;       /* dentry name */
    struct list_head         d_lru;        /* unused list */
    union
    {
        struct list_head     d_child;      /* list of dentries within */
        struct rcu_head      d_rcu;        /* RCU locking */
    } d_u;
    struct list_head         d_subdirs;    /* subdirectories */
    struct list_head         d_alias;  /* list of alias inodes */
    unsigned long            d_time;       /* revalidate time */
    struct dentry_operations *d_op;        /* dentry operations table */
    struct super_block       *d_sb;        /* superblock of file */
    void                     *d_fsdata;    /* filesystem-specific data */
    unsigned char            d_iname[DNAME_INLINE_LEN_MIN]; /* short name */
};

dentry 分为三种状态:

  • used: 该 dentry 对应一个有效的 inode(dentry 的 d_inode 域指向一个有效的 inode),并且 d_count 是正数,即有一个或者多个用户正在使用该 dentry
  • unused: 该 dentry 对应一个有效的 inode(dentry 的 d_inode 域指向一个有效的 inode),并且 d_count 为 0,即 VFS 并没有使用该 dentry,因为该 dentry 仍然指向一个有效的 inode 对象,dentry 当前被保存在 dentry cache 中(等待可能再次被使用)
  • negtive: 该 dentry 没有对应一个有效的 inode(dentry 的 d_inode 为 NULL),这种情况可能是因为对应的 inode 对象被销毁了或者是查找的路径名称不对。此时 dentry 仍然被保存在 cache 中,这样下次路径查找可以快速进行(直接从 dentry cache 中获得)

Dentry Cache

dentry cache 的机制由三个部分组成

  • used dentry 双向链表:每个 inode 对象都有一个 i_dentry 域,这是一个双向链表,用于保存该 inode 对应的 dentry 对象(一个 inode 可以有很多个 dentry 对象)
  • least recently used 双向链表:存储 unused 和 negative 状态的 dentry 对象。该链表按照 lru 的顺序存储,尾部的是最 not lru 的对象,当需要删除 dentry 来释放空间时,从链表的尾部删除对象。
  • 哈希表和哈希函数:哈希表存储路径和 dentry 的映射关系,哈希表使用 dentry_hanshtable 数组来存储,数组中每个元素都指向一个由哈希值相同的 dentry 组成的链表。哈希函数根据路径计算哈希值。具体的哈希计算方法由 detry 的操作函数 d_hash()来决定,文件系统可以自己实现这个函数。

dentry 存储在 cache 中时,dentry 的存在导致对应的 inode 的使用计数大于 0,这样 dentry 对象可以将 inode 钉在内存中,只要 dentry 被 cache 了,那么对应的 inode 就一定也被 cache 了(使用的是 inode cache,即 icache),所以当路径查找函数在 dentry cache 中命中时,其对应的 inode 一定也在内存中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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);
};

File

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
struct file
{
    union
    {
        struct list_head   fu_list;       /* list of file objects */
        struct rcu_head    fu_rcuhead;    /* RCU list after freeing */
    } f_u;
    struct path            f_path;        /* contains the dentry */
    struct file_operations *f_op;         /* file operations table */
    spinlock_t             f_lock;        /* per-file struct lock */
    atomic_t               f_count;       /* file object’s usage count */
    unsigned int           f_flags;       /* flags specified on open */
    mode_t                 f_mode;        /* file access mode */
    loff_t                 f_pos;         /* file offset (file pointer) */
    struct fown_struct     f_owner;       /* owner data for signals */
    const struct cred      *f_cred;       /* file credentials */
    struct file_ra_state   f_ra;  /* read-ahead state */
    u64                    f_version;     /* version number */
    void                   *f_security;   /* security module */
    void                   *private_data; /* tty driver hook */
    struct list_head       f_ep_links;    /* list of epoll links */
    spinlock_t             f_ep_lock;     /* epoll lock */
    struct address_space   *f_mapping;    /* page cache mapping */
    unsigned long          f_mnt_write_state; /* debugging state */
};

参考

  1. https://www.huliujia.com/blog/81d31574c9a0088e8ae0c304020b4b1c4f6b8fb9/
  2. https://www.cnblogs.com/huxiao-tee/p/4657851.html
updatedupdated2024-05-152024-05-15