虚拟文件系统(VFS)是linux内核和具体I/O设备之间的封装的一层共通访问插口,通过这层插口linux虚拟机文件,linux内核可以以同一的形式访问各类I/O设备。
虚拟文件系统本身是linux内核的一部份,是纯软件的东西,并不须要任何硬件的支持。
1.虚拟文件系统的作用
虚拟文件系统(VFS)是linux内核和储存设备之间的具象层,主要有以下益处。
-简化了应用程序的开发:应用通过统一的系统调用访问各类储存介质
-简化了新文件系统加入内核的过程:新文件系统只要实现VFS的各个插口即可,不须要更改内核部份
2.虚拟文件系统的4个主要对象
虚拟文件中的4个主要对象,具体每位对象的涵义参见如下的详尽介绍。
2.1超级块
超级块(super_block)主要储存文件系统相关的信息,这是个针对文件系统级别的概念。
它通常储存在c盘的特定磁道中,而且对于这些基于显存的文件系统(例如proc,sysfs),超级块是在使用时创建在显存中的。
超级块的定义在:
/**超级块结构中定义的数组特别多,*这儿只介绍一些重要的属性*/structsuper_block{structlist_heads_list;/*指向所有超级块的数组*/conststructsuper_operations*s_op;/*超级块方式*/structdentry*s_root;/*目录挂载点*/structmutexs_lock;/*超级块讯号量*/ints_count;/*超级块引用计数*/structlist_heads_inodes;/*inode数组*/structmtd_info*s_mtd;/*储存c盘信息*/fmode_ts_mode;/*安装权限*/};/**其中的s_op中定义了超级块的操作方式*这儿只介绍一些相对重要的函数*/structsuper_operations{structinode*(*alloc_inode)(structsuper_block*sb);/*创建和初始化一个索引节点对象*/void(*destroy_inode)(structinode*);/*释放给定的索引节点*/void(*dirty_inode)(structinode*);/*VFS在索引节点被更改时会调用这个函数*/int(*write_inode)(structinode*,int);/*将索引节点写入c盘,wait表示写操作是否须要同步*/void(*drop_inode)(structinode*);/*最后一个指向索引节点的引用被删掉后,VFS会调用这个函数*/void(*delete_inode)(structinode*);/*从c盘上删掉指定的索引节点*/void(*put_super)(structsuper_block*);/*卸载文件系统时由VFS调用,拿来释放超级块*/void(*write_super)(structsuper_block*);/*用给定的超级块更新c盘上的超级块*/int(*sync_fs)(structsuper_block*sb,intwait);/*使文件系统中的数据与c盘上的数据同步*/int(*statfs)(structdentry*,structkstatfs*);/*VFS调用该函数获取文件系统状态*/int(*remount_fs)(structsuper_block*,int*,char*);/*指定新的安装选项重新安装文件系统时,VFS会调用该函数*/void(*clear_inode)(structinode*);/*VFS调用该函数释放索引节点,并清空包含相关数据的所有页面*/void(*umount_begin)(structsuper_block*);/*VFS调用该函数中断安装操作*/};
2.2索引节点
索引节点是VFS中的核心概念,它包含内核在操作文件或目录时须要的全部信息。
一个索引节点代表文件系统中的一个文件(这儿的文件除了是指我们平常所觉得的普通的文件,还包括目录,特殊设备文件等等)。
索引节点和超级块一样是实际储存在c盘上的,当被应用程序访问到时就会在显存中创建。
索引节点定义在:
/**索引节点结构中定义的数组特别多,*这儿只介绍一些重要的属性*/structinode{structhlist_nodei_hash;/*散列表,用于快速查找inode*/structlist_headi_list;/*索引节点数组*/structlist_headi_sb_list;/*超级块数组超级块*/structlist_headi_dentry;/*目录戒指表*/unsignedlongi_ino;/*节点号*/atomic_ti_count;/*引用计数*/unsignedinti_nlink;/*硬链接数*/uid_ti_uid;/*使用者id*/gid_ti_gid;/*使用组id*/structtimespeci_atime;/*最后访问时间*/structtimespeci_mtime;/*最后更改时间*/structtimespeci_ctime;/*最后改变时间*/conststructinode_operations*i_op;/*索引节点操作函数*/conststructfile_operations*i_fop;/*缺省的索引节点操作*/structsuper_block*i_sb;/*相关的超级块*/structaddress_space*i_mapping;/*相关的地址映射*/structaddress_spacei_data;/*设备地址映射*/unsignedinti_flags;/*文件系统标志*/void*i_private;/*fs私有表针*/};/**其中的i_op中定义了索引节点的操作方式*这儿只介绍一些相对重要的函数*/structinode_operations{/*为dentry对象创造一个新的索引节点*/int(*create)(structinode*,structdentry*,int,structnameidata*);/*在特定文件夹中找寻索引节点,该索引节点要对应于dentry中给出的文件名*/structdentry*(*lookup)(structinode*,structdentry*,structnameidata*);/*创建硬链接*/int(*link)(structdentry*,structinode*,structdentry*);/*从一个符号链接查找它指向的索引节点*/void*(*follow_link)(structdentry*,structnameidata*);/*在follow_link调用以后,该函数由VFS调用进行清理工作*/void(*put_link)(structdentry*,structnameidata*,void*);/*该函数由VFS调用,用于更改文件的大小*/void(*truncate)(structinode*);};
2.3目录项
和超级块和索引节点不同,目录项并不是实际存在于c盘上的。
在使用的时侯在显存中创建目录项对象linux防火墙设置,虽然通过索引节点早已可以定位到指定的文件,
然而索引节点对象的属性十分多,在查找,比较文件时,直接用索引节点效率不高,所以引入了目录项的概念。
路径中的每位部份都是一个目录项,例如路径:/mnt/cdrom/foo/bar其中包含5个目录项,/mntcdromfoobar
每位目录项对象都有3种状态:被使用,未使用和负状态
-被使用:对应一个有效的索引节点,但是该对象由一个或多个使用者
-未使用:对应一个有效的索引节点,并且VFS当前并没有使用这个目录项
-负状态:没有对应的有效索引节点(可能索引节点被删掉或则路径不存在了)
目录项的目的就是提升文件查找,比较的效率linux虚拟机文件,所以访问过的目录项就会缓存在slab中。
slab中缓存的名称通常就是dentry,可以通过如下命令查看:
[wangyubin@localhostkernel]$sudocat/proc/slabinfo|grepdentrydentry212545212625192211:tunables000:slabdata10125101250
目录项定义在:
/*目录项对象结构*/structdentry{atomic_td_count;/*使用计数*/unsignedintd_flags;/*目录项标示*/spinlock_td_lock;/*单目录项锁*/intd_mounted;/*是否登陆点的目录项*/structinode*d_inode;/*相关联的索引节点*/structhlist_noded_hash;/*散列表*/structdentry*d_parent;/*父目录的目录项对象*/structqstrd_name;/*目录项名称*/structlist_headd_lru;/*未使用的数组*//**d_childandd_rcucansharememory*/union{structlist_headd_child;/*childofparentlist*/structrcu_headd_rcu;}d_u;structlist_headd_subdirs;/*子目录数组*/structlist_headd_alias;/*索引节点别称数组*/unsignedlongd_time;/*重置时间*/conststructdentry_operations*d_op;/*目录项操作相关函数*/structsuper_block*d_sb;/*文件的超级块*/void*d_fsdata;/*文件系统特有数据*/unsignedchard_iname[DNAME_INLINE_LEN_MIN];/*短文件名*/};/*目录项相关操作函数*/structdentry_operations{/*该函数判定目录项对象是否有效。
VFS打算从dcache中使用一个目录项时会调用这个函数*/int(*d_revalidate)(structdentry*,structnameidata*);/*为目录项对象生成hash值*/int(*d_hash)(structdentry*,structqstr*);/*比较qstr类型的2个文件名*/int(*d_compare)(structdentry*,structqstr*,structqstr*);/*当目录项对象的d_count为0时,VFS调用这个函数*/int(*d_delete)(structdentry*);/*当目录项对象即将被释放时,VFS调用该函数*/void(*d_release)(structdentry*);/*当目录项对象遗失其索引节点时(也就是c盘索引节点被删掉了)百度网盘LINUX,VFS会调用该函数*/void(*d_iput)(structdentry*,structinode*);char*(*d_dname)(structdentry*,char*,int);};
2.4文件对象
文件对象表示进程已打开的文件,从用户角度来看,我们在代码中操作的就是一个文件对象。
文件对象反过来指向一个目录项对象(目录项反过来指向一个索引节点)
虽然只有目录项对象才表示一个已打开的实际文件,即使一个文件对应的文件对象不是惟一的,但其对应的索引节点和目录项对象却是惟一的。
文件对象的定义在:
/**文件对象结构中定义的数组特别多,*这儿只介绍一些重要的属性*/structfile{union{structlist_headfu_list;/*文件对象数组*/structrcu_headfu_rcuhead;/*释放以后的RCU数组*/}f_u;structpathf_path;/*包含的目录项*/conststructfile_operations*f_op;/*文件操作函数*/atomic_long_tf_count;/*文件对象引用计数*/};/**其中的f_op中定义了文件对象的操作方式*这儿只介绍一些相对重要的函数*/structfile_operations{/*用于更新偏斜量表针,由系统调用lleek()调用它*/loff_t(*llseek)(structfile*,loff_t,int);/*由系统调用read()调用它*/ssize_t(*read)(structfile*,char__user*,size_t,loff_t*);/*由系统调用write()调用它*/ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*);/*由系统调用aio_read()调用它*/ssize_t(*aio_read)(structkiocb*,conststructiovec*,unsignedlong,loff_t);/*由系统调用aio_write()调用它*/ssize_t(*aio_write)(structkiocb*,conststructiovec*,unsignedlong,loff_t);/*将给定文件映射到指定的地址空间上,由系统调用mmap调用它*/int(*mmap)(structfile*,structvm_area_struct*);/*创建一个新的文件对象,并将它和相应的索引节点对象关联上去*/int(*open)(structinode*,structfile*);/*当已打开文件的引用计数降低时,VFS调用该函数*/int(*flush)(structfile*,fl_owner_tid);};
2.5四个对象之间关系图
里面分别介绍了4种对象分别的属性和技巧,下边用图来展示这4个对象的和VFS之间关系以及4个对象之间的关系。
3.文件系统相关的数据结构
处理里面4个主要的对象之外,VFS中还有2个专门针对文件系统的2个对象,
-structfile_system_type:拿来描述文件系统的类型(例如ext3,ntfs等等)
-structvfsmount:描述一个安装文件系统的实例
file_system_type结构体坐落:
structfile_system_type{constchar*name;/*文件系统名称*/intfs_flags;/*文件系统类型标志*//*从c盘中读取超级块,但是在文件系统被安装时,在显存中组装超级块对象*/int(*get_sb)(structfile_system_type*,int,constchar*,void*,structvfsmount*);/*中止访问超级块*/void(*kill_sb)(structsuper_block*);structmodule*owner;/*文件系统模块*/structfile_system_type*next;/*数组中下一个文件系统类型*/structlist_headfs_supers;/*超级块对象数组*//*下边都是运行时的锁*/structlock_class_keys_lock_key;structlock_class_keys_umount_key;structlock_class_keyi_lock_key;structlock_class_keyi_mutex_key;structlock_class_keyi_mutex_dir_key;structlock_class_keyi_alloc_sem_key;};
每种文件系统,不管由多少个实例安装到系统中,还是根本没有安装到系统中,都只有一个file_system_type结构。
当文件系统被实际安装时,会在安装点创建一个vfsmount结构体。
结构体代表文件系统的实例,也就是文件系统被安装几次,都会创建几个vfsmount
vfsmount的定义参见:
structvfsmount{structlist_headmnt_hash;/*散列表*/structvfsmount*mnt_parent;/*父文件系统,也就是要挂载到那个文件系统*/structdentry*mnt_mountpoint;/*安装点的目录项*/structdentry*mnt_root;/*该文件系统的根目录项*/structsuper_block*mnt_sb;/*该文件系统的超级块*/structlist_headmnt_mounts;/*子文件系统数组*/structlist_headmnt_child;/*子文件系统数组*/intmnt_flags;/*安装标志*//*4bytesholeon64bitsarches*/constchar*mnt_devname;/*设备文件名e。g。/dev/dsk/hda1*/structlist_headmnt_list;/*描述符数组*/structlist_headmnt_expire;/*到期数组的入口*/structlist_headmnt_share;/*共享安装数组的入口*/structlist_headmnt_slave_list;/*从安装数组*/structlist_headmnt_slave;/*从安装数组的入口*/structvfsmount*mnt_master;/*从安装数组的主人*/structmnt_namespace*mnt_ns;/*相关的命名空间*/intmnt_id;/*安装标示符*/intmnt_group_id;/*组标示符*//**Weputmnt_count&mnt_expiry_markattheendofstructvfsmount*toletthesefrequentlymodifiedfieldsinaseparatecacheline*(sothatreadsofmnt_flagswontping-pongonSMPmachines)*/atomic_tmnt_count;/*使用计数*/intmnt_expiry_mark;/*假如标记为到期,则为True*/intmnt_pinned;/*"钉住"进程计数*/intmnt_ghosts;/*"镜像"引用计数*/#ifdefCONFIG_SMPint*mnt_writers;/*写者引用计数*/#elseintmnt_writers;/*写者引用计数*/#endif};
4.进程相关的数据结构
以上介绍的都是在内核角度见到的VFS各个结构,所以结构体中包含的属性十分多。
而从进程的角度来看的话,大多数时侯并不须要这么多的属性,所有VFS通过以下3个结构体和进程紧密联系在一起。
-structfiles_struct:由进程描述符中的files目录项指向,所有与单个进程相关的信息(例如打开的文件和文件描述符)都包含在其中。
-structfs_struct:由进程描述符中的fs域指向,包含文件系统和进程相关的信息。
-structmmt_namespace:由进程描述符中的mmt_namespace域指向。
structfiles_struct坐落:
structfiles_struct{atomic_tcount;/*使用计数*/structfdtable*fdt;/*指向其他fd表的表针*/structfdtablefdtab;/*基fd表*/spinlock_tfile_lock____cacheline_aligned_in_smp;/*单个文件的锁*/intnext_fd;/*缓存下一个可用的fd*/structembedded_fd_setclose_on_exec_init;/*exec()时关掉的文件描述符数组*/structembedded_fd_setopen_fds_init;/*打开的文件描述符数组*/structfile*fd_array[NR_OPEN_DEFAULT];/*缺省的文件对象字段*/};
structfs_struct坐落:
structfs_struct{intusers;/*用户数量*/rwlock_tlock;/*保护结构体的读写锁*/intumask;/*网段*/intin_exec;/*当前正在执行的文件*/structpathroot,pwd;/*根目录路径和当前工作目录路径*/};
structmmt_namespace坐落:
然而在2.6内核以后虽然没有这个结构体了,而是用structnsproxy来取代。
以下是structtask_struct结构体中关于文件系统的3个属性。
structtask_struct的定义坐落:
/*filesysteminformation*/structfs_struct*fs;/*openfileinformation*/structfiles_struct*files;/*namespaces*/structnsproxy*nsproxy;