虚拟文件系统是Linux内核的一个子系统,它就像一个适配器,将对各种类型的文件系统的操作转化成统一的接口提供给程序使用,而满足VFS要求的文件系统可以动态的挂载或移除。
A filesystem is a hierarchical storage of data adhering to a specific structure一个文件系统就是数据的层次、结构化存储
A file is an ordered string of bytes 文件就是有序的子节串
Each component of a path is called a directory entry 路径的每个组成都是一个目录项
文件的元数据(metadata)是描述文件数据的数据,它是独立存储的,使用inode数据结构表示
File、Directory Entry、inode这些信息和文件系统自己的控制信息整合在一起存在superblock中
VFS 对象和它们的数据结构
VFS有四个很重要的对象,它们构成了通用对象模型
- The superblock object, which represents a specific mounted filesystem.
- The inode object, which represents a specific file.
- The dentry object, which represents a directory entry, which is a single component of a path.
- The file object, which represents an open file as associated with a process.
这四个对象都包含各自的Operation对象,这个对象描述的就是各自的操作方法,供内核调用
- The super_operations object, which contains the methods that the kernel can invoke on a specific filesystem, such as write_inode() and sync_fs()
- The inode_operations object, which contains the methods that the kernel can invoke on a specific file, such as create() and link()
- The dentry_operations object, which contains the methods that the kernel can invoke on a specific directory entry, such as d_compare() and d_delete()
- The file_operations object, which contains the methods that a process can invoke on an open file, such as read() and write()
file_system_type 结构体表示的是注册的文件系统
vfsmount 结构体表示的是挂载点
两个在进程中使用的结构体,fs_struct表示和进程相关的文件系统,file表示和进程相关的文件
superblock object
基于磁盘的文件系统,一般在磁盘的某个特殊的扇区存储着文件系统控制块(filesystem control block)或文件系统超级块(filesystem superblock),当这个磁盘接入到VFS,就会用这个特殊扇区的信息填充Superblock对象,所以它储存的信息是对某个文件系统的描述,这也是Superblock对象名字的来历。
如果文件系统不是基于磁盘的,比如sysfs是基于虚拟内存的,需要动态的在内存中生成Superblock对象。
superblock operations
Superblock对象中最重要的一项就是super_operations对象,这在前面也提到过,对Superblock的相关操作都定义在这个对象,以下节选了部分方法,所有的方法都是由VFS在进程上下文调用
struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *);
void (*dirty_inode) (struct inode *);
int (*write_inode) (struct inode *, int);
void (*drop_inode) (struct inode *);
void (*delete_inode) (struct inode *);
inode object
对于Unix风格的文件系统,它们将文件数据和描述文件数据的元数据分开存储,后者在磁盘上的名字叫inode,和superblock类似,inode对象也是得名于此,也是用磁盘上的inode数据填充inode对象。
对于磁盘上不是使用inode存储文件元数据的、文件和文件元数据不是分离的、使用磁盘上数据库存储文件元数据的,这些文件系统都得找到合适的方法去填充inode对象。这个对象包含了内核操作文件或目录所需的所有信息。
inode代表文件系统中的一个文件,只有当这个文件被访问到时,才会在内存中构造inode对象,包括设备和管道这样特殊的文件。inode表示的文件类型只能是下面三个的其中一个
union {
struct pipe_inode_info *i_pipe;//管道
struct block_device *i_bdev;//阻塞设备
struct cdev *i_cdev;//字符设备
};
Inode Operations
和superblock operations概念类型,这个对象描述了对inode操作的方法,由VFS调用,文件系统实现。下面是部分方法
int (*mkdir) (struct inode *,struct dentry *,int);
int (*rmdir) (struct inode *,struct dentry *);
int (*mknod) (struct inode *,struct dentry *,int,dev_t);
int (*rename) (struct inode *, struct dentry *,
struct inode *, struct dentry *);
int (*readlink) (struct dentry *, char __user *,int);
Dentry Object
在VFS中,目录也是被看作文件的,目录下的子目录或文件就是此目录的数据,比如/mnt/shared/demo.c,mnt和shared是目录文件,demo.c是常规文件,都用inode对象表示。目录特有的操作中经常有路径名查询,需要对路径的每个组成部分验证有效性,并可以沿着路径访问下个组成部分。
VFC为了简便这个操作,引入了directory entry(缩写为dentry),它表示路径的组成部分,比如上个例子中的/、mnt、shared和demo.c都是Dentry对象。在进行目录操作时,VFS会根据路径的字符串表达按需动态构造Dentry对象。
和superblock对象、inode对象不一样,Dentry对象的并不对应磁盘上的某个数据结构,它是不需要写回到磁盘的。
Dentry State
Dentry对象有以下三种状态
- 使用中,关联了有效的inode对象,VFS在使用Dentry对象,不可以回收
- 未使用中,关联了有效的inode对象,但是VFS不在使用inode对象,目前当缓存对待,如果内存紧张时可以回收Dentry对象
- 负使用,没有关联有效inode对象或路径不对,当缓存对待,说不定未来的某个时刻变为有效,在内存够用的时候缓存下来可以减少路径查询的昂贵开销,是值得的。
Dentry Operations
略
File Object
File Object表示的是一个被进程打开的文件,每当进程打开一个文件,就会在内存中创建一个File Object来表示,关闭文件的时候这个对象又会被释放,多个进程可能同时打开一个文件,所以一个文件的多个File Object会共存,它仅仅表示进程看一个被打开的文件的视角。和Dentry Object一样,它也不对于磁盘上的某个数据结构,它是不需要写回到磁盘的。
File Operations
...
//当file是一个打开的设备节点 如android中的Binder 使用ioctl 这个方法有大内核锁BKL
int (*ioctl) (struct inode *, struct file *, unsigned int,
unsigned long);
//同时 区别是没有BKL,需要调用者自己同步
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
//让32位程序跑在64位系统上
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
...
和进程相关的三个数据结构
Each process on the system has its own list of open files, root filesystem, current working directory, mount points, and so on.Three data structures tie together the VFS layer and the processes on the system: 每个进程都有自己的打开文件列表,根文件系统、当前工作目录和挂载点等。有三个数据结构将VFS层和系统上的进程关联在一起:files_struct, fs_struct, and namespace.
files_struct
包含打开的文件和文件描述符,进程描述符中使用files entry指向这个数据结构
struct files_struct {
atomic_t count;
struct fdtable *fdt;
struct fdtable fdtab;
spinlock_t file_lock;
int next_fd;
struct embedded_fd_set close_on_exec_init; /* list of close-on-exec fds */ struct embedded_fd_set open_fds_init /* list of open fds */
//当前进程打开的File Object,NR_OPEN_DEFAULT一般为64,超过这个数就要使用fdt
struct file *fd_array[NR_OPEN_DEFAULT]; /* base files array */
};
fs_struct
fs_struct 描述的是文件系统信息,进程描述符中使用fs指向这个数据结构
struct fs_struct {
...
int in_exec;/* currently executing a file */
struct path root; /* root directory */
struct path pwd; /* current working directory */
};
namespace
namespace让每个进程对系统上挂载的文件系统都有一个独一无二的视角,进程描述符中使用mnt_namespace指向这个数据结构
struct mnt_namespace {
atomic_t count; /* usage count */
struct vfsmount *root; /* root directory */
struct list_head list; /* list of mount points */
wait_queue_head_t poll; /* polling waitqueue */
int event; /* event count */
大部分进程指向的files_struct 和 fs_struct都是独有的,但如果进程创建时带有 CLONE_FILES 或 CLONE_FS标记,就会和创建它的进程共享这些数据结构,mnt_namespace恰恰相反,绝大部分进程是共享这个数据结构的,也就是命名空间是一样的,除非在clone时指定CLONE_NEWNS标记,才会有独立的mnt_namespace。