第十三章 虚拟文件系统

1. 虚拟文件系统VFS

虚拟文件系统是Linux内核为支持各种文件系统,在用户进程和文件系统实现之间引入的一个软件抽象层——Virtual File System,仅存在于内存中。

“一切皆文件”是Unix/Linux的设计哲学之一,Linux将普通文件、目录、字符设备、块设备、套接字等都看做文件,主要是他们的操作是一套统一的接口——知乎相关问题:如何理解「In UNIX, everything is a file」?


源自:Anatomy of the Linux file system

VFS与块I/O结合,定义了所有文件系统都支持的、基本的、概念上的接口和数据结构这一抽象层。

  • 使得用户使用一套标准的系统调用来操作文件而无需涉及具体的文件系统和存储介质,实现跨文件系统操作
  • 引入新的文件系统只要实现VFS定义的各个接口,不需要修改内核
  • 几个抽象概念:文件、目录项、索引节点、安装点(mount point,命名空间)

2. VFS中的对象类型

VFS采用面向对象的设计,主要包括超级块、索引节点、目录项、文件四个结构体对象。
超级块: 存储文件系统的控制信息。描述文件系统的状态、文件系统类型、大小、区块数、索引节点数等,存放于磁盘的特定扇区中。
索引节点: 存储文件的元数据信息。描述文件的大小、拥有者、创建时间、访问模式、磁盘mapping等。
目录: 就是一个文件,存放的数据就是目录信息,所以操作文件的接口完全可以用在目录上。
目录项: 在一个文件路径中,路径的每一部分都被称为目录项;如路径/home/source/helloworld.c中,目录 /, home, source和文件 helloworld.c都是一个目录项。
文件: 一组在逻辑上具有完整意义的信息项的系列。除了普通文件,其他诸如目录、设备、套接字等 也以文件被对待。

源自:从文件 I/O 看 Linux 的虚拟文件系统

每个对象包含一个操作对象:

  • super_operations对象:针对特定文件系统能调用的方法,如write_inode()/sync_fs()
  • inode_operations对象:针对特定文件所能调用的方法,如create()/link()
  • dentry_operations对象:针对特定目录所能调用的方法,如d_compare()/d_delete()
  • file_operations对象:包括进程针对已打开文件所能调用的方法,如read()/write()

  • file_system_type:描述文件系统及其性能

  • vfsmount:描述安装点信息

2.1 超级块super_block对象

指文件系统超级块或文件系统控制块,代表一个具体的已安装文件系统,存储着整个文件系统的信息,一般存放在磁盘特定扇区。当一个具体的文件系统安装时,内核从特定位置读取信息来填充内存中的超级块对象,一个安装实例对应一个超级块对象。

VFS超级块在各种具体文件系统在安装时建立的,并在卸载时被自动删除,所有超级块对象以双向链表链接在一起。

