vfs

vfs

数据结构

super_block

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
30
31
32
33
34
35
struct super_block {
struct list_head s_list; // 指向超级块链表的指针
dev_t s_dev; // 设备标识符
unsigned long s_blocksize; // 以字节为单位的块大小
unsigned long s_old_blocksize; // 以位为单位的旧的块大小
unsigned char s_blocksize_bits; // 以位为单位的块大小
unsigned char s_dirt; // 修改脏标志
unsigned long long s_maxbytes; // 文件大小上限
struct file_system_type s_type; // 文件系统类型
struct super_operations s_op; // 超级块方法
struct dquot_operations *dq_op; // 磁盘限额方法
struct quotactl_ops *s_qcop; // 限额控制方法
struct export_operations *s_export_op; // 到处方法
unsigned long s_flags; // 挂载标志
unsigned long s_magic; // 文件系统魔数
struct dentry *s_root; // 目录挂载点
struct rw_semaphore s_umount; // 卸载信号量
struct semaphore s_lock; // 超级块信号量
int s_count; // 引用计数
int s_syncing; // 文件系统同步标志
int s_need_sync_fs; // 尚未同步标志
atomic_t s_active; // 活动引用计数
void *s_security; // 安全模块
struct list_head s_dirty; // 脏节点链表
struct list_head s_io; // 回写链表
struct hlist_head s_anon; // 匿名目录项
struct list_head s_files; // 被分配文件链表
struct block_device *s_bdev; // 相关块设备
struct list_head s_instances; // 该类型文件系统
struct quota_info s_dquot; // 限额相关选项
char s_id[32]; // 文本名字
void *s_fs_info; // 文件系统特殊信号
struct semaphore s_vfs_rename_sem; // 重命名信号量

};

所有的超级块对象都以双向循环链表的形式链接在一起,链表中第一个元素用super_blocks变量来表示。s_list字段指向相邻的元素。sb_lock是保护链表的自旋锁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct super_operations {

struct inode *(*alloc_inode) (struct super_block *sb);//在给定的超级块下创建并初始化一个新的索引节点对象
void (*destroy_inode) (struct inode *);//释放跟定的索引节点
void (*read_inode) (struct inode *);//以inode->i_ino为索引,从磁盘上读取索引节点,并填充内存中对应的索引节点结构的剩余部分
void (*dirty_inode) (struct inode *);//VFS在索引节点脏时(被修改)会调用此函数,日志文件系统(ect3等)执行该函数进行日志更新
void (*write_inode) (struct inode *, int);//将给定的索引节点写入磁盘,wait参数知名写操作是否需要同步
void (*put_inode) (struct inode *);//释放给定索引节点
void (*drop_inode) (struct inode *);//在最后一个索引节点被释放后,VFS调用此函数
void (*delete_inode) (struct inode *);//从磁盘上删除给点的索引节点
void (*put_super) (struct super_block *);//在卸载文件系统时由VFS调用,用来释放超级块
void (*write_super) (struct super_block *);//使文件系统的数据元与磁盘上的文件系统同步
int (*sync_fs) (struct super_block *, int);//是文件系统数据源与磁盘上的文件系统同步
void (*write_super_lockfs) (struct super_block *);//此函数首先禁止对文件系统作改变,再使用给定的超级块更新磁盘上的超级块。LVM会调用此函数
void (*unlockfs) (struct super_block *);//对文件系统解除锁定
int (*statfs) (struct super_block *, struct statfs *);//VFS通过调用该函数获取文件系统状态,文件系统信息放置在statfs中
int (*remount_fs) (struct super_block *, int *, char *);//当制定新的安装选项重新安装文件系统时,vfs会调用该函数
void (*clear_inode) (struct inode *);//释放索引节点
void (*umount_begin) (struct super_block *);//调用该函数释放索引节点,并清空包含相关数据的所有页面
int (*show_options) (struct seq_file *, struct vfsmount *);
};
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
struct inode {
struct hlist_node i_hash; /* hash list */
struct list_head i_list; /* list of inodes */
struct list_head i_dentry; /* list of dentries */
unsigned long i_ino; /* inode number */
atomic_t i_count; /* reference counter */
umode_t i_mode; /* access permissions */
unsigned int i_nlink; /* number of hard links */
uid_t i_uid; /* user id of owner */
gid_t i_gid; /* group id of owner */
kdev_t i_rdev; /* real device node */
loff_t i_size; /* file size in bytes */
struct timespec i_atime; /* last access time */
struct timespec i_mtime; /* last modify time */
struct timespec i_ctime; /* last change time */
unsigned int i_blkbits; /* block size in bits */
unsigned long i_blksize; /* block size in bytes */
unsigned long i_version; /* version number */
unsigned long i_blocks; /* file size in blocks */
unsigned short i_bytes; /* bytes consumed */
spinlock_t i_lock; /* spinlock */
struct rw_semaphore i_alloc_sem; /* nests inside of i_sem */
struct semaphore i_sem; /* inode semaphore */
struct inode_operations *i_op; /* inode ops table */
struct file_operations *i_fop; /* default inode ops */
struct super_block *i_sb; /* associated superblock */
struct file_lock *i_flock; /* file lock list */
struct address_space *i_mapping; /* associated mapping */
struct address_space i_data; /* mapping for device */
struct dquot *i_dquot[MAXQUOTAS]; /* disk quotas for inode */
struct list_head i_devices; /* list of block devices */
struct pipe_inode_info *i_pipe; /* pipe information */
struct block_device *i_bdev; /* block device driver */
unsigned long i_dnotify_mask; /* directory notify mask */
struct dnotify_struct *i_dnotify; /* dnotify */
unsigned long i_state; /* state flags */
unsigned long dirtied_when; /* first dirtying time */
unsigned int i_flags; /* filesystem flags */
unsigned char i_sock; /* is this a socket? */
atomic_t i_writecount; /* count of writers */
void *i_security; /* security module */
__u32 i_generation; /* inode version number */
union {
void *generic_ip; /* filesystem-specific info */
} u;
};

