Linux的虚拟文件系统—byGeYingjun1概述文件系统在现代操作系统中扮演至关重要的角色,一般认为文件系统提供了访问外部存储设备(如磁盘)的能力,从而使得计算机能处理的程序和数据突破了内存的限制。Linux支持几乎所有的文件系统格式,除此之外,还将内核内部使用的某些对象也组织成文件系统的形式进行管理或者与用户程序交互。为了达到上述功能,linux采用了一个特别的内核软件层,虚拟文件系统(VirtualFilesystem,VFS)。该软件层对上提供了标准的系统调用接口,用来接收对文件的各种标准系统调用(如open,read等),对下则通过多种数据结构来处理各种格式的文件系统操作,或者处理对内核对象的访问等。Linux采用微内核架构,系统启动阶段仅把内核必须的功能模块载入内存并运行,之后需要执行的各种动态加载模块、中间件、服务组件及应用程序等,都必须从文件系统中获取。因此,VFS对Linux的正常运行非常关键,而且VFS与内核的多个功能模块联系也更加紧密。根据实际操作的目标不同,可以将VFS支持的文件系统划分为三种主要类型:磁盘文件系统这类文件系统管理本地磁盘分区,或者可以作为磁盘设备的其他块设备(如USB闪存、NandFlash等)。这类文件系统包括Unix和Linux通常支持的Ext2、Ext3、SystemV类的文件系统;微软操作系统支持的MS-DOS、FAT、NTFS等文件系统;各类光盘文件系统以及为了适应嵌入式产品而开发的基于NandFlash/NorFlash的JFFS系列、YAFFS系列、UBIFS等。网络文件系统这类文件系统允许本机通过网络访问属于其他计算机的文件系统内的文件。常见的有:NFS、CIFS(微软Windows的通用网络文件系统)、NCP等。特殊文件系统这类文件系统包含前面所述的用于内核管理内部对象、与用户程序交互访问的多种文件系统。这些文件系统并不操作具体的磁盘或者网络设备,而仅是借用了VFS的结构,因此这些文件系统内的数据都是系统启动后动态建立的,系统关闭后这些数据并不会保存下来。常见的特殊文件系统有sysfs(管理设备/驱动),proc(管理进程信息)等。这三类文件系统几乎包括了内核所能访问到的所有对象(数据),因此VFS体现了linux的一个重要思想,即“一切皆文件”。紧扣这个主题,就能较容易理解VFS的设计思想。本文将首先介绍VFS的整体架构,叙述从文件访问的系统调用开始,如何最终找到具体的某个系统文件对应的操作方法;之后将会详细分析几个具有代表性的文件系统是如何实现具体操作,以及如何接收VFS的管理。2VFS模型及数据结构2.1通用文件模型VFS为了能够对各种文件系统进行统一的组织,使用了几个数据结构对“文件”进行描述,这里的“文件”既可以是磁盘上的文件,也可以是某个设备驱动。这些数据结构就构成了VFS中的通用文件模型,也就是说无论这个“文件”是什么,都会有这几个数据结构对其描述,并且通过这个模型将“文件”纳入VFS的管理体系。下面就详细介绍这几个重要的数据结构:2.1.1超级块对象(superblockobject)存放与文件系统相关的信息,如果是磁盘文件系统,这些信息有一部分存放在磁盘内;如果是特殊文件系统,则由内核进行设定。structsuper_block{structlist_heads_list;/*Keepthisfirst*/dev_ts_dev;/*searchindex;_not_kdev_t*/unsignedlongs_blocksize;unsignedchars_blocksize_bits;unsignedchars_dirt;unsignedlonglongs_maxbytes;/*Maxfilesize*/structfile_system_type*s_type;conststructsuper_operations*s_op;structdquot_operations*dq_op;structquotactl_ops*s_qcop;conststructexport_operations*s_export_op;unsignedlongs_flags;unsignedlongs_magic;structdentry*s_root;structrw_semaphores_umount;structmutexs_lock;ints_count;ints_need_sync_fs;atomic_ts_active;structxattr_handler**s_xattr;structlist_heads_inodes;/*allinodes*/structlist_heads_dirty;/*dirtyinodes*/structlist_heads_io;/*parkedforwriteback*/structlist_heads_more_io;/*parkedformorewriteback*/structhlist_heads_anon;/*anonymousdentriesfor(nfs)exporting*/structlist_heads_files;structlist_heads_dentry_lru;/*unuseddentrylru*/ints_nr_dentry_unused;/*#ofdentryonlru*/structblock_device*s_bdev;structmtd_info*s_mtd;structlist_heads_instances;structquota_infos_dquot;/*Diskquotaspecificoptions*/ints_frozen;wait_queue_head_ts_wait_unfrozen;chars_id[32];/*Informationalname*/void*s_fs_info;/*Filesystemprivateinfo*/fmode_ts_mode;structmutexs_vfs_rename_mutex;/*Kludge*/u32s_time_gran;char*s_subtype;char*s_options;};conststructsuper_operations*s_op:超级块包含的若干操作方法,其中有alloc_inode等操作inode的方法函数。structlist_heads_inodes:超级块相关的所有inode的链表。structblock_device*s_bdev:超级块相关联的块设备(磁盘设备)指针。structmtd_info*s_mtd:超级块相关联的mtd设备信息(用于Flash的MTD模型)。需要注意的是,超级块,inode等对象均是在首次使用时才会创建。比如一个磁盘上有很多文件,但是仅在首次访问到某个文件时才会创建对应的inode。2.1.2索引节点对象(inodeobject)存放关于文件的一般信息。如果是磁盘文件,某些信息需要从磁盘中读出;如果是特殊文件,则由内核进行填充。无论是什么样的文件,内核访问时都使用一个唯一的inode与之对应。由于linux内支持符号链接,可能有多个路径最终指向同一个文件实体,而这多个路径对应的inode是唯一的。structinode{structhlist_nodei_hash;structlist_headi_list;structlist_headi_sb_list;structlist_headi_dentry;unsignedlongi_ino;atomic_ti_count;unsignedinti_nlink;uid_ti_uid;gid_ti_gid;dev_ti_rdev;u64i_version;loff_ti_size;#ifdef__NEED_I_SIZE_ORDEREDseqcount_ti_size_seqcount;#endifstructtimespeci_atime;structtimespeci_mtime;structtimespeci_ctime;unsignedinti_blkbits;blkcnt_ti_blocks;unsignedshorti_bytes;umode_ti_mode;spinlock_ti_lock;/*i_blocks,i_bytes,maybei_size*/structmutexi_mutex;structrw_semaphorei_alloc_sem;conststructinode_operations*i_op;conststructfile_operations*i_fop;/*former-i_op-default_file_ops*/structsuper_block*i_sb;structfile_lock*i_flock;structaddress_space*i_mapping;structaddress_spacei_data;#ifdefCONFIG_QUOTAstructdquot*i_dquot[MAXQUOTAS];#endifstructlist_headi_devices;union{structpipe_inode_info*i_pipe;structblock_device*i_bdev;structcdev*i_cdev;};inti_cindex;__u32i_generation;#ifdefCONFIG_DNOTIFYunsignedlongi_dnotify_mask;/*Directorynotifyevents*/structdnotify_struct*i_dnotify;/*fordirectorynotifications*/#endif#ifdefCONFIG_INOTIFYstructlist_headinotify_watches;/*watchesonthisinode*/structmutexinotify_mutex;/*protectsthewatcheslist*/#endifunsignedlongi_state;unsignedlongdirtied_when;/*jiffiesoffirstdirtying*/unsignedinti_flags;atomic_ti_writecount;#ifdefCONFIG_SECURITYvoid*i_security;#endifvoid*i_private;/*fsordeviceprivatepointer*/};conststructinode_operations*i_op:与inode相关的操作,如create、mknod、mkdir等。conststructfile_operations*i_fop:文件操作,操作对象是inode对应的文件(file对象)structsuper_block*i_sb:指向所属的超级块,如磁盘超级块等。structaddress_space*i_mapping:用来管理文件映射到内存的页面,比如把内存中的修改写回文件、从文件中读入数据到页面缓冲等。union{structpipe_inode_info*i_pipe;structblock_device*i_bdev;structcdev*i_cdev;};这个联合体用于inode关联的文件是管道/块设备/字符设备时,指向设备结构体的指针。2.1.3目录项对象(dentryobject)存放描述文件/目录所在路径等信息。在VFS体系中,并不区分文件和目录。每个文件或者目录都被认为是一个“文件”,都建立inode与之对应,而每个inode也都由一个相应的dentry来描述文件/目录之间的层次关系。dentry对象完全是由内核创建并管理的,因此这种对象并不需要在磁盘上保存相关信息。内核通过dentry将文件系统及文件/目录组织成树型结构,这种结构也是VFS的根本逻辑结构。stru