2.1.1 超级快的定义:
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
struct super_block {
struct list_head s_list; /* Keep this first 所有超级块的链表 */
dev_t s_dev; /* search index; _not_ kdev_t 标识文件系统所在的块设备,前者使用设备编号,而@s_bdev指向内存中的block_device结构 */
unsigned char s_blocksize_bits; /* 以位为单位的块大小*/
unsigned long s_blocksize; /* 以字节为单位的块大小*/
loff_t s_maxbytes; /* Max file size */
struct file_system_type *s_type; /* 文件系统类型 */
const struct super_operations *s_op; /* 超级块方法 */
const struct dquot_operations *dq_op; /* 磁盘限额方法 */
const struct quotactl_ops *s_qcop; /* 限额控制方法 */
const struct export_operations *s_export_op; /* 导出方法,nfs时使用*/
unsigned long s_flags; /* 挂载标志 */
unsigned long s_iflags; /* internal SB_I_* flags */
unsigned long s_magic; /* 文件系统的幻数*/
struct dentry *s_root; /* 目录挂载点 */
struct rw_semaphore s_umount; /* 卸载信号量 */
int s_count; /* 超级块引用计数 */
atomic_t s_active; /* 活动引用计数 */
#ifdef CONFIG_SECURITY
void *s_security; /* 安全模块 */
#endif
const struct xattr_handler **s_xattr; /* 扩展属性操作 */
const struct fscrypt_operations *s_cop; /* 文件加密操作 */
struct hlist_bl_head s_anon; /* 匿名目录项 anonymous dentries for (nfs) exporting */
struct list_head s_mounts; /* list of mounts; _not_ for fs use */
struct block_device *s_bdev;
struct backing_dev_info *s_bdi;
struct mtd_info *s_mtd; /* 存储磁盘信息 */
struct hlist_node s_instances; /* 该类型文件系统 */
unsigned int s_quota_types; /* Bitmask of supported quota types */
struct quota_info s_dquot; /* Diskquota specific options */
struct sb_writers s_writers;
char s_id[32]; /* Informational name */
u8 s_uuid[16]; /* UUID */
void *s_fs_info; /* Filesystem private info 具体的文件系统实现希望添加的数据 */
unsigned int s_max_links;
fmode_t s_mode; /* 安装权限 */
/* Granularity of c/m/atime in ns.
Cannot be worse than a second */
u32 s_time_gran; /* 时间戳粒度 */
/*
* The next field is for VFS *only*. No filesystems have any business
* even looking at it. You had been warned.
*/
struct mutex s_vfs_rename_mutex; /* Kludge */
/*
* Filesystem subtype. If non-empty the filesystem type field
* in /proc/mounts will be "type.subtype"
*/
char *s_subtype; /* 子类型名称 */
/*
* Saved mount options for lazy filesystems using
* generic_show_options()
*/
char __rcu *s_options; /* 安装选项 */
const struct dentry_operations *s_d_op; /* default d_op for dentries */
/*
* Saved pool identifier for cleancache (-1 means none)
*/
int cleancache_poolid;
struct shrinker s_shrink; /* per-sb shrinker handle */
/* Number of inodes with nlink == 0 but still referenced */
atomic_long_t s_remove_count;
/* Being remounted read-only */
int s_readonly_remount;
/* AIO completions deferred from interrupt context */
struct workqueue_struct *s_dio_done_wq;
struct hlist_head s_pins;
/*
* Owning user namespace and default context in which to
* interpret filesystem uids, gids, quotas, device nodes,
* xattrs and security labels.
*/
struct user_namespace *s_user_ns;
/*
* Keep the lru lists last in the structure so they always sit on their
* own individual cachelines.
*/
struct list_lru s_dentry_lru ____cacheline_aligned_in_smp;
struct list_lru s_inode_lru ____cacheline_aligned_in_smp;
struct rcu_head rcu;
struct work_struct destroy_work;
struct mutex s_sync_lock; /* sync serialisation lock */
/*
* Indicates how deep in a filesystem stack this SB is
*/
int s_stack_depth;
/* s_inode_list_lock protects s_inodes */
spinlock_t s_inode_list_lock ____cacheline_aligned_in_smp;
struct list_head s_inodes; /* all inodes所有inode链表 */
spinlock_t s_inode_wblist_lock;
struct list_head s_inodes_wb; /* writeback inodes 需要诙谐的inode */
};

扩展阅读:ecryptfs加密文件系统

超级块对象的创建、管理和撤销在fs/super.c中。

2.1.2 超级块操作:linux/fs.h
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
struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb); /* 创建并初始化一个inide对象*/
void (*destroy_inode)(struct inode *); /* 释放给定的inode*/
void (*dirty_inode) (struct inode *, int flags); /* 将给定的inode置脏*/
int (*write_inode) (struct inode *, struct writeback_control *wbc); /* 将inode写入磁盘,wait表示写操作是否需要同步 */
int (*drop_inode) (struct inode *); /* 当inode引用计数为0时,VFS调用该函数 */
void (*evict_inode) (struct inode *);
void (*put_super) (struct super_block *); /* 卸载文件系统时由VFS调用,用来释放超级块 */
int (*sync_fs)(struct super_block *sb, int wait); /* 超级块数据同步到磁盘 */
int (*freeze_super) (struct super_block *); /**/
int (*freeze_fs) (struct super_block *);
int (*thaw_super) (struct super_block *);
int (*unfreeze_fs) (struct super_block *);
int (*statfs) (struct dentry *, struct kstatfs *); /* VFS调用获取文件系统状态 */
int (*remount_fs) (struct super_block *, int *, char *); /* 指定新的安装选项重新安装文件系统时,VFS调用 */
void (*umount_begin) (struct super_block *); /* 中断安装操作 */
int (*show_options)(struct seq_file *, struct dentry *);
int (*show_devname)(struct seq_file *, struct dentry *);
int (*show_path)(struct seq_file *, struct dentry *);
int (*show_stats)(struct seq_file *, struct dentry *);
#ifdef CONFIG_QUOTA
ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
struct dquot **(*get_dquots)(struct inode *);
#endif
int (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t);
long (*nr_cached_objects)(struct super_block *,
struct shrink_control *);
long (*free_cached_objects)(struct super_block *,
struct shrink_control *);
};

