《linux虚拟文件系统.doc》由会员分享,可在线阅读,更多相关《linux虚拟文件系统.doc(60页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、Linux虚拟文件系统虚拟文件系统,简称VFS。系统中所有文件系统不但依赖VFS共存,而且也依靠VFS系统协同工作。程序可以利用标准的Unix系统调用对不同的文件系统,甚至不同介质上的文件系统进行读写操作。 1.通用文件系统接口 VFS使得用户可以直接使用open(),read()和write()这样的系统调用而无需考虑具体文件系统和实际物理介质,这点DOS是不具备的,DOS中任何对非本地文件系统的访问都要依靠特殊工具才能完成。 VFS与块I/O相结合,提供抽象、接口以及交融,使得用户空间的程序调用统一的系统调用访问各种文件,不管文件系统是什么,也不管文件系统位于何种介质,采用命名策略是统一的
2、。 2.文件系统抽象层 VFS可以为应用提供通用接口操作各类文件系统,是因为内核在底层文件系统接口上建立了一个抽象层。这个抽象系统模型,包括了任何文件系统的常用功能集和行为。当然,该模型偏重于Unix风格的文件系统。 内核通过抽象层能够方便、简单地支持各种类型的文件系统,实际文件系统通过编程提供VFS所期望的抽象接口和数据结构。比方用户空间的一个写操作: ret = write(fd, buf, len); write系统调用将buf指针指向的长度为len字节的数据写入文件描述符fd对应的 文件的当前位置。这个系统调用首先调用sys_write()处理,sys_write()函数要找到fd所在
3、的文件系统实际给出的是哪个写操作,然后再 执行该操作。具体文件系统的写方法是文件系统实现的一局部,数据最终写入介质。3.Unix文件系统 Unix使用了四种和文件系统相关的传统抽象概念:文件、目录项、索引节点和安装点(mount point)。 从本质上讲文件系统是特殊的数据分层存储结构,包含文件、目录和相关控制信息。 文件系统通用操作包含创立、删除和安装等。在Unix中,文件系统采用全局命名空间,挂载到系统根目录下的一个子节点。 Unix文件概念和面向记录的文件系统形比方OpenVMS的File-11成鲜明比照,面向记录的文件系统提供更丰富、更结构化的表示,而简单的面向字节流抽象的Unix文
4、件那么以简单性和相当的灵活性为代价。 Unix中,目录属于普通文件,它列出包含在其中的所有文件,由于VFS把目录当作文件对待,所以可以对目录执行和文件相同的操作。 Unix系统将文件的信息和文件本身这两个概念加以区分,例如访问控制权限、大小、拥有者、创立时间等。文件相关信息称作文件的元数据,被存储在一个单独的数据结构中,叫做索引节点inode。索引节点存储在超级块中。超级块是一种包含文件系统信息的数据结构。 Linux的VFS设计目标就是要保证能与支持或实现了这些概念的文件系统协同工作。 非Unixf风格的文件系统比方FAT或NTFS,要在Linux上工作,必须经过封装,提供一个符合这些概念的
5、界面。这个封装转换需要在使用现场引入一些特殊处理,这样文件系统仍然能工作,但其带来的开销是很大的。4.VFS对象及其数据结构 VFS其实采用的是面向对象的设计,内核是纯C开发,用结构体来实现对象,结构体包含数据和操作这些数据的函数指针。 VFS有四个主要对象类型,分别是: 超级块对象:代表一个具体已安装文件系统 索引节点对象:代表一个具体文件 目录项对象:代表一个目录项,是路径的一个组成局部 文件对象:它代表由进程翻开的文件 注意,这里的目录项代表的是路径的一个组成局部,可能包含一个普通文件。这不同于目录。Unix中,目录仅是一个普通文件。 每个主要对象中,都包含一个操作对象,这些操作对象描述
6、了内核针对主要对象可以使用的方法: super_operations对象,包含针对特定文件系统调用方法,比方write_inode(),sync_fs等 inode_operations对象,包含create(),link()等; dentry_operations对象,包含d_compare(),d_delete()等 file_operations对象,包含进程针对已翻开文件所能调用的方法,如read()和write()等 操作对象作为一个结构体指针来实现,此结构体中包含指向操作其父对象的函数指针。通常来说,可以继承使用VFS通用函数,如果通用函数提供的根本功能无法满足需要,那就必须使用实
7、际文件系统独有方法填充这些函数指针,使其指向文件系统实例。 VFS使用了大量结构体对象,还包括file_system_type描述文件系统及其性能,vfsmount包含安装点相关信息等。 5.超级块对象 各种文件系统都必须实现超级块对象,该对象用于存储特定文件系统信息,通常对应于存放在磁盘特定扇区中的文件系统超级块或者文件系统控制块(超级块对象)。对于非基于磁盘的文件系统比方基于内存的文件系统sysfs,它们会在使用现场创立超级块并将其保存到内存中。 超级块对象由super_block结构体表示,定义在中: struct super_block struct list_heads_list;/
8、* Keep this first */指向所有超级快的链表dev_ts_dev;/* search index; _not_ kdev_t */设备标识符unsigned chars_dirt; /修改(脏)标志unsigned chars_blocksize_bits; /块大小,单位bitunsigned longs_blocksize; /块大小,单位字节loff_ts_maxbytes;/* Max file size */文件大小上限struct file_system_type*s_type; /文件系统类型const struct super_operations*s_op;
9、/超级块方法const struct dquot_operations*dq_op; /磁盘限额方法const struct quotactl_ops*s_qcop; /限额控制方法const struct export_operations *s_export_op;/导出方法unsigned longs_flags; /挂载标志unsigned longs_magic; /文件系统幻数struct dentry*s_root; /目录挂载点struct rw_semaphores_umount; /卸载信号量struct mutexs_lock; /超级块信号量ints_count; /超
10、级块引用计数ints_need_sync; /尚未同步标志atomic_ts_active; /活动引用计数#ifdef CONFIG_SECURITYvoid *s_security; /活动模块#endifstruct xattr_handler*s_xattr; /扩展属性操作struct list_heads_inodes;/* all inodes */inodes链表struct hlist_heads_anon;/* anonymous dentries for (nfs) exporting */匿名目录项struct list_heads_files; /被分配文件链表/*
11、s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */struct list_heads_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_heads_inst
12、ances; /该类型文件系统struct quota_infos_dquot;/* Diskquota specific options */限额相关选项ints_frozen; /frozen标志位wait_queue_head_ts_wait_unfrozen; /冻结的等待队列char s_id32;/* Informational name */文本名字void *s_fs_info;/* Filesystem private info */文件系统特殊信息fmode_ts_mode; /安装权限/* Granularity of c/m/atime in ns. Cannot be
13、 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
14、be type.subtype */char *s_subtype;/子类型名称/* * Saved mount options for lazy filesystems using * generic_show_options() */char *s_options; /已存安装选项; 创立、管理和撤销超级块对象的代码位于文件fs/super.c中,超级块对象通过alloc_super()函数创立并初始化。在文件系统安装时,文件系统会调用该函数以便从磁盘读取文件系统超级块,并且将其信息填充到内存中的超级块对象中。 6.超级块操作 超级块对象中最重要的一个域是s_op, 它指向超级块的操作函数
15、表,操作函数表在定义 该结构体每一项都是一个指向超级块操作函数的指针,超级块操作函数执行文件系统和索引节点的底层操作。 当文件系统需要对其超级块执行操作时,首先要在超级块对象中找到适宜的方法,比方一个文件系要写自己的超级块,需要调用 sb-s_op-write_super(sb); 由于C语言无法直接得到操作函数的父对象,所以必须将父对象以参数显示传给操作函数。 struct super_operations /在给定的超级块下创立和初始化一个新的索引节点对象struct inode *(*alloc_inode)(struct super_block *sb);/释放给定的索引节点void
16、(*destroy_inode)(struct inode *);/VFS在索引节点脏(被修改)时会调用此函数/日志文件系统,(比方ext3/etx4)执行该函数进行日志更新void (*dirty_inode) (struct inode *);/用于将给定的索引节点写入磁盘,wait参数指明写操作是否需同步int (*write_inode) (struct inode *, struct writeback_control *wbc);/在最后一个指向索引节点的引用被释放后,VFS会调用该函数,VFS只需要简单地删除这个索引节点后,Unix文件系统就不会定义这个函数了void (*dro
17、p_inode) (struct inode *);void (*delete_inode) (struct inode *);/从磁盘删除给定的索引节点void (*put_super) (struct super_block *);/卸载文件系统时被VFS调用,用来释放超级块,调用者必须持有s_lock锁/用给定的超级块更新磁盘上的超级块,VFS通过该函数对内存中的超级块和磁盘中的超级块进行同步,调用者必须持有s_lock锁void (*write_super) (struct super_block *);/使文件系统的数据元与磁盘上的文件系统同步,wait参数指定操作是否同步int (
18、*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 *);/获取文件系统状态/当执行新的安装选项重新安装文件系统时,VFS会调用,必须持有s_lock锁int (*remount_fs) (struct super_block *, int *, char *);/VFS调用,释放索引节点,并清空包含相关
19、数据所有页面void (*clear_inode) (struct inode *);/VFS调用中断安装操作,该函数被网络文件系统使用,如NFS。void (*umount_begin) (struct super_block *);int (*show_options)(struct seq_file *, struct vfsmount *);int (*show_stats)(struct seq_file *, struct vfsmount *);#ifdef CONFIG_QUOTAssize_t (*quota_read)(struct super_block *, int,
20、char *, size_t, loff_t);ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);#endifint (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t); 以上函数都是由VFS在进程上下文调用,除了dirty_inode(),其他函数可以阻塞。 其中一些函数是可选的,不需要的函数设为NULL,如果VFS发现操作函数指针是NULL,它调用通用函数指向相应操作,或者什么都不做。 7.索引节点对
21、象 索引节点对象,包含了内核在操作文件或目录时需要的全部信息。 对于Unix风格的文件系统来说,这些信息可以从磁盘索引节点直接读入。有的文件系统没有索引节点,没有将数据和控制信息分开存放。不管哪种情况、采用哪种方式,索引节点对象必须在内存中创立,以便于文件系统调用。 索引节点对象inode结构,在中定义 struct inode struct hlist_nodei_hash; /散列表/* backing dev IO list */struct list_headi_list; /索引节点链表struct list_headi_sb_list; /超级块链表struct list_head
22、i_dentry; /目录项链表unsigned longi_ino; /节点号atomic_ti_count; /引用计数unsigned inti_nlink; /硬链接数uid_ti_uid; /使用者idgid_ti_gid; /使用组iddev_ti_rdev; /实际设备标志符unsigned inti_blkbits; /以位单位文件大小u64i_version; /版本号loff_ti_size; /以字节单位文件大小#ifdef _NEED_I_SIZE_ORDEREDseqcount_ti_size_seqcount; /以i_size进行串行计数#endifstruct
23、timespeci_atime; /最后访问时间struct timespeci_mtime; /最后修改时间struct timespeci_ctime; /最后改变时间blkcnt_ti_blocks; /文件的块数unsigned short i_bytes; /使用的字节数umode_ti_mode; /访问权限/* i_blocks, i_bytes, maybe i_size */spinlock_ti_lock; /自旋锁struct mutexi_mutex; /互斥锁struct rw_semaphorei_alloc_sem; /嵌入i_sem内部const struct
24、inode_operations*i_op;/索引节点操作表/* former -i_op-default_file_ops */const struct file_operations*i_fop;/缺省的索引节点操作表struct super_block*i_sb; /相关的超级块struct file_lock*i_flock;struct address_space*i_mapping;/相关的地址映射struct address_spacei_data; /设备地址映射#ifdef CONFIG_QUOTAstruct dquot*i_dquotMAXQUOTAS;#endifstr
25、uct list_headi_devices;union struct pipe_inode_info*i_pipe; /管道信息struct block_device*i_bdev; /块设备驱动struct cdev*i_cdev; /字符设备驱动;_u32i_generation;#ifdef CONFIG_FSNOTIFY_u32i_fsnotify_mask; /* all events this inode cares about */struct hlist_headi_fsnotify_mark_entries; /* fsnotify mark entries */#endi
26、f#ifdef CONFIG_INOTIFYstruct list_headinotify_watches; /* watches on this inode */struct mutexinotify_mutex;/* protects the watches list */#endifunsigned longi_state;unsigned longdirtied_when;/* jiffies of first dirtying */unsigned inti_flags; /文件系统标志atomic_ti_writecount; /写者技数#ifdef CONFIG_SECURITY
27、void*i_security; /平安模块#endif#ifdef CONFIG_FS_POSIX_ACLstruct posix_acl*i_acl;struct posix_acl*i_default_acl;#endif/* fs or device private pointer */void*i_private;/fs私有指针; 一个索引节点代表文件系统中的一个文件索引节点仅当文件被访问时,才在内存中创立,也可以是设备文件或管道文件这样的特殊文件。 8.索引节点操作 索引节点对象的inode_operations项非常重要,因为它描述了操作索引节点的所有方法,这些方法由文件系统实现
28、。在定义 struct inode_operations /VFS通过系统调用create()和open()来调用该函数,从而为dentry对象创立/一个新的索引节点int (*create) (struct inode *,struct dentry *,int, struct nameidata *);/在特定的目录中寻找索引节点,该索引节点要对应于denrty中给定的文件名struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);/被系统调用link()调用,用来创立硬链接/硬链接名称由d
29、entry参数指定,链接对象是dir目录中old_dentry目录项所代表的文件int (*link) (struct dentry *,struct inode *,struct dentry *);/被系统调用ulink()调用,从目录dir中删除由目录项dentry指定的索引节点对象int (*unlink) (struct inode *,struct dentry *);/被symlik()调用,创立符号链接int (*symlink) (struct inode *,struct dentry *,const char *);/被mknod()调用,创立新目录int (*mkdir
30、) (struct inode *,struct dentry *,int);/被rmdir()调用,删除dir目录中的dentry目录项代表的文件int (*rmdir) (struct inode *,struct dentry *);/被mknod()调用,船舰特殊文件(设备文件,管道,套接字等)int (*mknod) (struct inode *,struct dentry *,int,dev_t);/移动文件int (*rename) (struct inode *, struct dentry *,struct inode *, struct dentry *);/readli
31、nk()调用,拷贝数据到特定的缓冲buffer中?/拷贝的数据来自dentry指定的符号链接,拷贝大小最大可达buflen字节int (*readlink) (struct dentry *, char _user *,int);void * (*follow_link) (struct dentry *, struct nameidata *);void (*put_link) (struct dentry *, struct nameidata *, void *);void (*truncate) (struct inode *);/修改文件大小int (*permission) (st
32、ruct inode *, int);/检查访问权限int (*check_acl)(struct inode *, int);/被notify_change()调用,在修改索引节点后,通知发送了改变事件int (*setattr) (struct dentry *, struct iattr *);/在通知索引节点需要从磁盘中更新时,VFS会调用int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);/VFS调用,给dentry指定的文件设置扩展属性int (*setxattr) (struct dentr
33、y *, const char *,const void *,size_t,int);/VFS调用,向value中拷贝给定文件的扩展属性name对应的数值ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);/将特定文件的所有属性列表拷贝到一个缓冲列表中ssize_t (*listxattr) (struct dentry *, char *, size_t);/从文件中删除指定的属性int (*removexattr) (struct dentry *, const char *);void (*truncat
34、e_range)(struct inode *, loff_t, loff_t);long (*fallocate)(struct inode *inode, int mode, loff_t offset, loff_t len);int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len); 9.目录项对象 VFS把目录当作文件对待,一个完整路径中的每个组成局部都由一个索引节点对象标志。虽然可以用索引节点表示,但为了实际操作方便,引入目录项对象。完整路径的每一局部都是一个目录项对象,比方/bin
35、/vi中/,bin,vi是三个目录项对象。 VFS在执行目录操作时(需要的话)会现场创立目录项对象。 目录项对象dentry结构体在定义 struct dentry atomic_t d_count; /使用计数/* protected by d_lock */ unsigned int d_flags; /目录项标识/* per dentry lock */spinlock_t d_lock; /但目录项锁int d_mounted;/* Where the name belongs to - NULL is * negative */struct inode *d_inode; /相关联的
36、索引节点/* * 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 s
37、hare memory */union /* child of parent list */struct list_head d_child;/目录项内部形成的链表 struct rcu_head d_rcu; /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_operati
38、ons *d_op;/目录项操作时间struct super_block *d_sb;/* The root of the dentry tree */文件的超级块void *d_fsdata;/* fs-specific data */文件系统特有数据unsigned char d_inameDNAME_INLINE_LEN_MIN;/* small names */; 与超级块和索引节点不同,目录项对象没有对应的磁盘数据结构,VFS根据字符串形式的路径名现场创立它,而且由于目录项对象并非真正保存在磁盘上,所以目录项结构体没有是否被修改的标志。 9.1 目录项状态 目录项对象有三种有效状态:
39、被使用、未被使用和负状态。 (1)一个被使用的目录项对应一个有效的索引节点(d_inode指针),并且说明该对象存在一个或多个使用者(d_count为正值),一个目录项处于被使用状态,意味着它正被VFS使用并且指向有效数据,因此不能丢弃。 (2)一个未被使用的目录项对应一个有效的索引节点(d_inode指向索引节点),但VFS当前并未使用(d_count为0),该目录项对象仍然指向一个有效对象,而且被保存在缓存中以便需要时使用。这样当需要它时,不必重新创立,这样路径查找更快。 (3)负状态的目录项没有对应的有效索引节点,节点以及被删除或路径已经不正确了。但目录项仍然保存,以便快速解析以后的路径
40、查询。 目录项对象释放后也可以保存到slab对象缓存中。 9.2 目录项缓存如果VFS层遍历路径名中的所有元素并将它们逐个解析成目录项对象,还要到达最深层目录,是一件非常费力的工作,会浪费大量时间。所以内核将目录项对象缓存在目录项缓存中 dcache主要包括三个局部: (1)“被使用的目录项链表,i_dentry项链接相关索引节点。因为一个索引节点可能有多个链接,所以就可能有多个目录项对象,因此用链表链接他们。 (2)“最近被使用的双向链表。该链表含有未被使用的或负状态的目录项对象。 由于该链表总是在头部插入目录项,所以头节点目录项是最新的,内核通过删除节点回收内存时,会从链尾删除。 (3)散
41、列表和相应的散列函数,用来快速地将给定路径解析为相关目录项对象。 散列表由数组dentry_hashtable表示,其中每一个元素都是一个指向相同键值的目录项对象链表的指针。 实际的散列值由d_hash()函数计算,它是内核提供应文件系统的唯一的散列函数。 dcache在一定意义上也提供了对索引节点的缓存,即icache。和目录项对象相关的索引节点不会被释放。因为目录项让相关索引节点的使用计数为正,这样确保相应索引节点保存在内存中。所以,只要路径名在缓存中找到,那么相应的索引节点肯定也在内存中缓存着。 因为文件访问呈现空间和时间的局部性,所以对目录项和索引节点进程缓存非常有益。 时间的局部性,是指程序可能会一次又一次地访问相同的文件,当同一个文件被访问时,所缓存的相关目录项和索引节点命中率高。 空间局部性,是指程序可能在同一个目录下访问多个文件,因此一个文件对应的目录项缓存后命中率高,因为相关的文件可能在下次又被使用。 10.目录项操作 dentry_operation结构体指向VFS操作目录项的所有方法,在定义 struct dentry_operations int (*d