Linux虚拟文件系统是一个内核软件层,用来处理与UNIX标准文件系统相关的所有系统调用。其健壮性表现在能为各种文件系统提供一个通用的接口。
Linux虚拟文件系统支持的文件系统可以划分为三种主要的类型:
磁盘文件系统
这些文件系统管理在本地磁盘分区中可用的磁盘空间或者其他可以起到磁盘作用的设备(比如说一个USB闪存)。
网络文件系统
这些文件系统允许访问属于其他网络计算机的文件系统所包含的文件。
特殊文件系统
这些文件系统不管理本地或者远程磁盘空间
通用文件模型
虚拟文件系统所隐含的主要思想是引入一个通用的文件模型,这个模型可以支持所有的文件系统类型。
可以把这里的通用文件模型看作是面型对象的。在这里,对象是一个软件结构,其中定义了数据结构也定义了其上的操作方法。处于效率上的考虑,Linux的编码并为采用面向对象的程序设计(比如C++编程)。因此对象作为普通的C数据结构来实现,数据结构中指向函数的字段就对应于对象的方法。
VFS与进程关系
进程描述符中与VFS相关的部分:
struct task_struct {
……
/* filesystem information */
struct fs_struct *fs;
/* open file information */
struct files_struct *files;
/* namespaces */
struct nsproxy *nsproxy;
……
};
通用文件系统模型数据结构组成
通用文件系统模型由下列对象组成:
超级块对象
存放已安装文件系统的有关信息。对基于磁盘的文件系统,这类对象通常对应于存放在磁盘上的文件系统控制块。
/*存放已安装文件系统的有关信息,通常对应于存放在磁盘上的文件系统控制块
每挂载一个文件系统对应一个超级块对象*/
struct super_block {
struct list_head s_list;/*指向超级块链表的指针*//* Keep this first */
dev_t s_dev;/*设备标识符*//* search index; _not_ kdev_t */
unsigned long s_blocksize;/*以字节为单位的块大小*/
unsigned char s_blocksize_bits;/*以位为单位的块大小*/
unsigned char s_dirt;/*修改标志*/
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;/*网络文件系统使用的输出方法*/
unsigned long s_flags;/*安装标识*/
unsigned long s_magic;/*文件系统的魔术*/
struct dentry *s_root;/*文件系统根目录的目录项对象*/
struct rw_semaphore s_umount;/*卸载用的信号量*/
struct mutexs_lock;/*超级块信号量*/
ints_count;/*引用计数*/
ints_need_sync;/*表示对超级快的索引节点进行同步标志*/
atomic_ts_active;/*次级引用计数*/
#ifdef CONFIG_SECURITY
void *s_security;/*指向超级安全数据结构的指针*/
#endif
struct xattr_handler **s_xattr;/*执行超级快扩展数据结构的指针*/
struct list_head s_inodes;/*所有索引节点的链表*/ /* all inodes */
struct hlist_head s_anon;/*用于处理网络文件系统的匿名目录项的链表*/ /*
anonymous dentries for (nfs) exporting */
struct list_head s_files;/*文件对象的链表*/
/* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */
struct list_head s_dentry_lru;/* /* unused dentry lru */
ints_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;/*磁盘限额的描述符*/ /* Diskquota specific options
*/
ints_frozen;/*冻结文件系统时使用的标志*/
wait_queue_head_t s_wait_unfrozen;/*进程挂起的等待队列,直到文件系统被冻结*/
char s_id[32];/*包含超级快的块设备名称*/ /* Informational name */
void *s_fs_info;/*指向特定文件系统的超级快信息的指针*/ /* Filesystem
private info */
fmode_ts_mode;/**/
/*
* 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;/*当VFS通过目录命名文件时使用的互斥变量*/ /*
Kludge */
/* Granularity of c/m/atime in ns.
Cannot be worse than a second */
u32s_time_gran;/*时间戳的粒度*/
/*
* 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 *s_options;
};
索引节点对象
存放关于具体文件的一些信息。对于磁盘文件系统,这类对象通常对应与存放在磁盘上的文件控制块。每个索引节点对象都对应与一个索引节点号,这个节点号唯一标识了文件系统上的文件。
/*索引节点,要访问一个文件时,一定要通过他的索引才能知道
这个文件是什么类型的文件、是怎么组织的、文件中存储这
多少数据、这些数据在什么地方以及其下层的驱动程序在哪儿等
必要的信息*/
struct inode {
struct hlist_node i_hash;/*用于散列链表的指针*/
struct list_head i_list;/*用于描述索引节点当前状态的链表指针*//* backing
dev IO list */
struct list_head i_sb_list;/*用于超级快的索引节点链表的指针*/
struct list_head i_dentry;/*引用索引节点的目录项对象链表头*/
unsigned long i_ino;/*索引节点号*/
atomic_ti_count;/*引用计数器*/
unsigned inti_nlink;/*硬链接数目*/
uid_t i_uid;/*所有者标识符*/
gid_t i_gid;/*主标识符*/
dev_t i_rdev;/*实设备标识符*/
u64i_version;/*版本号,每次使用后自动增加*/
loff_t i_size;/*文件的字节数*/
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;
#endif
struct timespec i_atime;/*上次访问文件的时间*/
struct timespec i_mtime;/*上次写文件的时间*/
struct timespec i_ctime;/*上次修改索引节点的时间*/
blkcnt_ti_blocks;/*文件的块数*/
unsigned inti_blkbits;/*块的位数*/
unsigned short i_bytes;/*文件最后一个块的字节数*/
umode_ti_mode;/**/
spinlock_t i_lock;/*保护索引节点一些字段的子璇锁*/ /* i_blocks, i_bytes,
maybe i_size */
struct mutexi_mutex;/**/
struct rw_semaphore i_alloc_sem;/*在直接IO
文件操作中避免出现竞争条件的读写信号量*/
const struct inode_operations *i_op;/*索引节点的操作*/
const struct file_operations *i_fop;/*缺省文件操作*/ /* former ->i_op->
default_file_ops */
struct super_block *i_sb;/*指向超级快对象的指针*/
struct file_lock *i_flock;/*指向文件锁链表的指针*/
struct address_space *i_mapping;/*指向address_space对象的指针?*/
struct address_space i_data;/*文件的address_space对象*/
#ifdef CONFIG_QUOTA
struct dquot*i_dquot[MAXQUOTAS];/*索引节点磁盘限额*/
#endif
struct list_head i_devices;/*用于具体的字符或块设备索引节点链表指针*/
union {
struct pipe_inode_info *i_pipe;/*如果文件是一个管道,则使用他8、*/
struct block_device *i_bdev;/*指向块设备驱动的指针*/
struct cdev *i_cdev;/*指向字符设备驱动的指针*/
};
__u32 i_generation;/*索引节点版本号*/
#ifdef CONFIG_FSNOTIFY
__u32 i_fsnotify_mask;/*目录通知事件的位掩码*//* all events this inode
cares about */
struct hlist_head i_fsnotify_mark_entries; /* fsnotify mark entries */
#endif
#ifdef CONFIG_INOTIFY
struct list_head inotify_watches; /* watches on this inode */
struct mutexinotify_mutex; /* protects the watches list */
#endif
unsigned long i_state;/*索引节点的状态标志*/
unsigned long dirtied_when;/*索引节点的弄脏标志*/ /* jiffies of first
dirtying */
unsigned inti_flags;/*文件系统的安装标志*/
atomic_ti_writecount;/*用于写进程的引用计数*/
#ifdef CONFIG_SECURITY
void *i_security;/*指向索引节点安全结构的指针*/
#endif
#ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *i_acl;/**/
struct posix_acl *i_default_acl;
#endif
void *i_private; /* fs or device private pointer */
};
文件对象
存放打开文件与进程之间进行交互的有关信息。这类信息仅当进程访问文件期间存放于内核内存中。
/*存放打开文件与进程之间进行交互的有关信息。这类信息仅当进程访问
文件期间存放在内核内存中。如文件的读取或写位置,以及
读写同步等操作*/
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;
spinlock_t f_lock; /* f_ep_links, f_flags, no IRQ */
atomic_long_t f_count;
unsigned intf_flags;
fmode_tf_mode;/*打开文件的模式*/
loff_t f_pos;/*文件的读写位置*/
struct fown_struct f_owner;
const struct cred *f_cred;
struct file_ra_state f_ra;
u64f_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;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;/*指向node节点的页面高速缓存*/
#ifdef CONFIG_DEBUG_WRITECOUNT
unsigned long f_mnt_write_state;
#endif
};
目录项对象
存放目录项(也就是文件的特定名称)与对应文件进行链接的有关信息。每个磁盘文件系统都以自己特有的方式将该类信息存放在磁盘上。
/*存放目录(也就是文件的名称)与对应文件进行链接的有关信息,
每个磁盘文件系统都以自己特有的方式将该类信息放在磁盘上*/
struct dentry {
atomic_t d_count;/*目录项引用对象*/
unsigned int d_flags;/*目录项高速缓存标志*/ /* protected by d_lock */
spinlock_t d_lock;/*保护目录项对象的自旋锁*/ /* per dentry lock */
int d_mounted;/*对于目录而言,用于记录安装该目录项的文件系统数的计数器*/
struct inode *d_inode;/*与文件名关联的索引节点*/ /* Where the name belongs to - NULL is
* negative */
/*
* The next three fields are touched by __d_lookup. Place them here
* so they all fit in a cache line.
*/
struct hlist_node d_hash;/*指向散列表项链表的指针*/ /* lookup hash list */
struct dentry *d_parent; /* parent directory */
struct qstr d_name;/*文件名,文件名较长时,需要动态分配空间,放在这里*/
struct list_head d_lru;/*用于未使用目录项链表的指针*//* LRU list */
/*
* d_child and d_rcu can share memory
*/
union {
struct list_head d_child;/*对于目录而言,用于统一父目录中的目录项链表的指针*/ /* child of parent list */
struct rcu_head d_rcu;
} d_u;
struct list_head d_subdirs;/*对目录,子目录项链表的指针*/ /* our children */
struct list_head d_alias;/*用于与同意索引节点相关的目录项链表的指针*/ /* inode alias list */
unsigned long d_time; /* used by d_revalidate */
const struct dentry_operations *d_op;
struct super_block *d_sb; /* The root of the dentry tree */
void *d_fsdata;/*依赖于文件系统的数据*/ /* fs-specific data */
unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /*存放短文件的空间,文件名长度小于36时放在这里*//* small names */
};
挂接点结构
每个已经挂装的文件系统用挂接点对象结构vfsmout描述,所有的结构vfsmount实例形成了一个链表,用全局变量vfsmntlist指向链表头,该链表可称为已挂接文件系统链表。Linux支持一个文件系统挂接多次,但他们仅有一个超级块,每个挂接点用挂接点结构vfsmount描述。
struct vfsmount {
struct list_head mnt_hash;
struct vfsmount *mnt_parent; /* fs we are mounted on */
struct dentry *mnt_mountpoint;/*安装点的目录,该目录属于安装到的文件系统*/ /* dentry of mountpoint */
struct dentry *mnt_root;/*安装的文件系统根目录*/ /* root of the mounted tree */
struct super_block *mnt_sb; /* pointer to superblock */
struct list_head mnt_mounts; /* list of children, anchored here */
struct list_head mnt_child; /* and going through their mnt_child */
int mnt_flags;
/* 4 bytes hole on 64bits arches */
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 vfsmount *mnt_master; /* slave is on master->mnt_slave_list */
struct mnt_namespace *mnt_ns; /* containing namespace */
int mnt_id;/* mount identifier */
int mnt_group_id; /* peer group identifier */
/*
* 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 if marked for expiry */
int mnt_pinned;
int mnt_ghosts;
#ifdef CONFIG_SMP
int *mnt_writers;
#else
int mnt_writers;
#endif
};
挂接点对象分类连接成不同的链表。
命名空间结构
每个进程可以拥有属于自己的已挂接文件系统树,称为命名空间。通常大多数进程共享一个文件系统命名空间,即系统的根文件系统。命名空间被子进程继承,但如果系统调用clone()用标示CLONE_NEWNS创建一个新进程时,那么新进程将获得一个新命名空间。
进程挂接或卸载文件系统时,仅修改它的命名空间,在同一命名空间的进程才可见这些修改,修改对其他命名空间没有影响。
struct mnt_namespace {
atomic_tcount;/*用这个命名空间的进程数*/
struct vfsmount * root;/*root目录的vfsmount*/
struct list_head list;/*mnt的链表开始点*/
wait_queue_head_t poll;
int event;
};
文件系统类型结构
Linux内核支持多种文件系统,各个文件系统可以作为内核模块或者作为内核一部分进行编译,linux内核使用文件系统类型结构file_system_type对各种文件系统进行跟踪。
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;
};
文件系统注册后,不同类型的文件系统存放在全局变量的链表file_systems中,同一类型的多个文件系统将超级块链表到结构file_system_type的成员fs_supers上,通过全局变量file_systems,可以找到文件系统类型结构,从该结构可以找到方法(*get_sb)()获取文件系统的超级块,也就可以访问该文件系统了。
VFS数据结构关系
系统支持的文件系统类型挂接在文件系统类型链表file_systems中,已挂接的文件系统放在挂接点对象链表vfsmntlist中,已挂接的不同类型的文件系统的超级块放在超级块链表super_block中。
虚拟文件系统除了为所有文件系统的实现提供一个通用的接口外,还具有另一个与系统性能相关的重要作用。最近最常使用的目录项对象被放在所谓的目录项高速缓存的磁盘高速缓存中,以加速从文件路径名到最后一个路径分量的索引节点的转换过程。
一般来说,磁盘高速缓存属于软件机制,他允许内核将原本存在磁盘上的某些信息保存在RAM中,以便对这些数据的进一步访问能快速进行。而不必慢速访问磁盘本身。
磁盘高速缓存不同于硬件高速缓存和内存高速缓存,后者都与磁盘和其他设备无关,硬件高速缓存是一个快速静态RAM,他加快了直接对慢速动态RAM的请求。内存高速缓存是一种软件机制,引入他是为了绕过内存分配器。
除了目录项高速缓存和索引节点高速缓存以外,Linux还使用其他磁盘高速缓存。其中最重要的一个是页面高速缓存。(后面会总结到)
VFS是应用程序和具体文件系统之间一层。不过,在某些情况下,一个文件操作可能由VFS本身执行,无需调用底层函数。