虚拟文件系统

现在我们的主流价值观是社会和谐、世界和谐。同样,Linux成功的关键因素之一是它具有与其他操作系统和谐共存的能力。你能够透明地安装具有其他操作系统文件格式的磁盘或分区,这些操作系统如Windows、其他版本的Unix,甚至像Amiga那样的市场占有率很低的系统。通过所谓的虚拟文件系统概念,Linux使用与其他Unix变体相同的方式设法支持多种文件系统类型。

虚拟文件系统所隐含的思想是把表示很多不同种类文件系统的共同信息放入内核;其中有一个字段或函数来支持Linux所支持的所有实际文件系统所提供的任何操作。对所调用的每个读、写或其他函数,内核都能把它们替换成支持本地Linux文件系统、NTFS文件系统,或者文件所在的任何其他文件系统的实际函数。

1 虚拟文件系统概述


虚拟文件系统(Virtual Filesystem)也可以称之为虚拟文件系统转换(Virtual Filesystem Switch,VFS),是一个内核软件层,用来处理与Unix标准文件系统相关的所有系统调用。其健壮性表现在能为各种文件系统提供一个通用的接口。

例如,假设一个用户输入以下shell命令:
$ cp /floppy/TEST /tmp/test

其中/floppy是MS-DOS磁盘的一个安装点,而/tmp是一个标准的第二扩展文件系统(second Extended Filesystom, Ext2)的目录。正如图(a)所示,VFS是用户的应用程序与文件系统实现之间的抽象层。因此,cp程序并不需要知道/floppy/TEST 和 /tmp/test是什么文件系统类型。相反,cp程序直接与VFS交互,这是通过Unix程序设计人员都熟悉的普通系统调用来进行的。cp的执行代码如图(b)所示:


VFS支持的文件系统可以划分为三种主要类型:

磁盘文件系统

这些文件系统管理在本地磁盘分区中可用的存储空间或者其他可以起到磁盘作用的设备(比如说一个USB闪存)。VFS支持的基于磁盘的某些著名文件系统还有:

- Linux使用的文件系统,如广泛使用的第二扩展文件系统(Ext2),新近的第三扩展文件系统(Third Extended Filesystem,Ext3)及Reiser文件系统(ReiserFS)

- Unix家族的文件系统,如sysv文件系统(System V、Coherent、Xenix)、UFS(BSD、Solaris、NEXTSTEP),MINIX文件系统及VERITAS VxFS(SCO UnixWare)。

- 微软公司的文件系统,如MS-DOS、VFAT(Windows 95及随后的版本)及NTFS(Windows NT以及随后的版本)。

- IS09660 CD-ROM文件系统(以前的High Sierra文件系统)和通用磁盘格式(UDF)的DVD文件系统。

- 其他有专利权的文件系统,如HPFS(IBM公司的OS/2)、HFS(苹果公司的Macintosh)、AFFS(Amiga公司的快速文件系统)以及ADFS(Acorn 公司的磁盘文件归档系统)。

- 起源于非Linux系统的其他日志文件系统,如IBM的JFS和SGI的XFS。

网络文件系统


这些文件系统允许轻易地访问属于其他网络计算机的文件系统所包含的文件。虚拟文件系统所支持的一些著名的网络文件系统有:NFS、Coda、AFS(Andrew文件系统)、CIFS(用于Microsoft Windows的通用网络文件系统)以及NCP(Novell公司的NetWare Core Protocol)。

特殊文件系统

这些文件系统不管理本地或者远程磁盘空间。/proc、/sys、/dev等文件系统是特殊文件系统的一个典型范例。

Unix的目录建立了一棵根目录为“/”的树。根目录包含在根文件系统(root filesystem)中,在Linux中这个根文件系统通常就是Ext2或Ext3类型。其他所有的文件系统都可以被“安装”在根文件系统的子目中。当一个文件系统被安装在某一个目录上时,在父文件系统中的目录内容不再是可访问的了,因为任何路径(包括安装点),都将引用已安装的文件系统。但是,当被安装文件系统卸载时,原目录的内容又可再现。

所以,Unix文件系统的一个重要特点就是可以由系统管理员用来隐藏文件,他们只需把一个文件系统安装在要隐藏文件的目录中即可。

