linux文件系统:VFS

vfs

Linux内核通过虚拟文件系统(Virtual File System,VFS)管理文件系统

VFS为所有的文件系统提供了统一的接口,对每个具体文件系统的访问要通过VFS定义的接口来实现

VFS本身只存在于内存中,它需要将硬盘上的文件系统抽象到内存中,这个工作是通过几个重要的结构实现的:

  • dentry
  • inode
  • super_block
  • file

1 super_block

超级块(super_block)代表了整个文件系统本身。通常,超级块是对应文件系统自身的控制块结构,其内容需要读取文件系统在硬盘上的超级块结构获得,所以超级块是具体文件系统超级块的内存抽象。

超级块简略结构如下:

struct super_block {
        unsigned long        s_blocksize;            /* 文件系统的块大小 */
        unsigned char        s_blocksize_bits;
        ……/*省略超级块的链表、设备号等代码*/
        unsigned long long        s_maxbytes;        /* Max file size */

        struct file_system_type        *s_type;
        struct super_operations        *s_op;    /* 提供了一些重要的超级块操作函数 */

        unsigned long        s_magic;            /* 每个文件系统都有一个magic */
        struct dentry        *s_root;            /* 指向文件系统根dentry的指针 */

        struct list_head        s_inodes;        /* 包含文件系统内所有的 inodes */
        struct list_head        s_dirty;         /* dirty inodes */

        struct block_device        *s_bdev;      /* 指向文件系统存在的块设备指针 */
        void         *s_fs_info;                 /* Filesystem private info */
};

参考include/linux/fs.h

每个文件系统都有一个超级块结构,每个超级块都要链接到一个超级块链表。

ext4_super_block
super_block
ufs_super_block
squashfs_super_block

文件系统内的每个文件在打开时都需要在内存分配一个inode结构,这些inode结构都要链接到超级块。

顺着super_blocks链表可以遍历整个操作系统打开过的文件的inode结构。

2 dentry

对于一个通常的文件系统来说,文件和目录一般按树状结构保存,目录项(dentry)就是反映了文件系统的这种树状关系。

2.1 dentry树

在VFS里,目录本身也是一个文件,只是有点特殊。每个文件都有一个dentry,这个dentry链接到上级目录的dentry,这样层层链接形成了目录树。

dentry-subdir_child

dentry简略结构如下:

struct dentry {
 ……/省略dentry锁、标志等代码/
    struct inode *d_inode;        /* 指向一个inode结构。这个inode和dentry共同描述了一个普通文件或者目录文件 */
    /*
     * The next three fields are touched by __d_lookup.  Place them here
     * so they all fit in a cache line.
     */
    struct hlist_node d_hash;           /* 链接到dentry cache的hash链表 */
    struct dentry *d_parent;            /* parent directory */
    struct qstr d_name;                 /* 文件或目录的名字 */

    /*
     * 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;        /* 子项的链表头,所有子项d_child都要链接到这个链表 */

    struct dentry_operations *d_op;
    struct super_block *d_sb;          /* The root of the dentry tree */
    int d_mounted;                     /* 指示dentry是否是一个挂载点,如果是则不为0 */
};

参考include/linux/fs.h

2.2 dentry的cache

所有的dentry都指向一个dentry_hashtabledentry_hashtable是个数组,它的数组成员是hash链表数据结构。

dentry__hashtable

这里所说的dentry,指的是在内存中的dentry。
如果某个文件已经被打开过,内存中就应该有该文件的dentry结构,并且该dentry被链接到dentry_hashtable数组的某个hash链表头。
后续再访问该文件的时候,就可以直接从hash链表里面找到,避免了再次读硬盘。这是dentry的cache概念。

2.3 挂载

通过dentry的d_mounted成员不为0可以判断: 该目录不是一个普通的目录,而是一个文件系统的挂载点。

在一开始,根文件系统和要挂载的源文件系统分别有两个dentry树。
当文件系统被挂载的时候,它的vfsmount结构就被链接到内核的一个全局链表—mount_hashtable数组链表。

每个文件系统都有这样一个vfsmount结构

当发现mnt目录是个特殊的目录时,从mount_hashtable数组找到hash链表头,再遍历整个hash链表,就能找到txt文件所在文件系统的vfsmount,然后mnt目录的dentry就被替换,置换为新文件系统的根目录。

3 inode

inode代表一个文件。inode保存了文件的大小、创建时间、文件的块大小等参数,以及对文件的读写函数、文件的读写缓存等信息。

具体文件系统的inode(静态节点)存储在硬盘中,在使用时需要调入内存,填写vfs的inode(动态节点)