在实现具体的文件系统时,可以将不需要的函数指针设置为NULL,VFS会调用通用的函数执行相应操作,一般什么也不做。


2.2 索引节点对象

索引节点包含了内核操作文件或目录需要的全部信息,在同一个文件系统内,每个索引节点号是唯一的。

2.2.1 索引节点定义:linux/fs.h

一个索引节点与文件系统中的一个文件(包括文件、目录、设备、管道)对应,和超级块一样,索引节点实际存储在磁盘上,当文件被访问时,相应的索引节点才在内存创建。

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
struct inode {
umode_t i_mode; /* 访问权限 */
unsigned short i_opflags;
kuid_t i_uid; /* 使用者id */
kgid_t i_gid; /* 使用组id */
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;
blkcnt_t i_blocks;
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount; /* 对i_size进行串行计数 */
#endif
/* Misc */
unsigned long i_state; /* 状态标志 */
struct rw_semaphore i_rwsem;
unsigned long dirtied_when; /* jiffies of first dirtying */
unsigned long dirtied_time_when;
struct hlist_node i_hash; /*散列表,用于快速查找inode */
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; /* 超级块链表 */
struct list_head i_wb_list; /* backing dev writeback 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;
unsigned i_dir_seq;
};
__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 */
};

2.2.2 索引节点操作:linux/fs.h

描述VFS操作inode对象的所有方法

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 inode_operations {
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int); /* 在特点目录中寻找索引节点,该索引节点对应dentry中给定的文件名 */
const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
int (*permission) (struct inode *, int); /* 检查访问模式 */
struct posix_acl * (*get_acl)(struct inode *, int);
int (*readlink) (struct dentry *, char __user *,int); /* readlink()系统调用 */
int (*create) (struct inode *,struct dentry *, umode_t, bool); /* create()和open()系统调用,为dentry创建新的inode */
int (*link) (struct dentry *,struct inode *,struct dentry *); /*link()系统调用,创建硬链接 */
int (*unlink) (struct inode *,struct dentry *); /* unlink()系统调用, 从inode对应目录中删除dentry指定的索引节点对象 */
int (*symlink) (struct inode *,struct dentry *,const char *); /* symlik()系统调用,创建符号链接 */
int (*mkdir) (struct inode *,struct dentry *,umode_t); /* mkdir()系统调用 */
int (*rmdir) (struct inode *,struct dentry *); /* rmdir()系统调用 */
int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t); /* mknod()系统调用 */
int (*rename) (struct inode *, struct dentry *,
struct inode *, struct dentry *, unsigned int); /* VFS调用该函数来移动文件*/
int (*setattr) (struct dentry *, struct iattr *); /* notify_change()调用,在修改节点后,通知发生了"改变事件" */
int (*getattr) (const struct path *, struct kstat *, u32, unsigned int); /* 通知索引节点需要从磁盘更新 */
ssize_t (*listxattr) (struct dentry *, char *, size_t);
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;

2.3 目录项对象

dentry结构代表逻辑意义上的文件,是对一个文件逻辑属性的描述,目录项没有对应的磁盘数据结构,VFS根据路径名现场创建它。路径中的每个部分都是一个目录项,比如路径: /mnt/cdrom/foo 其中包含/ mnt cdrom foo 4个目录项。

直接使用inode也可以找到文件,引入目录项主要为了加快文件的查找、比较。

目录项有三个状态:

  • 被使用:d_inode指向一个有效的索引节点,存在一个或多个使用者d_count>0
  • 未被使用:d_inode指向一个有效的索引节点,但当前没有使用者d_count=0,需要时直接使用
  • 负状态:没有对应有效的索引节点d_inode=NULL,(因为索引节点已经被删除了,或路径不再正确)

目录项缓存dcache包括:

  • “被使用的”目录项链表
  • “最近被使用的”双向链表
  • 散列表和相应的散列函数

目录项定义在linux/dcache.h

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
struct dentry {
/* RCU lookup touched fields */
unsigned int 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 */
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 */
union {
struct list_head d_lru; /* LRU list */
wait_queue_head_t *d_wait; /* in-lookup ones only */
};
struct list_head d_child; /* child of parent list 用于将当前dentry链接到父dentry的d_subdirs*/
struct list_head d_subdirs; /* 所有的子dentry都通过他们的d_child链接到父亲的d_subdirs our children */
/*
* d_alias and d_rcu can share memory
*/
union {
struct hlist_node d_alias; /* 别名链表 inode alias list */
struct hlist_bl_node d_in_lookup_hash; /* only for in-lookup ones */
struct rcu_head d_rcu;
} d_u;
};