基于磁盘的文件系统通常存放在块设备中,如硬盘、软盘或者CD-ROM。Linux VFS的一个有用特点是能够处理如/dev/loop0这样的虚拟块设备,这种设备可以用来安装普通文件所在的文件系统。作为一种可能的应用,用户可以保护自己的私有文件系统,因为可以通过把自己文件系统的加密版本存放在一个普通文件中来实现。

第一个虚拟文件系统包含在1986年由Sun公司发布的SunOS操作系统中。从那时起,多数UNIX文件系统都包含VFS。然而,Linux的VFS支持最广泛的文件系统。

2 通用文件模型


VFS所隐含的主要思想在于引入了一个通用的文件模型(common file model),这个模型能够表示所有支持的文件系统。该模型严格反映传统Unix文件系统提供的文件模型。这并不奇怪,因为Linux希望以最小的额外开销运行它的本地文件系统。不过,要实现每个具体的文件系统,必须将其物理组织结构转换为虚拟文件系统的通用文件模型。

例如在通用文件模型中,每个目录被看作一个文件,可以包含若干文件和其他的子目录。但是,存在几个非Unix的基于磁盘的文件系统,它们利用文件分配表(File Allocation Table,FAT)存放每个文件在目录树中的位置,在这些文件系统中,存放的是目录而不是文件。为了符合VFS的通用文件模型,对上述基于FAT的文件系统的实现,Linux必须在必要时能够快速建立对应于目录的文件。这样的文件只作为内核内存的对象而存在。

从本质上说,Linux内核不能对一个特定的函数进行硬编码来执行诸如read()或ioctl()这样的操作,而是对每个操作都必须使用一个指针,指向要访问的具体文件系统的适当函数。

为了进一步说明这一概念,参见前面的那个图,其中显示了内核如何把read()转换为专对MS-DOS文件系统的一个调用。应用程序对read()的调用引起内核调用相应的sys_read()服务例程,这与其他系统调用完全类似。我们在本章后面会看到,文件在内核内存中是由一个file数据结构来表示的。这种数据结构中包含一个称为f_op的字段,该字段中包含一个指向专对MS-DOS文件的函数指针,当然还包括读文件的函数。

sys_read()查找到指向该函数的指针,并调用它。这样一来,应用程序的read()就被转化为相对间接的调用:
file->f_op->read(…);

与之类似,write()操作也会引发一个与输出文件相关的Ext2写函数的执行。简而言之,内核负责把一组合适的指针分配给与每个打开文件相关的file变量,然后负责调用针对每个具体文件系统的函数(由f_op字段指向)。

你可以把通用文件模型看作是面向对象的,在这里,对象是一个软件结构,其中既定义了数据结构也定义了其上的操作方法。出于效率的考虑,Linux的编码并未采用面向对像的程序设计语言(比如C++)。因此对象作为普通的C数据结构来实现,数据结构中指向函数的字段就对应于对象的方法。

通用文件模型由下列对象类型组成:

超级块对象(superblock object):存放已安装文件系统的有关信息。对基于磁盘的文件系统,这类对象通常对应于存放在磁盘上的文件系统控制块(filesystem control block)。

索引节点对象(inode object):存放关于具体文件的一般信息。对基于磁盘的文件系统,这类对象通常对应于在磁盘上的文件控制块(file control block)。每个索引节点对象都有一个索引节点号,这个节点号唯一地标识文件系统中的文件。

文件对象(file object):存放打开文件与进程之间进行交互的有关信息。这类信息仅当进程访问文件期间在于内核内存中。

目录项对象(dentry object):存放目录项(也就是文件的特定名称)与对应文件进行链接的有关信息。每个磁盘文件系统都以自己特有的方式将该类信息存在磁盘上。

下图所示是一个简单的示例,说明进程怎样与文件进行交互。


三个不同进程已经打开同一个文件,其中两个进程使用同一个硬链接。在这种情况下,其中的每个进程都使用自己的文件对象,但只需要两个目录项对象,每个硬链接对应一个目录项对象。这两个目录项对象指向同一个索引节点对象,该索引节点对象标识超级块对象,以及随后的普通磁盘文件。

VFS除了能为所有文件系统的实现提供一个通用接口外,还具有另一个与系统性能相关的重要作用,那就是一些文件相关数据结构的磁盘高速缓存。例如最近最常使用的目录项对象被放在所谓目录项高速缓存(dentry cache)的磁盘高速缓存中,从而加速从文件路径名到最后一个路径分量的索引节点的转换过程。

