通用文件模型及VFS-VFS结构

在VFS接口实现中,涉及大量的数据结构。VFS结构由两个部分组成:文件文件系统,这些都需要管理和抽象。

1.文件表示

inode是Linux内核选择用于表示内容和相关元数据的方法。在抽象对底层文件系统的访问时,并未使用固定的函数,而是使用了函数指针。这函数指针保存在两个结构中,包括了所有相关的函数。
(1)inode操作:创建链接、文件重命名、在目录中生成新文件、删除文件。

const struct inode_operations	*i_op;

(2)文件操作:作用于文件的数据内容。他们包含一些显然的操作(如读和写),还包括如设置文件位置指针和创建内存映射之类的操作。

const struct file_operations	*i_fop;

打开的文件总是分配到系统中一个特定的进程,内核必须在数据结构中存储文件和进程之间的关联,各个文件系统实现也能在VFS inode中存储自身的数据(不通过VFS层操作)。

2.文件系统和超级块信息

VFS支持的文件系统类型通过一种特殊的内核对象链接进来,该对象提供了一种读取超级块的方法。除了文件系统的关键信息(如块长度、最大文件长度等等)之外,超级块还包含了读、写、操作inode的函数指针。
内核还建立了一个链表,包含所有活动文件系统的超级块实例。之所以使用活动(active)这个术语替代已装载(mounted),是因为在某些环境中,有可能使用一个超级块对应几个装载点。
尽管每个文件系统在file_system_type中只出现一次,但所有超级块实例的链表中,可能有几个同一文件系统类型的超级块实例,因为在各个块设备/分区上可能存储了同一类型的几个文件系统。

struct file_system_type {
	const char *name;
	int fs_flags;
#define FS_REQUIRES_DEV		1 
#define FS_BINARY_MOUNTDATA	2
#define FS_HAS_SUBTYPE		4
#define FS_USERNS_MOUNT		8	/* Can be mounted by userns root */
#define FS_USERNS_DEV_MOUNT	16 /* A userns mount does not imply MNT_NODEV */
#define FS_USERNS_VISIBLE	32	/* FS must already be visible */
#define FS_RENAME_DOES_D_MOVE	32768	/* FS will handle d_move() during rename() internally. */
	struct dentry *(*mount) (struct file_system_type *, int,
		       const char *, void *);
	struct dentry *(*mount2) (struct vfsmount *, struct file_system_type *, int,
			       const char *, void *);
	void *(*alloc_mnt_data) (void);
	void (*kill_sb) (struct super_block *);
	struct module *owner;
	struct file_system_type * next;
	struct hlist_head fs_supers;

	struct lock_class_key s_lock_key;
	struct lock_class_key s_umount_key;
	struct lock_class_key s_vfs_rename_key;
	struct lock_class_key s_writers_key[SB_FREEZE_LEVELS];

	struct lock_class_key i_lock_key;
	struct lock_class_key i_mutex_key;
	struct lock_class_key i_mutex_dir_key;
};

3.VFS的inode结构源码

/*
 * Keep mostly read-only and often accessed (especially for
 * the RCU path lookup and 'stat' data) fields at the beginning
 * of the 'struct 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;
	enum rw_hint		i_write_hint;
	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 */
	unsigned long		dirtied_time_when;

	struct hlist_node	i_hash;
	struct list_head	i_io_list;	/* backing dev IO list */
#ifdef CONFIG_CGROUP_WRITEBACK
	struct bdi_writeback	*i_wb;		/* the associated cgroup wb */

	/* foreign inode detection, see wbc_detach_inode() */
	int			i_wb_frn_winner;
	u16			i_wb_frn_avg_time;
	u16			i_wb_frn_history;
#endif
	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;
#ifdef CONFIG_IMA
	atomic_t		i_readcount; /* struct files open RO */
#endif
	const struct file_operations	*i_fop;	/* former ->i_op->default_file_ops */
	struct file_lock_context	*i_flctx;
	struct address_space	i_data;
	struct list_head	i_devices;
	union {
		struct pipe_inode_info	*i_pipe;
		struct block_device	*i_bdev;
		struct cdev		*i_cdev;
		char			*i_link;
	};

	__u32			i_generation;

#ifdef CONFIG_FSNOTIFY
	__u32			i_fsnotify_mask; /* all events this inode cares about */
	struct hlist_head	i_fsnotify_marks;
#endif

#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
	struct fscrypt_info	*i_crypt_info;
#endif
	void			*i_private; /* fs or device private pointer */
};

4.VFS与各文件系统的关系

VFS之基本数据结构
在这里插入图片描述
VFS支持三种类型的文件系统:基于磁盘的文件系统、特殊的文件系统和网络文件系统。
基于磁盘的文件系统:管理在非易失去介质存储的文件。这种类型的文件系统最多,比如常见的ext2/3/4、fat、ntfs等。
特殊的文件系统:在内核中生成,是一种使用户应用程序与用户通信的方法,比如常见的proc文件系统,它存储于内存,不占用硬件存储空间。
网络文件系统:这种文件系统可以通过网络访问另一台计算机上的数据,相当于连接到本地计算机一样。比如nfs文件系统。

5.VFS基本数据结构

VFS通用文件模型包含有4种基本的数据结构,通过这几个数据结构实现将硬盘上的文件抽象到内存中:

  • 超级块
  • 目录项
  • 索引节点
  • 文件
    在这里插入图片描述

6.inode操作