一个真实的文件可以有多个dentry,因为指向文件的路径可以有多个(考虑文件的链接),而inode只有一个。

dentry-inode

inode简略结构如下:

struct inode {
        struct list_head        i_list;             /* 用于描述inode当前状态的链表*/
        struct list_head        i_sb_list;          /* 用于链接到超级块中的inode链表 */
        struct list_head        i_dentry;           /* 该文件的所有dentry需要链接到i_dentry */
        unsigned long           i_ino;              /* inode号 */
        atomic_t                i_count;            /* inode引用计数 */
        loff_t                  i_size;             /* 字节为单位的文件长度 */
        
        unsigned int            i_blkbits;          /* 文件块的位数 */
        struct inode_operations         *i_op;
        const struct file_operations    *i_fop;         /* former ->i_op->default_file_ops */
        struct address_space            *i_mapping;     /* 用于缓存文件的内容,便于快速读写 */
        struct block_device             *i_bdev;        /* 指向文件系统所绑定的块设备 */
  ……/*省略锁等代码*/
};

参考include/linux/fs.h

当创建一个新的inode的时候,成员i_list要链接到inode_in_use这个链表,表示inode处于使用状态,同时成员i_sb_list也要链接到文件系统超级块的s_inodes链表头。

内核提供了一个hash链表数组inode_hashtable,所有的inode结构都要链接到数组里面的某个hash链表

inode结构的i_mode成员用不同的值代表不同的文件类型:

imode类型
S_IFBLK块设备
S_IFCHR字符设备
S_IFDIR目录
S_IFSOCKsocket
S_IFIFOFIFO

4 文件file

文件对象的作用是描述进程和文件交互的关系。

这里需要指出的是,硬盘上并不存在一个文件结构。进程打开一个硬盘上的文件时,内核就动态创建一个文件对象,返回一个文件描述符,并保存文件指针到文件描述符表里面的数组。

同一个文件,在不同的进程中有不同的文件对象.

file简略结构如下:

struct file {
    struct dentry           *f_dentry;          /* 指向文件对应的dentry结构 */
    struct vfsmount         *f_vfsmnt;          /* 指向文件所属于的文件系统的vfsmount对象 */
    const struct file_operations    *f_op;   
    atomic_long_t		f_count;                     /* 使用此结构体的进程数量 */
    ……/*省略部分代码*/
    loff_t                  f_pos;              /* 表示进程对文件操作的位置。如读取前10字节,f_pos就指示第11字节位置 */
    struct fown_struct          f_owner;
    unsigned int                f_uid, f_gid;
    struct file_ra_state        f_ra;           /* 用于文件预读的设置 */

    struct address_space        *f_mapping;     /* 指向一个address_space结构。这个结构封装了文件的读写缓存页面 */
};

参考include/linux/fs.h

files_struct:用于记录进程中文件描述符的使用情况,是进程的私有数据

/*
 * Open file table structure
 */
struct files_struct {
  /*
   * read mostly part
   */
	atomic_t count;                                     /* 共享该表的进程数量 */
	bool resize_in_progress;                            /* 指示是否正在进行扩容操作 */
	wait_queue_head_t resize_wait;                      /* 等待队列头,扩容时阻塞等待的进程 */

	struct fdtable __rcu *fdt;                          /* 指向一个文件描述符表的指针 */
	struct fdtable fdtab;                               /* 文件描述符表 */
  /*
   * written part on a separate cache line in SMP
   */
	spinlock_t file_lock ____cacheline_aligned_in_smp;  /* 保护如下所有的域 */
	unsigned int next_fd;
	unsigned long close_on_exec_init[1];                /* exec()关闭文件描述符的初始值 */
	unsigned long open_fds_init[1];
	unsigned long full_fds_bits_init[1];
	struct file __rcu * fd_array[NR_OPEN_DEFAULT];      /* 文件描述符数组,存储每个打开的文件对应的file结构体指针 */
};

参考include/linux/fdtable.h

fs_struct表示文件系统信息的结构体:

struct fs_struct {
	int users;              /* 表示有多少用户正在使用该fs_struct */
	spinlock_t lock;
	seqcount_t seq;         /* 一个序列号计数器,用于维护fs_struct的序列化访问 */
	int umask;              /* 代表用户文件屏蔽掩码,用于设置新创建文件的默认权限 */
	int in_exec;            /* 指示当前进程是否正在执行 */
	struct path root, pwd;  /* 根目录 当前目录 */
} __randomize_layout;

参考include/linux/fd_struct.h

5 vfs各结构体的关系

vfs结构体之间的关系

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值