一般说来,磁盘高速缓存(disk cache)属于软件机制,它允许内核将原本存在磁盘上的某些信息保存在RAM中,以便对这些数据的进一步访问能快速进行,而不必慢速访问磁盘本身。

注意,磁盘高速缓存不同于硬件高速缓存或内存高速缓存,后两者都与磁盘或其他设备无关。硬件高速缓存是一个快速静态RAM,它加快了直接对内存,这样的慢速动态RAM的请求。内存高速缓存是一种软件机制,引入它是为了绕过内核内存分配器(slab分配器)。

除了目录项高速缓存和索引结点高速缓存之外,Linux还使用其他磁盘高速缓存。其中最重要的一种就是所谓的页高速缓存,我们将在本专题中中进行详细介绍。

3 VFS所处理的系统调用


下表列出了VFS的系统调用,这些系统调用涉及文件系统、普通文件、目录文件以及符号链接文件:

系统调用名

说明

mount( ) umount( ) umount2( )

安装/卸载文件系统

sysfs( )

获取文件系统信息

statfs( ) fstatfs( ) statfs64( ) fstatfs64( )

ustat( )

获取文件系统统计信息

chroot( ) pivot_root( )

更改根目录

chdir( ) fchdir( ) getcwd( )

对当前目录进行操作

mkdir( ) rmdir( )

创建/删除目录

getdents( ) getdents64( ) readdir( ) link( )

unlink( ) rename( ) lookup_dcookie( )

对目录项进行操作

readlink( ) symlink( )

对软链接进行操作

chown( ) fchown( ) lchown( ) chown16( )

fchown16( ) lchown16( )

更改文件所有者性

chmod( ) fchmod( ) utime( )

更改文件属性

stat( ) fstat( ) lstat( ) access( ) oldstat( ) oldfstat( ) oldlstat( ) stat64( ) lstat64( )

fstat64( )

读取文件状态

open( ) close( ) creat( ) umask( )

打开/关闭/创建文件

dup( ) dup2( ) fcntl( ) fcntl64( )

对文件描述符进行操作

select( ) poll( )

等待一组文件描述符上发生的事件

truncate( ) ftruncate( ) truncate64( )

ftruncate64( )

更改文件长度

lseek( ) _llseek( )

更改文件指针

read( ) write( ) readv( ) writev( ) sendfile( ) sendfile64( ) readahead( )

进行文件I/O 操作

io_setup( ) io_submit( ) io_getevents( ) io_cancel( ) io_destroy( )

异步I/O (允许多个读和写请求)

pread64( ) pwrite64( )

搜索并访问文件

mmap( ) mmap2( ) munmap( ) madvise( ) mincore( )

remap_file_pages( )

处理文件内存映射

fdatasync( ) fsync( ) sync( ) msync( )

同步文件数据

flock( )

处理文件锁

setxattr( ) lsetxattr( ) fsetxattr( ) getxattr( ) lgetxattr( ) fgetxattr( ) listxattr( ) llistxattr( ) flistxattr( ) removexattr( ) lremovexattr( ) fremovexattr( )

处理文件扩展属性



另外还有少数几个由VFS处理的其他系统调用,诸如ioperm()、ioctl()、pipe()和mknod(),涉及设备文件和管道文件,这些将在后续博文中讨论。最后一组由VFS处理的系统调用,诸如socket()、connect()和bind()属于套接字系统调用,并用于实现网络功能。与表中列出的系统调用还对应的一些内核服务例程,我们会在后面的博文中陆续进行讨论。

前面我们已经提到,VFS是应用程序和具体文件系统之间的一层。不过,在某些情况下,一个文件操作可能由VFS本身去执行,无需调用低层函数。例如,当某个进程关闭一个打开的文件时,并不需要涉及磁盘上的相应文件,因此VFS只需释放对应的文件对象。类似地,当系统调用lseek()修改一个文件指针,而这个文件指针是打开文件与进程交互所涉及的一个属性时,VFS就只需修改对应的文件对象,而不必访问磁盘上的文件,因此,无需调用具体文件系统的函数。从某种意义上说,可以把VFS看成“通用”文件系统,它在必要时依赖某种具体文件系统。

 

