在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;