虚拟文件系统
虚拟文件系统所隐含的思想是把表示很多不同种类文件系统的共同信息放入内核;其中有一个字段或函数来支持Linux所支持的所有实际文件系统所提供的任何操作。对所调用的每个读、写或其他函数,内核都能把它们替换成支持本地Linux文件系统、NTFS文件系统,或者文件所在的任何其他文件系统的实际函数。
虚拟文件系统(VFS)的作用
虚拟文件系统(Virtual Filesystem)也可以称之为虚拟文件系统转换(Virtual Filesystem Switch,VFS),是一个内核软件层,用来处理与Unix标准文件系统相关的所有系统调用。其健壮性表现在能为各种文件系统提供一个通用的接口。
VFS是用户的应用程序与文件系统实现之间的抽象层。因此,cp程序并不需要知道/floppy/TEST和/tmp/test是什么文件系统类型。相反.cp程序直接与VFS交互,这是通过普通系统调用来进行的。
通用文件模型
VFS所隐含的主要思想在于引入了一个通用的文件模型(common file model),这个模型能够表示所有支持的文件系统。该模型严格反映传统Unix文件系统提供的文件模型。要实现每个具体的文件系统,必须将其物理组织结构转换为虚拟文件系统的通用文件模型。例如,在通用文件模型中,每个目录被看作一个文件,可以包含若干文件和其他的子目录。但是,存在几个非Unix的基于磁盘的文件系统,它们利用文件分配表(File AllocationTable,FAT)存放每个文件在目录树中的位置,在这些文件系统中,存放的是目录而不是文件。为了符合VFS的通用文件模型,对上述基于FAT的文件系统的实现,Linux必须在必要时能够快速建立对应于日录的文件。这样的文件只作为内核内存的对象而存在。
通用文件模型的组成:
超级块对象(superblock object)
存放已安装文件系统的有关信息。对基于磁盘的文件系统,这类对象通常对应于存放在磁盘上的文件系统控制块(filesystem control block)。
super_block的数据结构
struct super_block {
struct list_head s_list; /* 指向超级块链表的指针 */
dev_t s_dev; /* 设备标识符 */
unsigned char s_dirt;//修改(脏)标志
unsigned char s_blocksize_bits;//以位为单位的块大小
unsigned long s_blocksize;//以字节为单位的块大小
loff_t s_maxbytes; /* 文件的最长长度*/
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;//网络文件系统使用的输出操作
unsigned long s_flags;//安装标志
unsigned long s_magic;//文件系统的魔数
struct dentry *s_root;//文件系统根目录的目录项对象
struct rw_semaphore s_umount;//卸载所用的信号量
struct mutex s_lock;//超级块信号量
int s_count;//引用计数器
atomic_t s_active;//次级引用计数器
#ifdef CONFIG_SECURITY
void *s_security;//指向超级块安全数据结构的指针
#endif
const struct xattr_handler **s_xattr;//指向超级块扩展属性结构的指针
struct list_head s_inodes; /* 所有索引节点的链表 */
struct hlist_bl_head s_anon; /* 用于处理远程网络文件系统的匿名目录项的链表*/
#ifdef CONFIG_SMP
struct list_head __percpu *s_files;
#else
struct list_head s_files;//文件对象的链表
#endif
/* s_dentry_lru, s_nr_dentry_unused protected by dcache.c lru locks */
struct list_head s_dentry_lru; /* unused dentry lru */
int s_nr_dentry_unused; /* # of dentry on lru */
struct block_device *s_bdev;//指向块设备驱动程序描述符的指针
struct backing_dev_info *s_bdi;
struct mtd_info *s_mtd;
struct list_head s_instances;//用于给定文件系统类型的超级快对象链表的指针
struct quota_info s_dquot; /* 磁盘限额的描述符 */
int s_frozen;//冻结文件系统时使用的标志
wait_queue_head_t s_wait_unfrozen;//进程挂起的等待队列,直到文件系统被解冻
char s_id[32]; /* 包含超级快的块设备名称 */
u8 s_uuid[16]; /* UUID */
void *s_fs_info; /* 指向特定文件系统的超级块信息的
指针*/
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 */
};
所有超级块对象都以双向循环链表的形式链接在一起。链表中第一个元素用super_blocks变量来表示,而超级块对象的s_list字段存放指向链表相邻元素的指针。sb_lock自旋锁保护链表免受多处理器系统上的同时访问。s_fs_info字段指向属于具体文件系统的超级块信息。通常,为了效率起见,由s_fs_info字段所指向的数据被复制到内存。任何基于磁盘的文件系统都需要访问和更改自己的磁盘分配位图,以便分配或释放磁盘块。VFS允许这些文件系统直接对内存超级块的s_fs_info字段进行操作,而无需访问磁盘。但是,这种方法带来一个新问题:有可能VFS超级块最终不再与磁盘上相应的超级块同步。因此,有必要引入一个s_dirt标志来表示该超级块是否是脏的——那磁盘上的数据是否必须要更新。
超级块操作:
struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb);
//为索引节点对象分配空间,包括具体文件系统的数据所需要的空间。
void (*destroy_inode)(struct inode *);
//撤消索引节点对象,包括具体文件系统的数据。
void (*dirty_inode) (struct inode *);
//当索引节点标记为修改(脏)时调用。像ReiserFS和Ext3这样的文件系统用它来更新磁盘上的文件系统日志。
int (*write_inode) (struct inode *, struct writeback_control *wbc);
//用通过传递参数指定的索引节点对象的内容更新一个文件系统的索引节点。索引节点对象的i_ino字段标识所涉及磁盘上文件系统的索引节点。flag参数表示I/O操作是否应当同步。
int (*drop_inode) (struct inode *);
//在即将撤消索引节点时调用——也就是说,当最后一个用户释放该索引节点时,实现该方法的文件系统通常使用generic_drop_inode()函数。该函数从VFS数据结构中移走对索引节点的每一个引用,如果索引节点不再出现在任何目录中,则调用超级块方法delete_inode将它从文件系统中删除。
void (*evict_inode) (struct inode *);
void (*put_super) (struct super_block *);
//释放通过传递的参数指定的超级块对象(因为相应的文件系统被卸载)。
void (*write_super) (struct super_block *);
//用指定对象的内容更新文件系统的超级块。
int (*sync_fs)(struct super_block *sb, int wait);
//在清除文件系统来更新磁盘上的具体文件系统数据结构时调用(由日志文件系统使用)。
int (*freeze_fs) (struct super_block *);
int (*unfreeze_fs) (struct super_block *);
int (*statfs) (struct dentry *, struct kstatfs *)