目录 1 虚拟文件系统概述 5 1.1 通用文件模型 7 1.2 VFS所处理的系统调用 9 2 虚拟文件系统架构 11 2.1 VFS对象数据结构 11 2.1.1 超级块对象 11 2.1.2 索引节点对象 15 2.1.3 文件对象 18 2.1.4 目录项对象 22 2.2 把Linux中的VFS对象串联起来 24 2.2.1 与进程相关的文件 25 2.2.2 索引节点高速缓存 29 2.2.3 目录项高速缓存 30 2.2.4 VFS对象的具体实现 32 2.3 文件系统的注册与安装 38 2.3.1 文件系统类型注册 38 2.3.2 文件系统安装数据结构 41 2.3.3 安装普通文件系统 52 2.3.4 分配超级块对象 58 2.3.5 安装根文件系统 60 2.3.6 卸载文件系统 65 2.4 路径名的查找 66 2.4.1 查找路径名的一般流程 67 2.4.2 父路径名查找 82 2.4.3 符号链接的查找 84 2.5 VFS系统调用的实现 88 2.5.1 open()系统调用 88 2.5.2 read()和write()系统调用 96 2.5.3 close()系统调用 97 3 第二扩展文件系统 99 3.1 Ext2磁盘数据结构 101 3.1.1 磁盘超级块 102 3.1.2 组描述符和位图 105 3.1.3 磁盘索引节点表 105 3.2 VFS接口数据结构 110 3.2.1 Ext2 超级块对象 110 3.2.2 Ext2 的索引节点对象 121 3.2.3 创建Ext2文件系统 124 3.2.4 Ext2的方法总结 126 3.3 Ext2索引节点分配 129 3.3.1 创建索引节点 130 3.3.2 删除索引节点 143 3.4 Ext2数据块分配 144 3.4.1 数据块寻址 145 3.4.2 文件的洞 147 3.4.3 分配数据块 148 4 页面高速缓存 160 4.1 页高速缓存数据结构 160 4.1.1 address_space对象 161 4.1.2 基树 164 4.2 高速缓存底层处理函数 166 4.2.1 查找页 166 4.2.2 增加页 168 4.2.3 删除页 173 4.3 文件系统与高速缓存 175 4.3.1 缓冲头数据结构 175 4.3.2 分配块设备缓冲区页 178 4.3.3 释放块设备缓冲区页 184 4.4 在页高速缓存中搜索块 185 4.4.1 __find_get_block()函数 185 4.4.2 __getblk()函数 188 4.4.3 __bread()函数 190 4.5 把脏页写入磁盘 191 4.5.1 pdflush内核线程 192 4.5.2 搜索要刷新的脏页 193 4.5.3 回写陈旧的脏页 196 5 文件读写 199 5.1 系统调用VFS层的处理 200 5.2 第二扩展文件系统Ext2层的处理 201 5.2.1 Ext2的磁盘布局 202 5.2.2 Ext2的超级块对象 206 5.2.3 Ext2索引节点对象的创建 210 5.2.4 Ext2索引节点对象的读取 218 5.2.5 Ext2层读文件入口函数 225 5.3 页高速缓存层的处理 237 5.3.1 创建一个bio请求 238 5.3.2 得到文件的逻辑块号 244 5.3.3 普通文件的readpage方法 251 5.3.4 块设备文件的readpage方法 252 5.3.5 文件的预读 260 5.4 通用块层的处理 264 5.4.1 块设备的基础知识 265 5.4.2 通用块层相关数据结构 269 5.4.3 提交I/O传输请求 271 5.4.4 请求队列描述符 273 5.5 块设备I/O调度层的处理 281 5.5.1 块设备的初始化 284 5.5.2 建立块设备驱动环境 288 5.5.3 关联block_device结构 295 5.5.4 为设备建立请求队列 306 5.5.5 块设备I/O调度程序 311 5.5.6 真实的I/O调度层处理 321 5.6 块设备驱动层的处理 330 5.6.1 scsi总线驱动的初始化 330 5.6.2 scsi设备驱动体系架构 342 5.6.3 scsi块设备驱动层处理 347 5.6.4 scsi命令的执行 369 5.6.5 scsi命令的第一次转变 372 5.6.6 scsi命令的第二次转变 380 5.7 写文件 384 5.7.1 generic file_write函数 384 5.7.2 普通文件的prepare_write方法 386 5.7.3 块设备文件的prepare_write方法 387 5.7.4 将脏页写到磁盘 388 6 直接I/O与异步I/O 391 6.1 直接I/O 391 6.2 异步I/O 393 6.2.1 Linux 2.6中的异步I/O 394 6.2.2 异步I/O环境 394 6.2.3 提交异步I/O操作 395
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值