目录项操作:linux/dcache.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct dentry_operations {
int (*d_revalidate)(struct dentry *, unsigned int); /* 判断目录项是否有效,VFS准备从dcache中使用一个目录项时会调用该函数*/
int (*d_weak_revalidate)(struct dentry *, unsigned int);
int (*d_hash)(const struct dentry *, struct qstr *); /* 为目录项生成hash */
int (*d_compare)(const struct dentry *,
unsigned int, const char *, const struct qstr *); /* 比较qstr类型的两个文件名 */
int (*d_delete)(const struct dentry *); /* 目录项d_count=0时,调用该函数*/
int (*d_init)(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)(const struct path *, bool);
struct dentry *(*d_real)(struct dentry *, const struct inode *,
unsigned int);
} ____cacheline_aligned;


2.4 文件对象

文件对象表示进程已打开的文件,是已打开文件在内存中的表示,用户直接操作的就是文件。
文件对象反过来指向一个目录项对象(目录项反过来指向一个索引节点)。
一个文件可能对应多个文件对象(因为多个进程访问同一个文件),但是对应的索引节点和目录项是唯一的。

文件对象没有对应的磁盘数据。
文件对象定义:linux/fs.h

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
struct file {
union {
struct llist_node fu_llist; /* 文件对象链表*/
struct rcu_head fu_rcuhead; /* 释放后的RCU链表 */
} f_u;
struct path f_path; /* 包含的目录项 */
struct inode *f_inode; /* cached value */
const struct file_operations *f_op; /* 文件操作函数 */
/*
* Protects f_ep_links, f_flags.
* Must not be taken from IRQ context.
*/
spinlock_t f_lock;
atomic_long_t f_count; /* 引用计数 */
unsigned int f_flags; /* 打开文件时指定的标志 */
fmode_t f_mode; /* 文件访问模式 */
struct mutex f_pos_lock;
loff_t f_pos; /* 文件的当前偏移量 */
struct fown_struct f_owner; /* 拥有者通过信号进行异步IO数据传送,*/
const struct cred *f_cred; /* 文件的信任状 */
struct file_ra_state f_ra; /* 预读状态 */
u64 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;
struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping; /* 页缓存映射 */
} __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
struct file_handle {
__u32 handle_bytes;
int handle_type;
/* file identifier */
unsigned char f_handle[0];
};

文件操作:

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
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 *);
unsigned int (*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 *);
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);
};


2.5 文件系统相关数据结构

file_system_type结构描述文件系统功能和行为,系统用一个全局的变量file_systems来保存所有已注册到系统中的文件系统,在mount一个文件系统时,系统遍历这个变量判断是否支持挂载的文件系统。
http://www.cnblogs.com/fengkang1008/p/4691231.html
定义:

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
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_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */
struct dentry *(*mount) (struct file_system_type *, int,
const char *, void *); /* 之前版本为get_sb, 它VFS能够和底层文件系统交互的起始点,
该函数是不能放在super_block结构中的,因为super_block是在mount执行之后才能建立的。
mount从底层文件系统获取super_block的信息,是和底层文件系统相关的 */
void (*kill_sb) (struct super_block *); /* 终止访问超级块 */
struct module *owner; /*owner是指向module的指针,仅当文件系统类型是以模块方式注册时,owner才有效 */
struct file_system_type * next; /* 下一个文件系统类型,file_systems利用这个指针将所有注册的文件系统链接起来 */
struct hlist_head fs_supers; /* 超级块对象链表,对于每一个mount的文件系统,系统都会为他创建一个super_block,
该结构保存文件系统本身以及挂载点相关的信息。由于可以同时挂载多个同一文件系统类型的文件系统(比如/ 和/home都挂载了ext3文件系统),
因此同一个文件系统类型会对应多个super block,fs_supers就把这个文件系统类型对应的super block链接起来。 */
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;
};

vfsmount描述文件系统实例,代表一个安装点,文件系统被安装几次,就会创建几个vfsmount

定义:

1
2
3
4
5
struct vfsmount {
struct dentry *mnt_root; /* 该文件系统的根目录项 root of the mounted tree */
struct super_block *mnt_sb; /* 该文件系统的超级块 pointer to superblock */
int mnt_flags; /* 安装标志 */
};

