Linux 文件系统.docx
《Linux 文件系统.docx》由会员分享,可在线阅读,更多相关《Linux 文件系统.docx(77页珍藏版)》请在冰豆网上搜索。
![Linux 文件系统.docx](https://file1.bdocx.com/fileroot1/2023-1/10/f01b0fc5-59a9-4791-821c-89311da9ca77/f01b0fc5-59a9-4791-821c-89311da9ca771.gif)
Linux文件系统
Linux文件系统
ØLinux文件系统体系结构
ØLinux文件系统ext2、ext3
ØLinuxReiserFS文件系统
ØLinux新型文件系统
ØRAID
ØJFS
ØLVM
Linux文件系统体系结构
Linux文件系统剖析
在文件系统方面,Linux®可以算得上操作系统中的“瑞士军刀”。
Linux支持许多种文件系统,从日志型文件系统到集群文件系统和加密文件系统。
对于使用标准的和比较奇特的文件系统以及开发文件系统来说,Linux是极好的平台。
本文讨论Linux内核中的虚拟文件系统(VFS,有时候称为虚拟文件系统交换器),然后介绍将文件系统连接在一起的主要结构。
基本的文件系统体系结构
Linux文件系统体系结构是一个对复杂系统进行抽象化的有趣例子。
通过使用一组通用的API函数,Linux可以在许多种存储设备上支持许多种文件系统。
例如,read 函数调用可以从指定的文件描述符读取一定数量的字节。
read 函数不了解文件系统的类型,比如ext3或NFS。
它也不了解文件系统所在的存储媒体,比如ATAttachmentPacketInterface(ATAPI)磁盘、Serial-AttachedSCSI(SAS)磁盘或SerialAdvancedTechnologyAttachment(SATA)磁盘。
但是,当通过调用 read 函数读取一个文件时,数据会正常返回。
本文讲解这个机制的实现方法并介绍Linux文件系统层的主要结构。
什么是文件系统?
首先回答最常见的问题,“什么是文件系统”。
文件系统是对一个存储设备上的数据和元数据进行组织的机制。
由于定义如此宽泛,支持它的代码会很有意思。
正如前面提到的,有许多种文件系统和媒体。
由于存在这么多类型,可以预料到Linux文件系统接口实现为分层的体系结构,从而将用户接口层、文件系统实现和操作存储设备的驱动程序分隔开。
文件系统作为协议
另一种看待文件系统的方式是把它看作一个协议。
网络协议(比如IP)规定了互联网上传输的数据流的意义,同样,文件系统会给出特定存储媒体上数据的意义。
挂装
在Linux中将一个文件系统与一个存储设备关联起来的过程称为挂装(mount)。
使用 mount 命令将一个文件系统附着到当前文件系统层次结构中(根)。
在执行挂装时,要提供文件系统类型、文件系统和一个挂装点。
为了说明Linux文件系统层的功能(以及挂装的方法),我们在当前文件系统的一个文件中创建一个文件系统。
实现的方法是,首先用 dd 命令创建一个指定大小的文件(使用/dev/zero作为源进行文件复制)——换句话说,一个用零进行初始化的文件,见清单1。
清单1.创建一个经过初始化的文件
$ddif=/dev/zeroof=file.imgbs=1kcount=10000
10000+0recordsin
10000+0recordsout
$
现在有了一个10MB的file.img文件。
使用 losetup 命令将一个循环设备与这个文件关联起来,让它看起来像一个块设备,而不是文件系统中的常规文件:
$losetup/dev/loop0file.img
$
这个文件现在作为一个块设备出现(由/dev/loop0表示)。
然后用 mke2fs 在这个设备上创建一个文件系统。
这个命令创建一个指定大小的新的ext2文件系统,见清单2。
清单2.用循环设备创建ext2文件系统
$mke2fs-c/dev/loop010000
mke2fs1.35(28-Feb-2004)
max_blocks1024000,rsv_groups=1250,rsv_gdb=39
Filesystemlabel=
OStype:
Linux
Blocksize=1024(log=0)
Fragmentsize=1024(log=0)
2512inodes,10000blocks
500blocks(5.00%)reservedforthesuperuser
...
$
使用 mount 命令将循环设备(/dev/loop0)所表示的file.img文件挂装到挂装点/mnt/point1。
注意,文件系统类型指定为 ext2。
挂装之后,就可以将这个挂装点当作一个新的文件系统,比如使用 ls 命令,见清单3。
清单3.创建挂装点并通过循环设备挂装文件系统
$mkdir/mnt/point1
$mount-text2/dev/loop0/mnt/point1
$ls/mnt/point1
lost+found
$
如清单4所示,还可以继续这个过程:
在刚才挂装的文件系统中创建一个新文件,将它与一个循环设备关联起来,再在上面创建另一个文件系统。
清单4.在循环文件系统中创建一个新的循环文件系统
$ddif=/dev/zeroof=/mnt/point1/file.imgbs=1kcount=1000
1000+0recordsin
1000+0recordsout
$losetup/dev/loop1/mnt/point1/file.img
$mke2fs-c/dev/loop11000
mke2fs1.35(28-Feb-2004)
max_blocks1024000,rsv_groups=125,rsv_gdb=3
Filesystemlabel=
...
$mkdir/mnt/point2
$mount-text2/dev/loop1/mnt/point2
$ls/mnt/point2
lost+found
$ls/mnt/point1
file.imglost+found
$
通过这个简单的演示很容易体会到Linux文件系统(和循环设备)是多么强大。
可以按照相同的方法在文件上用循环设备创建加密的文件系统。
可以在需要时使用循环设备临时挂装文件,这有助于保护数据。
文件系统体系结构
既然已经看到了文件系统的构造方法,现在就看看Linux文件系统层的体系结构。
本文从两个角度考察Linux文件系统。
首先采用高层体系结构的角度。
然后进行深层次讨论,介绍实现文件系统层的主要结构。
高层体系结构
尽管大多数文件系统代码在内核中(后面讨论的用户空间文件系统除外),但是图1所示的体系结构显示了用户空间和内核中与文件系统相关的主要组件之间的关系。
图1.Linux文件系统组件的体系结构
用户空间包含一些应用程序(例如,文件系统的使用者)和GNUC库(glibc),它们为文件系统调用(打开、读取、写和关闭)提供用户接口。
系统调用接口的作用就像是交换器,它将系统调用从用户空间发送到内核空间中的适当端点。
VFS是底层文件系统的主要接口。
这个组件导出一组接口,然后将它们抽象到各个文件系统,各个文件系统的行为可能差异很大。
有两个针对文件系统对象的缓存(inode和dentry)。
它们缓存最近使用过的文件系统对象。
每个文件系统实现(比如ext2、JFS等等)导出一组通用接口,供VFS使用。
缓冲区缓存会缓存文件系统和相关块设备之间的请求。
例如,对底层设备驱动程序的读写请求会通过缓冲区缓存来传递。
这就允许在其中缓存请求,减少访问物理设备的次数,加快访问速度。
以最近使用(LRU)列表的形式管理缓冲区缓存。
注意,可以使用 sync 命令将缓冲区缓存中的请求发送到存储媒体(迫使所有未写的数据发送到设备驱动程序,进而发送到存储设备)。
什么是块设备?
块设备就是以块(比如磁盘扇区)为单位收发数据的设备,它们支持缓冲和随机访问(不必顺序读取块,而是可以在任何时候访问任何块)等特性。
块设备包括硬盘、CD-ROM和RAM盘。
与块设备相对的是字符设备,字符设备没有可以进行物理寻址的媒体。
字符设备包括串行端口和磁带设备,只能逐字符地读取这些设备中的数据。
这就是VFS和文件系统组件的高层情况。
现在,讨论实现这个子系统的主要结构。
主要结构
Linux以一组通用对象的角度看待所有文件系统。
这些对象是超级块(superblock)、inode、dentry和文件。
超级块在每个文件系统的根上,超级块描述和维护文件系统的状态。
文件系统中管理的每个对象(文件或目录)在Linux中表示为一个inode。
inode包含管理文件系统中的对象所需的所有元数据(包括可以在对象上执行的操作)。
另一组结构称为dentry,它们用来实现名称和inode之间的映射,有一个目录缓存用来保存最近使用的dentry。
dentry还维护目录和文件之间的关系,从而支持在文件系统中移动。
最后,VFS文件表示一个打开的文件(保存打开的文件的状态,比如写偏移量等等)。
虚拟文件系统层
VFS作为文件系统接口的根层。
VFS记录当前支持的文件系统以及当前挂装的文件系统。
可以使用一组注册函数在Linux中动态地添加或删除文件系统。
内核保存当前支持的文件系统的列表,可以通过/proc文件系统在用户空间中查看这个列表。
这个虚拟文件还显示当前与这些文件系统相关联的设备。
在Linux中添加新文件系统的方法是调用 register_filesystem。
这个函数的参数定义一个文件系统结构(file_system_type)的引用,这个结构定义文件系统的名称、一组属性和两个超级块函数。
也可以注销文件系统。
在注册新的文件系统时,会把这个文件系统和它的相关信息添加到file_systems列表中(见图2和linux/include/linux/mount.h)。
这个列表定义可以支持的文件系统。
在命令行上输入 cat/proc/filesystems,就可以查看这个列表。
图2.向内核注册的文件系统
VFS中维护的另一个结构是挂装的文件系统(见图3)。
这个结构提供当前挂装的文件系统(见linux/include/linux/fs.h)。
它链接下面讨论的超级块结构。
图3.挂装的文件系统列表
超级块
超级块结构表示一个文件系统。
它包含管理文件系统所需的信息,包括文件系统名称(比如ext2)、文件系统的大小和状态、块设备的引用和元数据信息(比如空闲列表等等)。
超级块通常存储在存储媒体上,但是如果超级块不存在,也可以实时创建它。
可以在./linux/include/linux/fs.h中找到超级块结构(见图4)。
图4.超级块结构和inode操作
超级块中的一个重要元素是超级块操作的定义。
这个结构定义一组用来管理这个文件系统中的inode的函数。
例如,可以用 alloc_inode 分配inode,用 destroy_inode 删除inode。
可以用 read_inode 和write_inode 读写inode,用 sync_fs 执行文件系统同步。
可以在./linux/include/linux/fs.h中找到 super_operations 结构。
每个文件系统提供自己的inode方法,这些方法实现操作并向VFS层提供通用的抽象。
inode和dentry
inode表示文件系统中的一个对象,它具有惟一标识符。
各个文件系统提供将文件名映射为惟一inode标识符和inode引用的方法。
图5显示inode结构的一部分以及两个相关结构。
请特别注意inode_operations 和 file_operations。
这些结构表示可以在这个inode上执行的操作。
inode_operations 定义直接在inode上执行的操作,而 file_operations 定义与文件和目录相关的方法(标准系统调用)。
图5.inode结构和相关联的操作
inode和目录缓存分别保存最近使用的inode和dentry。
注意,对于inode缓存中的每个inode,在目录缓存中都有一个对应的dentry。
可以在./linux/include/linux/fs.h中找到 inode 和 dentry 结构。
缓冲区缓存
除了各个文件系统实现(可以在./linux/fs中找到)之外,文件系统层的底部是缓冲区缓存。
这个组件跟踪来自文件系统实现和物理设备(通过设备驱动程序)的读写请求。
为了提高效率,Linux对请求进行缓存,避免将所有请求发送到物理设备。
缓存中缓存最近使用的缓冲区(页面),这些缓冲区可以快速提供给各个文件系统。
有趣的文件系统
本文没有讨论Linux中可用的具体文件系统,但是值得在这里稍微提一下。
Linux支持许多种文件系统,包括MINIX、MS-DOS和ext2等老式文件系统。
Linux还支持ext3、JFS和ReiserFS等新的日志型文件系统。
另外,Linux支持加密文件系统(比如CFS)和虚拟文件系统(比如/proc)。
最后一种值得注意的文件系统是FilesysteminUserspace(FUSE)。
这种文件系统可以将文件系统请求通过VFS发送回用户空间。
所以,如果您有兴趣创建自己的文件系统,那么通过使用FUSE进行开发是一种不错的方法。
结束语
尽管文件系统的实现并不复杂,但它是可伸缩和可扩展的体系结构的好例子。
文件系统体系结构已经发展了许多年,并成功地支持了许多不同类型的文件系统和许多目标存储设备类型。
由于使用了基于插件的体系结构和多层的函数间接性,Linux文件系统在近期的发展很值得关注。
参考资料
学习
∙您可以参阅本文在developerWorks全球站点上的 英文原文。
∙proc文件系统提供了一种通过虚拟文件系统在用户空间和内核之间进行通信的新方式。
“使用/proc文件系统来访问Linux内核的内容”(developerWorks,2006年3月)介绍了/proc虚拟文件系统并演示它的使用方法。
∙Linux系统调用接口可以在用户空间和内核之间传递控制,从而调用内核API函数。
“使用Linux系统调用的内核命令”(developerWorks,2007年)介绍了Linux系统调用接口。
∙Y 维护Linux文件系统、集群文件系统和性能计算集群的列表。
还可以在 FilesystemsHOWTO 中找到Linux文件系统的完整列表。
Xenotime 也描述了许多文件系统。
∙关于Linux用户空间编程的更多信息,请参考本文作者所著的 GNU/LinuxApplicationProgramming 。
∙在 developerWorksLinux专区 中可以找到为Linux开发人员准备的更多参考资料,还可以查阅 最流行的文章和教程。
∙查阅developerWorks上的所有 Linux技巧 和 Linux教程。
∙随时关注 developerWorks技术活动和网络广播。
获得产品和技术
∙FilesysteminUserspace(FUSE)是一个支持在用户空间中开发文件系统的内核模块。
文件系统驱动程序实现将来自VFS的请求发送回用户空间。
这是一种在不借助内核开发的情况下开发文件系统的好方法。
如果您精通Python,也可以通过 LUFS-Python 用Python编写文件系统。
∙订购SEKforLinux,这有两张DVD,包含最新的IBMLinux试用版软件,涉及DB2®、Lotus®、Rational®、Tivoli®和WebSphere®。
∙下载 IBM产品评估版,试用来自DB2®、Lotus®、Rational®、Tivoli®和WebSphere®的应用程序开发工具和中间件产品。
讨论
∙通过 新的developerWorks空间 中的blog、论坛、podcast和社区主题,加入 developerWorks社区。
Linux虚拟系统文件交换器剖析(New!
)
Linux®的最显著特征是灵活性和扩展性,例如它的虚拟文件系统交换器(VFS)。
您可以在各种设备上创建文件系统,包括传统的磁盘、USBflash驱动、内存以及其他储存设备。
您还可以在另一个文件系统环境中嵌入文件系统。
探索导致VFS如此强大的因素,并了解VFS的主要接口和进程。
Linux文件系统的灵活性和扩展性支持直接源于一组抽象接口。
这组接口的核心就是虚拟文件系统交换器(VFS)。
VFS为上层应用程序提供一组标准接口,用于对不同的文件系统执行文件I/O。
这组接口在一个或多个底层设备上支持多个并发文件系统。
另外,这些文件系统可以不是静态的,可以根据储存设备而变化。
VF与VFS
您还可以看到VFS还被定义为虚拟文件系统(virtualfilesystem),但定义为虚拟文件系统转换器(virtualfilesystemswitch)更能说明其作用,因为虚拟层跨多个文件系统转换(即多路复用)请求。
/proc 文件系统在这里带来很多的混淆,因为它通常也被称为虚拟文件系统。
例如,一个典型的Linux桌面在可用硬盘上支持ext3文件系统,并且在可用的CD-ROM(或者称为 CD-ROM文件系统或CDFS)上支持ISO9660文件系统。
因为CD-ROM是可以插入和移除的,所以Linux内核必须适应这些包含不同内容和结构的新文件系统。
可以通过网络文件系统(NetworkFileSystem,NFS)访问远程文件系统。
在此时,Linux还可以挂载来自本地硬盘的Windows®/Linux双引导系统的NTFileSystem(NTFS)分区,并且能够向其读写数据。
最后,可移除的USBflash启动(UFD)是可以热插拔的,它构成另一个文件系统。
概而言之,可以在这些设备中使用同一组文件I/O接口,从而允许底层的文件系统和物理设备能够从用户中抽象出来(见图1)。
图1.在不同文件系统和储存设备之间提供统一接口的抽象层
分层抽象
现在,我们向LinuxVFS提供的抽象特性添加一些具体的架构。
图2从VFS的角度显示Linux结构的高级视图。
在VFS之上的是标准的内核系统调用接口(SCI)。
这个接口允许用户空间发出要求转换到内核的调用(在不同地址空间中)。
在这个域中,调用POSIX open 调用的用户空间应用程序经过GNUC库(glibc)进入内核和系统调用去多元化(de-multiplexing)。
最后,使用 sys_open 调用VFS。
图2.VFS的分层架构
早期的VFS实现
Linux并不是第一个包含虚拟层以支持通用文件模型的操作系统。
早期的VFS实现包括Sun的VFS(SunOSversion2.0,大约出现在1985年),以及IBM和Microsoft®的“InstallableFileSystem”forIBMOS/2。
这些虚拟化文件系统层的方法为LinuxVFS铺平了道路。
VFS提供抽象层,从而将POSIXAPI与特定文件系统如何实现该行为的细节分离开来。
这里的关键之处是,不管底层文件系统是ext3还是Btrfs,Open、Read、Write或CloseAPI系统调用都能正常工作。
VFS提供一个由底层文件系统(它们必须为各种POSIXAPI函数实现行为)继承的通用文件模型。
另一个在VFS范围之外的深层抽象隐藏了底层物理设备(可能是磁盘、磁盘分区、网络储存实体、内存或其他能够储存信息的媒介——即使是暂时性的)。
除了从底层文件系统抽象文件操作的细节之外,VFS还将底层块设备绑定到可用的文件系统。
让我们看看VFS的内部结构及其工作原理。
VFS的内部结构
在查看VFS子系统的总体架构之前,我们先看看所使用的主要对象。
这个小节探索了超块(superblock)、索引节点(或 inode)、目录条目(或 dentry)和文件对象。
在这里,其他一些组成部分也很重要,比如缓存。
不过我将在后面的总体架构中讨论它们。
超块
超块(superblock)是关于文件系统的高级元数据的容器。
超块是存在于磁盘上(实际上位于磁盘的多个位置上,以提供冗余)的结构。
它是处理磁盘上的文件系统的基础,因为它定义文件系统的管理参数(例如,块的总数、空闲块和根索引节点)。
在磁盘上,超块向内核提供关于磁盘上的文件系统的结构的信息。
在内存中,超块为管理活动的(已挂载)文件系统提供必要的信息和状态。
因为Linux支持同时挂载多个并发文件系统,所以在一个列表中维护每个 super_block 结构(super_blocks 在./linux/fs/super.c中定义,结构在/linux/include/fs/fs.h中定义)。
图3提供了超块及其元素的简化视图。
super_block 结构是指封装了其他信息的许多其他结构。
例如,file_system_type 结构维护文件系统的名称(比如 ext3)以及各种锁和函数,以获取和删除super_block。
file_system_type 对象由常见的 register_filesystem 和 unregister_filesystem 函数管理(见./linux/fs/filesystems.c)。
super_operations 结构为读写节点和高级操作(比如重新挂载)定义大量函数。
根目录条目(dentry)对象也缓存在这里,因为它是文件系统所在的块设备。
最后,提供许多用于管理节点的列表,包括 s_inodes(列出所有节点的列表)、s_dirty(列出所有脏节点的列表)、s_io 和 s_more_io 以及 s_files(列出特定文件系统的所有打开文件的列表)。
图3.super_block结构及其元素的简化视图
注意,在内核内部,另一个称为 vfsmount 的管理对象提供关于已挂载的文件系统的信息。
这些对象的列表引用超块,并定义挂载点、文件系统所在的/dev设备的名称以及其他高级附加信息。
索引节点(inode)
Linux通过一个称为 inode(indexnode 的缩写)的对象管理文件系统中的所有对象。
一个inode可以引用一个文件、目录或另一个对象的符号链接。
注意,因为文件用于表示其他类型的对象(比如设备或内存),所以也使用inode来表示它们。
我在这里所指的inode是VFS层inode(常驻inode)。
每个文件系统也包含一个位于磁盘上的inode,并且提供关于特定文件系统的特定对象的细节。
VFSinode使用slab分配器进行分配(来自 inode_cache;参考资料 部分提供一个介绍slab分配器的链接)。
inode由描述inode、inode内容和可能在i