虚拟文件系统,也不知道大家听过没有,反正我是听过了!我们知道在计算机行业,很多东西都不是一定有个官方说:朋友,我最大,你们做的东西,都要是这个样子,否则是非法的。事实上,很多东西都是靠的一种实力,通过实力来慢慢在人们心中成为既定事实。这个事实同样是没有官方的。好了,问题来了,没有官方,就没有标准,没有标准就没有统一,没有统一那就是三国时代,混战当道也!
怎么办?特别是百花争鸣的文件系统,这时linux的内核开发者们想到了VFS(虚拟文件系统)。VFS使得用户可以直接使用open(),read()和write()这样的系统调用而不用关注具体文件系统和实际物理介质。也许你感觉不是很新奇啊,告诉你新奇的事情:在老式操作系统上(比如DOS),任何对非本地文件系统的访问都必须依靠特殊工具才能完成。这种实现的方式是内核在它的底层文件系统接口上建立了一个抽象层。该抽象层是linux能够支持各种文件系统,即便是它们在功能和行为上存在很大差别。为了支持文件系统,VFS提供了一个通用文件系统模型,该模型囊括了我们所能想到的文件系统的常用功能和行为。这个VFS抽象层之所以能衔接各种各样的文件系统,是因为它定义了所有文件系统都支持的基本抽象接口和数据结构,同时实际系统也将自身的诸如“如何打开文件”,“目录是什么”等概念在形式上与VFS的定义保持一致。因为实际文件系统的代码在统一的接口和数据结构隐藏了具体的实现细节,所以在VFS层和内核的其他部分看来,所有文件系统都是相同的,它们都支持像文件和目录这样的概念,同时也支持像创建和删除文件这样的操作。
实际文件系统通过编程提供VFS所期望的抽象接口和数据结构,这样,内核就可以毫不费力地和任何文件系统协同工作。那么接下的问题,它们直接的关系如何呢,看下边的例子:
write(f,&buf,len);
该代码不用说,应该明白。这个用户调用首先被一个通用系统调用sys_write()处理,sys_write()函数要找到f所在的文件系统实际给出的是哪个写操作,然后再执行该操作。实际文件系统的写方法是文件系统实现的一部分,数据最终通过该操作写入介质。下图给出流程:
下面我就先从整体上对unix(linux)文件系统做个概述,然后在具体下去。Unix使用了四种和文件系统相关的传统抽象概念,如下:
1.文件:就是一个有序字节串。 2.目录项:文件是放在目录中,目录又可以层层嵌套,形成文件路径。路径中的每一项就叫做目录项。目录是文件,这个文件列出了该目录下的所有文件. 3.索引节点:一个文件其实是由两部分组成:相关信息和文件本身。这里的相关信息指的是访问控制权限,大小,拥有者,创建时间等。文件相关信息也叫 做元数据,被存储在一个单独的数据结构中,这个结构就叫做索引点(index node,简写inode)。 4.安装点(挂载点):文件系统被安装在一个特定的安装点上,该安装点在全局层次结构中被称为命名空间,所有的已安装文件系统都作为根文件树的树叶出 现在系统中。 5.超级块:是一种包含文件系统信息的数据结构,里边是文件系统的控制信息。 |
对应于上图,我们知道VFS是介于用户文件和文件系统之间的一个概念,所以如果文件系统想要穿透VFS供用户空间使用,就必须经过封装,提供一个符合这些概念的界面。上述每个元素都对应一个对象,该对象有属性结构体,描述了该对象的属性。有操作结构体,包含了自身所支持的操作,下面详细介绍:
1.超级块对象:代表一个已安装的文件系统。由数据结构super_block结构体表示,定义在linux/fs.h中。如下所示:
struct super_block { struct list_head s_list; /* list of all superblocks */ dev_t s_dev; /* identifier */ unsigned long s_blocksize; /* block size in bytes */ unsigned long s_old_blocksize; /* old block size in bytes */ unsigned char s_blocksize_bits; /* block size in bits */ unsigned char s_dirt; /* dirty flag */ unsigned long long s_maxbytes; /* max file size */ struct file_system_type s_type; /* filesystem type */ struct super_operations s_op; /* superblock methods */ struct dquot_operations *dq_op; /* quota methods */ struct quotactl_ops *s_qcop; /* quota control methods */ struct export_operations *s_export_op; /* export methods */ unsigned long s_flags; /* mount flags */ unsigned long s_magic; /* filesystem's magic number */ struct dentry *s_root; /* directory mount point */ struct rw_semaphore s_umount; /* unmount semaphore */ struct semaphore s_lock; /* superblock semaphore */ int s_count; /* superblock ref count */ int s_syncing; /* filesystem syncing flag */ int s_need_sync_fs; /* not-yet-synced flag */ atomic_t s_active; /* active reference count */ void *s_security; /* security module */ struct list_head s_dirty; /* list of dirty inodes */ struct list_head s_io; /* list of writebacks */ struct hlist_head s_anon; /* anonymous dentries */ struct list_head s_files; /* list of assigned files */ struct block_device *s_bdev; /* associated block device */ struct list_head s_instances; /* instances of this fs */ struct quota_info s_dquot; /* quota-specific options */ char s_id[32]; /* text name */ void *s_fs_info; /* filesystem-specific info */ struct semaphore s_vfs_rename_sem; /* rename semaphore */ };
创建,管理和销毁超级块对象的代码位于文件fs/super.c中,超级块对象通过alloc_super()函数创建并初始化。在文件系统安装时,内核会调用该函数以便从磁盘读取文件系统超级块,并且将其信息填充到内存中的超级块对象中。其中最重要的一个是s_op,指向超级块的操作函数表,由super_operations结构体表示,定义在linux/fs.h中,如下:
struct super_operations { struct inode *(*alloc_inode) (struct super_block *sb); void (*destroy_inode) (struct inode *); void (*read_inode) (struct inode *); void (*dirty_inode) (struct inode *); void (*write_inode) (struct inode *, int); void (*put_inode) (struct inode *); void (*drop_inode) (struct inode *); void (*delete_inode) (struct inode *); void (*put_super) (struct super_block *); void (*write_super) (struct super_block *); int (*sync_fs) (struct super_block *, int); void (*write_super_lockfs) (struct super_block *); void (*unlockfs) (struct super_block *); int (*statfs) (struct super_block *, struct statfs *); int (*remount_fs) (struct super_block *, int *, char *); void (*clear_inode) (struct inode *); void (*umount_begin) (struct super_block *); int (*show_options) (struct seq_file *, struct vfsmount *); };
当文件系统需要对其超级块执行操作时,首先要在超级块对象中寻找需要的操作方法。比如一个文件系统要写自己的超级块,需要调用:sb->s_op->write_super(sb)这里的sb是指向文件系统超级块的指针,沿着该指针进入超级块操作函数表,并从表中取得希望得到的write_super()函数,该函数执行写入超级块的实际操作。
2.索引节点对象:由inode结构体表示,定义在linux/fs.h中,如下:
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; };
有时,某些文件系统可能并不能完整的包含索引节点结构体要求的所有信息。举个例子,有的文件系统可能并不记录文件的创建时间,这时,该文件系统就可以在实现中选择任意合适的办法来解决这个问题,它可以在i_ctime中存储0,或者让i_ctime等于i_mtime,甚至任何其他值。索引节点对象中的inode_operations项存放了操作函数列表,定义在linux/fs.h中,如下:
struct inode_operations { int (*create) (struct inode *, struct dentry *,int); struct dentry * (*lookup) (struct inode *, struct dentry *); int (*link) (struct dentry *, struct inode *, struct dentry *); int (*unlink) (struct inode *, struct dentry *); int (*symlink) (struct inode *, struct dentry *, const char *); 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 *, int); int (*follow_link) (struct dentry *, struct nameidata *); int (*put_link) (struct dentry *, struct nameidata *); void (*truncate) (struct inode *); int (*permission) (struct inode *, int); int (*setattr) (struct dentry *, struct iattr *); int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *); int (*setxattr) (struct dentry *, const char *,const void *, size_t, int); ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); ssize_t (*listxattr) (struct dentry *, char *, size_t); int (*removexattr) (struct dentry *, const char *); };
同样,操作调用时,用以下方式:i->i_op->truncate(i).
由于版面原因,我不得不分两次说了,下次继续后面有关虚拟文件系统的剩余部分.