linux内核-VFS的结构

结构概观

知识点1:
VFS由两个部分组成,文件和文件系统,这都需要管理和抽象。

文件的表示

inode是内核选择用于表示文件内容和相关元数据的方法。
各个VFS组件的相互关系**inode操作:**创建连接、文件重命名、在目录中生成新文件、删除文件。
**文件操作:**作用于文件的数据内容。包含一些显然的操作(如读和写),还包括设置文件位置指针和创建内存映射之类的操作。

上面的图中,一共有两条线路可以走。

  1. 从内核出发的一条路,super_block->f_dentry->i_dentry->inode->address_space
  2. 从进程出发的一条路,task_struct->file_struct->f_dentry->inode->address_space

结构体task_struct中,包含成员files用于保存所有打开的文件,此成员是一个数组,访问时利用文件描述符作为索引。各个数组项包含的对象不仅关联对应文件的inode,还包含一个指针,指向用于加速查找操作的目录项缓存的一个成员。打开的文件总是分配到系统中的一个特定的进程,内核必须在数据结构中存储文件和进程之间的关联。

内核中还需要其他结构来保存和inode相关的信息,特别重要的是与每个inode关联的数据段,都存储了文件的内容和目录项表。每个inode还包含了指向底层文件系统的超级块对象的指针,用于执行对inode本身的操作。

文件系统和超级块信息

VFS支持的文件系统类型通过一种特殊的内核对象连接起来,该对象提供了一种读取超级块的方法,超级块中包含了文件系统的关键信息,包括,块长度,最大文件长度。与此同时,还包
含了读、写inode的函数指针。

内核中还建立了一个链表,用来存放所有活动文件的超级块实例。
进一步的,超级块实例中存放着一个列表,保存文件系统中所有修改过的inode,也即脏inode。这个列表的作用是很容易标识已经修改过的文件和目录,以便将其写回到存储介质中。

补充知识点:
回写必须经过协调,保证一定程度上最小化开销,因为这是非常费时的操作。另一方面,如果写回数据的间隔太长,可能导致数据丢失。如系统崩溃。所以,内核会周期性的扫描脏块的列表,并将修改传输到底层硬件。

inode

VFS的inode结构如下:

struct inode {
        umode_t                 i_mode;
        unsigned short          i_opflags;
        kuid_t                  i_uid;
        kgid_t                  i_gid;
        unsigned int            i_flags;

#ifdef CONFIG_FS_POSIX_ACL
        struct posix_acl        *i_acl;
        struct posix_acl        *i_default_acl;
#endif

        const struct inode_operations   *i_op;
        struct super_block      *i_sb;
        struct address_space    *i_mapping;

#ifdef CONFIG_SECURITY
        void                    *i_security;
#endif

        /* Stat data, not accessed from path walking */
        unsigned long           i_ino;
        /*
         * Filesystems may only read i_nlink directly.  They shall use the
         * following functions for modification:
         *
         *    (set|clear|inc|drop)_nlink
         *    inode_(inc|dec)_link_count
         */
        union {
                const unsigned int i_nlink;
                unsigned int __i_nlink;
        };
        dev_t                   i_rdev;
        loff_t                  i_size;
        struct timespec         i_atime;
        struct timespec         i_mtime;
        struct timespec         i_ctime;
        spinlock_t              i_lock; /* i_blocks, i_bytes, maybe i_size */
        unsigned short          i_bytes;
        unsigned int            i_blkbits;
#if defined(CONFIG_IMA) && (defined(CONFIG_PPC64) || defined(CONFIG_S390))
        /* 4 bytes hole available on both required architectures */
        RH_KABI_FILL_HOLE(atomic_t              i_readcount)
#endif
        blkcnt_t                i_blocks;
#ifdef __NEED_I_SIZE_ORDERED
        seqcount_t              i_size_seqcount;
#endif

        /* Misc */
        unsigned long           i_state;
        struct mutex            i_mutex;

        unsigned long           dirtied_when;   /* jiffies of first dirtying */