内核提供了大量函数对inode进行操作。为此定义了一个函数指针的集合,以抽象这些操作,因为实际数据是通过具体的文件系统的实现操作的,调用接口不变,但实际工作是由特定实现函数完成的。

struct inode_operations {
	struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);//根据文件系统对象的名称(表示为字符串)查找其inode实例
	const char * (*follow_link) (struct dentry *, void **);
	int (*permission) (struct inode *, int);
	int (*permission2) (struct vfsmount *, struct inode *, int);
	struct posix_acl * (*get_acl)(struct inode *, int);

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

	int (*create) (struct inode *,struct dentry *, umode_t, bool);//VFS通过系统调用create()和open()来调用该函数,从而为dentey对象创建一个新的索引节点,在创建时使用inode指定初始的一个模式。
	int (*link) (struct dentry *,struct inode *,struct dentry *);
	//该函数被系统调用link()调用,用来创建硬链接,硬链接名称由dentry指定,链接对象是dir目录中old_dentry目录项所指定。
	int (*unlink) (struct inode *,struct dentry *);//该函数被系统调用unlink()调用,从目录dir中删除有目录项dentry指定的索引节点对象。
	int (*symlink) (struct inode *,struct dentry *,const char *);
	int (*mkdir) (struct inode *,struct dentry *,umode_t);//系统调用mkdir()调用,创建一个新的目录,创建时使用mode指定初始模式。
	int (*rmdir) (struct inode *,struct dentry *);//系统调用rmdir调用,创建一个新的目录,创建时使用mode指定初始模式。
	int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
	int (*rename) (struct inode *, struct dentry *,
			struct inode *, struct dentry *);
	int (*rename2) (struct inode *, struct dentry *,
			struct inode *, struct dentry *, unsigned int);
	int (*setattr) (struct dentry *, struct iattr *);
	int (*setattr2) (struct vfsmount *, 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);
	int (*tmpfile) (struct inode *, struct dentry *, umode_t);
	int (*set_acl)(struct inode *, struct posix_acl *, int);
} ____cacheline_aligned;

inode结构有两个指针(i_op和i_fop),file_operations用于操作文件中包含的数据,inode_operations负责管理结构性的操作(删除一个文件)和文件相关的元数据(属性等)。
inode_operations介绍

7.目录项缓存

由于块设备速度较慢,可能需要很长时间才能找到与一个文件名关联的inode,即使设备数据已经在页缓存。linux使用目录项缓存(简称dentry缓存)来快速访问此前的查找操作的结果。该缓存围绕着struct dentry建立。
在VFS连同文件系统实现读取的一个目录项(目录或文件)的数据之后,则创建一个dentry实例,以缓存找到的数据。

struct dentry {
	/* RCU lookup touched fields */
	//由d_lock保护,目录项缓存标识
	unsigned int d_flags;		/* protected by d_lock */
	seqcount_t d_seq;		/* per dentry seqlock */
	//内核使用dentry_hashtable对detry进程管理
	struct hlist_bl_node d_hash;	/* lookup hash list */
	//父目录的目录项
	struct dentry *d_parent;	/* parent directory */
	//目录项名称
	struct qstr d_name;
	//存放短的文件名称
	struct inode *d_inode;		/* Where the name belongs to - NULL is
					 * negative */
	unsigned char d_iname[DNAME_INLINE_LEN];	/* small names */

	/* Ref lookup also touches following */
	struct lockref d_lockref;	/* per-dentry lock and refcount */
	const struct dentry_operations *d_op;
	struct super_block *d_sb;	/* The root of the dentry tree */
	unsigned long d_time;		/* used by d_revalidate */
	void *d_fsdata;			/* fs-specific data */

	struct list_head d_lru;		/* LRU list */
	struct list_head d_child;	/* child of parent list */
	struct list_head d_subdirs;	/* our children */
	/*
	 * d_alias and d_rcu can share memory
	 */
	union {
		struct hlist_node d_alias;	/* inode alias list */
	 	struct rcu_head d_rcu;
	} d_u;
};

缓存的组织

dentry结构不仅使得易于处理文件系统,对提高系统性能也很关键。dentry对象在内存中的组织,涉及下面两个部分。
(1)一个散列表(dentry_hashtable)包含了所有的dentry对象。
(2)一个LRU(最近最少使用,least recently used)链表,其中不再使用的对象将授予一个最后宽期限,宽期限过后才从内存移除。

dentry操作

dentry_operation结构保存了一些指向各种特定文件系统可以对dentry对象执行的操作函数指针。

struct dentry_operations {
	int (*d_revalidate)(struct dentry *, unsigned int);
	int (*d_weak_revalidate)(struct dentry *, unsigned int);
	//计算散列值,该值用于将对象放置到dentry散列表中。
	int (*d_hash)(const struct dentry *, struct qstr *);
	//比较两个dentry对象的文件名称
	int (*d_compare)(const struct dentry *, const struct dentry *,
	unsigned int, const char *, const struct qstr *);
	int (*d_delete)(const struct dentry *);
	void (*d_release)(struct dentry *);
	void (*d_prune)(struct dentry *);
	void (*d_iput)(struct dentry *, struct inode *);
	char *(*d_dname)(struct dentry *, char *, int);
	struct vfsmount *(*d_automount)(struct path *);
	int (*d_manage)(struct dentry *, bool);
	struct inode *(*d_select_inode)(struct dentry *, unsigned);
	void (*d_canonical_path)(const struct path *, struct path *);
	struct dentry *(*d_real)(struct dentry *, struct inode *);
} ____cacheline_aligned;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

<( ̄︶ ̄)Okay.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值