实际的mount结构:

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
struct mount {
struct hlist_node mnt_hash; /* 散列表 */
struct mount *mnt_parent; /* 父文件系统,就是挂载到哪个文件系统 */
struct dentry *mnt_mountpoint; /*安装点的目录项,挂载点 */
struct vfsmount mnt;
union {
struct rcu_head mnt_rcu;
struct llist_node mnt_llist;
};
#ifdef CONFIG_SMP
struct mnt_pcp __percpu *mnt_pcp;
#else
int mnt_count; /* 引用计数 */
int mnt_writers; /* 写者引用计数 */
#endif
struct list_head mnt_mounts; /* 子文件系统链表 list of children, anchored here */
struct list_head mnt_child; /* and going through their mnt_child */
struct list_head mnt_instance; /* mount instance on sb->s_mounts */
const char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */
struct list_head mnt_list; /* 描述符链表 */
struct list_head mnt_expire; /* link in fs-specific expiry list */
struct list_head mnt_share; /* circular list of shared mounts */
struct list_head mnt_slave_list;/* list of slave mounts */
struct list_head mnt_slave; /* slave list entry */
struct mount *mnt_master; /* slave is on master->mnt_slave_list */
struct mnt_namespace *mnt_ns; /* containing namespace */
struct mountpoint *mnt_mp; /* where is it mounted */
struct hlist_node mnt_mp_list; /* list mounts with the same mountpoint */
#ifdef CONFIG_FSNOTIFY
struct hlist_head mnt_fsnotify_marks;
__u32 mnt_fsnotify_mask;
#endif
int mnt_id; /* mount identifier */
int mnt_group_id; /* peer group identifier */
int mnt_expiry_mark; /* true if marked for expiry */
struct hlist_head mnt_pins;
struct fs_pin mnt_umount;
struct dentry *mnt_ex_mountpoint;
};


2.6 进程相关数据结构

系统中每个进程都有自己的一组打开文件,VFS利用file_struct、fs_struct和namespace三个数据结构与系统的进程联系起来。

  • file_struct:
    由进程描述符中的files域指向
    Open file table structure

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    struct files_struct {
    /*
    * read mostly part
    */
    atomic_t count; /* 引用计数 */
    bool resize_in_progress;
    wait_queue_head_t resize_wait;
    struct fdtable __rcu *fdt; /* 指向其他fd表的指针,比如进程共享描述符 */
    struct fdtable fdtab; /* 基fd表 */
    /*
    * written part on a separate cache line in SMP
    */
    spinlock_t file_lock ____cacheline_aligned_in_smp;
    unsigned int next_fd; /* 下一个可用的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]; /* 缺省的文件对象数组 */
    };
  • fs_struct:
    由进程描述符中的fs域指向

1
2
3
4
5
6
7
8
struct fs_struct {
int users; /* 用户数目 */
spinlock_t lock;
seqcount_t seq;
int umask;
int in_exec; /* 当前正在执行的文件 */
struct path root, pwd; /* 根目录路径和当前工作目录路径 */
};
  • namespace:
    由进程描述符中的 mmt_namespace域指向
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    struct mnt_namespace {
    atomic_t count;
    struct ns_common ns;
    struct mount * root;
    struct list_head list;
    struct user_namespace *user_ns;
    struct ucounts *ucounts;
    u64 seq; /* Sequence number to prevent loops */
    wait_queue_head_t poll;
    u64 event;
    unsigned int mounts; /* # of mounts in the namespace */
    unsigned int pending_mounts;
    };

以上三个数据结构在task_struct中的使用

1
2
3
4
5
6
7
8
/* Filesystem information: */
struct fs_struct *fs;
/* Open file information: */
struct files_struct *files;
/* Namespaces: */
struct nsproxy *nsproxy;


2.7 VFS所处理的系统调用

  • mount、umount:挂载/卸载文件系统
  • sysfs: 获取文件系统信息
  • statfs、fstatfs、ustat: 获取文件系统统计信息
  • chroot: 更改根目录
  • chdir、fchdir、getcwd:操作当前工作目录
  • mkdir、rmdir: 创建/删除目录
  • getdents、readdir、link、unlink、rename: 操作目录项
  • readlink、symlink: 对符号链接进行操作
  • chown、fchown、lchown: 更改文件所有者
  • chmod、fchmod、utime: 更改文件属性
  • open、close、create、write、read等

小结

VFS作为一个抽象层,记录了安装的文件系统类型,建立设备和文件系统的联系,实现面向文件的通用操作,涉及特定文件系统的操作时映射到具体文件系统中去。
目前Linux实现的文件系统可以在fs目录下查看。

参考资料