inode存在于两个双向链表中:

  • 一个是inode(i_sb_list)所在文件系统的super_block的 s_inodes(表头)字段 链表中
  • 一个是根据inode的使用状态存在于以下三个链表中的某个链表中(i_list):
    1. 未用的: inode_unused 链表
    2. 正在使用的: inode_in_use 链表
    3. 脏的: super block中的s_dirty 链表
  • 另外,还有一个重要的链表: inode_hashtable(这个暂不介绍) (i_hash).


inode 的 i_fop字段

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
30
31
32
33
34
35
36
37
38
39
40
41
42
struct file_operations {
//指向拥有该结构的模块的指针
struct module *owner;
//修改当前文件的读写位置
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iterate) (struct file *, struct dir_context *);
int (*iterate_shared) (struct file *, struct dir_context *);
__poll_t (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
unsigned long mmap_supported_flags;
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
loff_t, size_t, unsigned int);
int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
u64);
ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,
u64);
} __randomize_layout;

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
26
27
28
29
30
31
32
33
34
35
36
37
struct file 
{
/*
* fu_list becomes invalid after file_free is called and queued via
* fu_rcuhead for RCU freeing
*/
union
{
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
#define f_dentry f_path.dentry
#define f_vfsmnt f_path.mnt
const struct file_operations *f_op;
atomic_t f_count;
unsigned int f_flags;
mode_t f_mode;
loff_t f_pos;
struct fown_struct f_owner;
unsigned int f_uid, f_gid;
struct file_ra_state f_ra;

unsigned long f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data;

#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
spinlock_t f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
};
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 dentry
{
/* RCU lookup touched fields */
unsignedint d_flags;/* protected by d_lock */
seqcount_t d_seq;/* per dentry seqlock */
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 */
unsignedchar d_iname[DNAME_INLINE_LEN];/* small names */
/* Ref lookup also touches following */
struct lockref d_lockref;/* per-dentry lock and refcount */
conststruct dentry_operations *d_op;
struct super_block *d_sb;/* The root of the dentry tree */
unsignedlong 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_unused 链表
  • 正在使用的目录项: 对应inode的 i_dentry 链表
  • 表示父子目录结构的链表
    另外,还有一个重要的链表: inode_hashtable(这个暂不介绍).

而从进程的角度来看的话,大多数时候并不需要那么多的属性,所有VFS通过以下3个结构体和进程紧密联系在一起。

  • struct files_struct :由进程描述符中的 files 目录项指向,所有与单个进程相关的信息(比如打开的文件和文件描述符)都包含在其中。
  • struct fs_struct :由进程描述符中的 fs 域指向,包含文件系统和进程相关的信息。
  • struct mmt_namespace :由进程描述符中的 mmt_namespace 域指向。
1
2
3
4
5
6
7
8

struct fs_struct {
atomic_t count;
rwlock_t lock;
int umask;
struct dentry * root, * pwd, * altroot;
struct vfsmount * rootmnt, * pwdmnt, * altrootmnt;
};

其中:
count:共享这个表的进程个数
lock:用于表中字段的读/写自旋锁
umask:当打开文件设置文件权限时所使用的位掩码
root:根目录的目录项
pwd:当前工作目录的目录项
altroot:模拟根目录的目录项(在80x86结构上始终为NULL)
rootmnt:根目录所安装的文件系统对象
pwdmnt:当前工作目录所安装的文件系统对象
altrootmnt:模拟根目录所安装的文件系统对象(在80x86结构上始终为NULL)

1
2
3
4
5
6
7
8
9
10
struct files_struct {
atomic_t count; /* 使用计数 */
struct fdtable *fdt; /* 指向其他fd表的指针 */
struct fdtable fdtab;/* 基 fd 表 */
spinlock_t file_lock ____cacheline_aligned_in_smp; /* 单个文件的锁 */
int next_fd; /* 缓存下一个可用的fd */
struct embedded_fd_set close_on_exec_init; /* exec()时关闭的文件描述符链表 */
struct embedded_fd_set open_fds_init; /* 打开的文件描述符链表 */
struct file * fd_array[NR_OPEN_DEFAULT]; /* 缺省的文件对象数组 */
};

VFS中还有2个专门针对文件系统的2个对象:

  • struct file_system_type: 用来描述文件系统的类型(比如ext3,ntfs等等)
  • struct vfsmount : 描述一个安装文件系统的实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct file_system_type {
const char *name; /* 文件系统名称 */
int fs_flags; /* 文件系统类型标志 */
/* 从磁盘中读取超级块,并且在文件系统被安装时,在内存中组装超级块对象 */
int (*get_sb) (struct file_system_type *, int,
const char *, void *, struct vfsmount *);
/* 终止访问超级块 */
void (*kill_sb) (struct super_block *);
struct module *owner; /* 文件系统模块 */
struct file_system_type * next; /* 链表中下一个文件系统类型 */
struct list_head fs_supers; /* 超级块对象链表 */

/* 下面都是运行时的锁 */
struct lock_class_key s_lock_key;
struct lock_class_key s_umount_key;

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

安装点,如exe4安装在5个位置,则有5个安装点。

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
30
31
32
33
34
35
struct vfsmount {
struct list_head mnt_hash; /* 散列表 */
struct vfsmount *mnt_parent; /* 父文件系统,也就是要挂载到哪个文件系统 */
struct dentry *mnt_mountpoint; /* 安装点的目录项 */
struct dentry *mnt_root; /* 该文件系统的根目录项 */
struct super_block *mnt_sb; /* 该文件系统的超级块 */
struct list_head mnt_mounts; /* 子文件系统链表 */
struct list_head mnt_child; /* 子文件系统链表 */
int mnt_flags; /* 安装标志 */
/* 4 bytes hole on 64bits arches */
const char *mnt_devname; /* 设备文件名 e.g. /dev/dsk/hda1 */
struct list_head mnt_list; /* 描述符链表 */
struct list_head mnt_expire; /* 到期链表的入口 */
struct list_head mnt_share; /* 共享安装链表的入口 */
struct list_head mnt_slave_list;/* 从安装链表 */
struct list_head mnt_slave; /* 从安装链表的入口 */
struct vfsmount *mnt_master; /* 从安装链表的主人 */
struct mnt_namespace *mnt_ns; /* 相关的命名空间 */
int mnt_id; /* 安装标识符 */
int mnt_group_id; /* 组标识符 */
/*
* We put mnt_count & mnt_expiry_mark at the end of struct vfsmount
* to let these frequently modified fields in a separate cache line
* (so that reads of mnt_flags wont ping-pong on SMP machines)
*/
atomic_t mnt_count; /* 使用计数 */
int mnt_expiry_mark; /* 如果标记为到期,则为 True */
int mnt_pinned; /* "钉住"进程计数 */
int mnt_ghosts; /* "镜像"引用计数 */
#ifdef CONFIG_SMP
int *mnt_writers; /* 写者引用计数 */
#else
int mnt_writers; /* 写者引用计数 */
#endif
};

被Linux支持的文件系统,都有且仅有一个file_system_type结构而不管它有零个或多个实例被安装到系统 中。每安装一个文件系统,就对应有一个超级块和安装点。超级块通过它的一个域s_type指向其对应的具体的文件系统类型。具体的 文件系统通过file_system_type中的一个域fs_supers链接具有同一种文件类型的超级块。同一种文件系统类型的超级块通过域s_instances链接。