        struct hlist_node       i_hash;
        RH_KABI_RENAME(struct list_head i_wb_list,
                       struct list_head i_io_list); /* backing dev IO list */
        struct list_head        i_lru;          /* inode LRU list */
        struct list_head        i_sb_list;
        union {
                struct hlist_head       i_dentry;
                struct rcu_head         i_rcu;
        };
        u64                     i_version;
        atomic_t                i_count;
        atomic_t                i_dio_count;
        atomic_t                i_writecount;
        const struct file_operations    *i_fop; /* former ->i_op->default_file_ops */
        struct file_lock        *i_flock;
        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
        __u32                   i_fsnotify_mask; /* all events this inode cares about */
        RH_KABI_REPLACE(struct hlist_head i_fsnotify_marks,
                        struct fsnotify_mark_connector __rcu *i_fsnotify_marks)
#endif

#if defined(CONFIG_IMA) && defined(CONFIG_X86_64)
        atomic_t                i_readcount; /* struct files open RO */
#endif
        void                    *i_private; /* fs or device private pointer */
};

知识点1:
inode是内存中处理的,因此包含了一些实际介质上存储的inode所没有的成员。这些是由内核自身从底层文件系统读入信息时生成或动态建立

知识点2:
大部分成员是元数据信息,用于管理简单的状态信息。

时间信息,i_atimei_mtime, i_ctime,分别存储了最后访问时间,最后修改时间,最后inode修改时间。i_mtime意味着修改与inode相关的数据段内容,i_ctime意味着修改inode结构自身,导致了i_ctime的改变。

文件长度保存在i_size,按照字节计算。i_blocks指定了文件按块计算的长度,此值与文件系统相关,不属于文件自身。

VFS inode都由一个唯一的编号标识,保存在i_ino中,i_count是一个使用计数器,指定访问该inode结构的进程数目。
例如,进程通过fork复制自身时,inode会由不同进程同时使用。i_links也是一个计数器,记录使用inode的硬链接总数。

文件的访问权限和所有权保存在i_mode(文件类型和访问权限)、i_uidi_gid(与该文件相关的UIDGID)中。

inode操作

内核中提供了大量的函数,对inode进行操作,并为此定义了函数指针的集合(将其封装inode_operations结构体中 )。实际数据是通过具体文件系统的实现操作的,调用接口保持不变,实际工作由特定于实现的函数完成。

inode结构有两个指针(i_opi_fop),指向实现了上述抽象的数组。一个与inode操作有关,一个与file操作有关。inode最终都指向了file_operations结构的指针。file_operations用于操作文件中包含的数据,而inode_operations负责管理结构性的操作(删除一个文件)和文件相关的元数据(属性)。

struct inode_operations {
	struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
	void * (*follow_link) (struct dentry *, struct nameidata *);
	int (*permission) (struct inode *, int);
	struct posix_acl * (*get_acl)(struct inode *, int);

	int (*readlink) (struct dentry *, char __user *,int);
	void (*put_link) (struct dentry *, struct nameidata *, void *);

	int (*create) (struct inode *,struct dentry *, umode_t, bool);
	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 *);
	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 *);
	int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
		      u64 len);
	int (*update_time)(struct inode *, struct timespec *, int);
	int (*atomic_open)(struct inode *, struct dentry *,
			   struct file *, unsigned open_flag,
			   umode_t create_mode, int *opened);
} ____cacheline_aligned;

lookup根据文件系统对象的名称查找inode实例。
unlink用于删除文件。(补充,如果硬链接的引用计数器表明该inode仍然被多个文件使用,则不会执行删除操作。)
xattr函数建立、读取、删除文件的扩展属性。
truncate修改指定inode长度,该函数只接受一个参数,即所处理的inode的数据结构。
truncate_range用于截断一个范围内的块,但该操作只有共享内存文件系统支持。
follow_link根据符号链接查找目标文件的inode
fallocate用于对文件预先分配空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值