红联Linux门户
Linux帮助

Linux世界驰骋----文件系统和设备管理

发布时间:2005-09-21 18:20:21来源:红联作者:晴
作者:孟庆昌 来源:赛迪技术天地-开放系统世界

Linux系统的一个重要特征就是支持多种不同的文件系统,如ext、FAT、ext2、ext3、MINIX、MSDOS、SYSV等。目前,Linux主要使用的文件系统是ext2和ext3。

ext3是ext2的升级版本,加入了记录数据的日志功能。ext2和ext3都是十分优秀的文件系统,即使系统发生崩溃也能很快修复。

Linux系统的第一个文件系统是Minix,1992年引进了第一个专门为Linux设计的文件系统--ext(extended file system),1993年又推出了一个新文件系统--ext2。

当Linux引进ext文件系统时有了一个重大的改进:真正的文件系统从操作系统和系统服务中分离出来,在它们之间使用了一个接口层--虚拟文件系统VFS (Virtual File System)。VFS为用户程序提供一个统一的、抽象的、虚拟的文件系统界面,这个界面主要由一组标准的、抽象的、有关文件操作的系统调用构成。

ext2文件系统

ext2文件系统支持标准Unix文件类型,例如普通文件、目录文件、特别文件和符号链接等。ext2文件系统可以管理特别大的分区。以前内核代码限制文件系统的大小为2GB,现在VFS把这个限制提高到4TB。因此,现在使用大磁盘而不必划分多个分区。

ext2文件系统支持长文件名,最大长度为255个字符,如果需要还可以增加到1012个字符,而且,它使用变长的目录表项。

ext2文件系统为超级用户保留了一些数据块,约为5%。这样,在用户进程占满整个文件系统的情况下,系统管理员仍可以简单地恢复整个系统。

除了标准的Unix功能外,ext2文件系统还支持在一般Unix文件系统中没有的高级功能,如设置文件属性、支持数据更新时同步写入磁盘的功能、允许系统管理员在创建文件系统时选择逻辑数据块的大小、实现快速符号链接,以及提供两种定期强迫进行文件系统检查的工具等。

1.ext2文件系统的物理结构

与其它文件系统一样,ext2文件系统中的文件信息都保存在数据块中。对同一个ext2文件系统而言,所有数据块的大小都是一样的,例如1024字节。但是,不同的ext2文件系统中数据块的大小可以不同。ext2文件系统的物理构造形式如图1所示。


图1 ext2文件系统的物理布局


ext2文件系统分布在块结构的设备中,文件系统不必了解数据块的物理存储位置,它保存的是逻辑块的编号。块设备驱动程序能够将逻辑块号转换到块设备的物理存储位置。

ext2文件系统将逻辑块划分成块组,每个块组重复保存着一些有关整个文件系统的关键信息及实际文件和目录的数据块。

系统引导块总是介质上的第一个数据块,只有根文件系统才有引导程序放在这里,其余一般文件系统都不使用引导块。

使用块组对于提高文件系统的可靠性有很大好处,由于文件系统的控制管理信息在每个块组中都有一份拷贝,因此当文件系统意外出现崩溃时可以很容易地恢复。另外,由于在有关块组内部,索引节点表和数据块的位置很近,在对文件进行I/O操作时,可减少硬盘磁头的移动距离。
文章评论

共有 19 条评论

  1. ly_chaojilong 于 2009-09-08 15:00:56发表:

    文件系统向来都是比较头痛的,不过也收藏了,有时间看看。

  2. a253794397 于 2009-09-03 23:52:30发表:

    嗯,俺得多看看呀, 向楼主敬礼

  3. a253794397 于 2009-09-03 23:52:17发表:

    多谢分享哦

  4. xuweixin 于 2007-08-24 16:39:30发表:

  5. 新新 于 2005-09-22 00:32:59发表:

    不错

  6. 晴 于 2005-09-21 18:29:42发表:

    核心对中断处理的顺序主要由以下动作完成:

    ◆保存正在运行进程的各寄存器的内容,把它们放入核心栈的新帧面中。

    ◆确定“中断源”或核查中断发生,识别中断的类型(如时钟中断或盘中断)和中断的设备号(如哪个磁盘引起的中断)。系统接到中断后,就从机器那里得到一个中断号,它是检索中断向量表的位移。中断向量因机器而异,但通常都包括相应中断处理程序入口地址和中断处理时处理机的状态字。

    ◆核心调用中断处理程序,对中断进行处理。

    ◆中断处理完成并返回。中断处理程序执行完以后,核心便执行与机器相关的特定指令序列,恢复中断时寄存器内容和执行核心栈退栈,进程回到用户态。如果设置了重调度标志,则在本进程返回到用户态时做进程调度。

    2.系统调用

    在Unix/Linux系统中,系统调用像普通C函数调用那样出现在C程序中。但是一般的函数调用序列并不能把进程的状态从用户态变为核心态,而系统调用却可以做到。

    C语言编译程序利用一个预先确定的函数库(一般称为C库),其中有各系统调用的名字。C库中的函数都专门使用一条指令,把进程的运行状态改为核心态。Linux的系统调用是通过中断指令“INT 0x80”实现的。

    每个系统调用都有惟一的号码,称作系统调用号。所有的系统调用都集中在系统调用入口表中统一管理。

    系统调用入口表是一个函数指针数组,以系统调用号为下标在该数组中找到相应的函数指针,进而就能确定用户使用的是哪一个系统调用。不同系统中系统调用的个数是不同的,目前Linux系统中共定义了221个系统调用。

    另外,系统调用表中还留有一些余项,可供用户自行添加。

    当CPU执行到中断指令“INT 0x80”时,硬件就做出一系列响应,其动作与上述的中断响应相同。CPU穿过陷阱门,从用户空间进入系统空间。相应地,进程的上下文从用户堆栈切换到系统堆栈。

    接着运行内核函数system_call()。首先,进一步保存各寄存器的内容;接着调用syscall_trace( ),以系统调用号为下标检索系统调用入口表sys_call_table,从中找到相应的函数;然后转去执行该函数,完成具体的服务。

    执行完服务程序,核心检查是否发生错误,并作相应处理。如果本进程收到信号,则对信号作相应处理。最后进程从系统空间返回到用户空间。

    上面两讲简要介绍了Linux内核的主要数据结构和相应的算法。Linux内核包含了丰富的内容,这里仅是其中的一点点,以求起到“抛砖引玉”的作用。

  7. 晴 于 2005-09-21 18:29:20发表:

    中断、异常和系统调用

    所谓中断是指CPU对系统发生的某个事件做出的一种反应,CPU暂停正在执行的程序,保留现场后自动地转去执行相应的处理程序,处理完该事件后再返回断点继续执行被“打断”的程序。

    中断可分为三类,第一类是由CPU外部引起的,称作中断,如I/O中断、时钟中断、控制台中断等。第二类是来自CPU的内部事件或程序执行中的事件引起的过程,称作异常,如由于CPU本身故障(电源电压低于105V或频率在47~63Hz之外)、程序故障(非法操作码、地址越界、浮点溢出等)等引起的过程。

    第三类由于在程序中使用了请求系统服务的系统调用而引发的过程,称作“陷入”(trap,或者陷阱)。前两类通常都称作中断,它们的产生往往是无意、被动的,而陷入是有意和主动的。

    1.中断处理

    中断处理一般分为中断响应和中断处理两个步骤。中断响应由硬件实施,中断处理主要由软件实施。

    (1)中断响应

    对中断请求的整个处理过程是由硬件和软件结合起来而形成的一套中断机构实施的。发生中断时,CPU暂停执行当前的程序,而转去处理中断。这个由硬件对中断请求作出反应的过程,称为中断响应。一般说来,中断响应顺序执行下述三步动作:

    ◆中止当前程序的执行;

    ◆保存原程序的断点信息(主要是程序计数器PC和程序状态寄存器PS的内容);

    ◆从中断控制器取出中断向量,转到相应的处理程序。

    通常CPU在执行完一条指令后,立即检查有无中断请求,如果有,则立即做出响应。

    当发生中断时,系统作出响应,不管它们是来自硬件(如来自时钟或者外部设备)、程序性中断(执行指令导致“软件中断”--Software Interrupts),或者来自意外事件(如访问页面不在内存)。

    如果当前CPU的执行优先级低于中断的优先级,那么它就中止对当前程序下条指令的执行,接受该中断,并提升处理机的执行级别(一般与中断优先级相同),以便在CPU处理当前中断时,能屏蔽其它同级的或低级的中断,然后保存断点现场信息,通过取得的中断向量转到相应的中断处理程序的入口。

    (2)中断处理

    CPU从中断控制器取得中断向量,然后根据具体的中断向量从中断向量表IDT中找到相应的表项,该表项应是一个中断门。于是,CPU就根据中断门的设置而到达了该通道的总服务程序的入口。

  8. 晴 于 2005-09-21 18:28:55发表:

    数组blkdevs也使用设备的主设备号作为索引,其元素类型是device_struct结构。该结构中包括指向已登记的设备驱动程序名的指针和指向block_device_operations结构的指针。

    在block_device_operations结构中包含指向有关操作的函数指针。所以,该结构就是连接抽象的块设备操作与具体块设备类型的操作之间的枢纽。

    与字符设备不一样,块设备有几种类型,例如SCSI设备和IDE设备。每类块设备都在Linux系统内核中登记,并向内核提供自己的文件操作。

    为了把各种块设备的操作请求队列有效地组织起来,内核中设置了一个结构数组blk_dev,该数组中的元素类型是blk_dev_struct结构。这个结构由三个成分组成,其主体是执行操作的请求队列request_queue,还有一个函数指针queue。

    当这个指针不为0时,就调用这个函数来找到具体设备的请求队列。这是考虑到多个设备可能具有同一主设备号,该指针在设备初始化时被设置好。通常当它不为0 时,还要使用该结构中的另一个指针data,用来提供辅助性信息,帮助该函数找到特定设备的请求队列。每一个请求数据结构都代表一个来自缓冲区的请求。

    每当缓冲区要和一个登记过的块设备交换数据,它都会在blk_dev_struct中添加一个请求数据结构,如图8所示。

    图8 块设备驱动程序数据结构示意图


    每一个请求都有一个指针指向一个或多个buffer_head数据结构,而该结构都是一个读写数据块的请求。每一个请求结构都在一个静态链表 all_requests中。若干请求是添加到一个空的请求链表中,则调用设备驱动程序的请求函数,开始处理该请求队列。否则,设备驱动程序就简单地处理请求队列中的每一个请求。

    当设备驱动程序完成了一个请求后,就把buffer_head结构从request结构中移走,并标记buffer_head结构已更新,同时解锁,这样,就可以唤醒相应的等待进程。

  9. 晴 于 2005-09-21 18:28:12发表:

    为了适应设备驱动程序动态连接的特性,设备驱动程序在其初始化时就在系统内核中进行登记。Linux系统利用设备驱动程序的登记表作为内核与驱动程序接口的一部分,这些表中包括指向有关处理程序的指针和其它信息。

    3.字符设备

    在Linux系统中,打印机、终端等字符设备都作为字符特别文件出现在用户面前。用户对字符设备的使用就和存取普通文件一样。在应用程序中,使用标准的系统调用来打开、关闭、读写字符设备。当字符设备初始化时,其设备驱动程序被添加到由device_struct结构组成的chrdevs结构数组中。

    device_struct结构由两项构成,一个是指向已登记的设备驱动程序名的指针,另一个是指向file_operations结构的指针。而 file_operations结构的成分几乎全是函数指针,分别指向实现文件操作的入口函数。设备的主设备号用来对chrdevs数组进行索引,如图7 所示。

    图7 字符设备驱动程序示意图


    前面讲过,每个VFS索引节点都和一系列文件操作相联系,并且这些文件操作随索引节点所代表的文件类型不同而不同。每当一个VFS索引节点所代表的字符设备文件创建时,它的有关文件的操作就设置为默认的字符设备操作。

    默认的文件操作只包含一个打开文件的操作。当打开一个代表字符设备的特别文件以后,就得到相应的VFS索引节点,其中包括该设备的主设备号和次设备号。

    利用主设备号就可以检索chrdevs数组,进而可以找到有关此设备的各种文件操作。这样,应用程序中的文件操作就会映射到字符设备的文件操作调用中。

    4.块设备

    对块设备的存取和对文件的存取方式一样,其实现机制也和字符设备使用的机制相同。Linux系统中有一个名为blkdevs的结构数组,它描述了一系列在系统中登记的块设备。

  10. 晴 于 2005-09-21 18:27:20发表:

    图6示出设备驱动的分层结构,从中可以看出,处于应用层的进程通过文件描述字fd与已打开文件的file结构相联系。在文件系统层,按照文件系统的操作规则对该文件进行相应处理。

    对于一般文件(即磁盘文件),要进行空间的映射--从普通文件的逻辑空间映射到设备的逻辑空间,然后在设备驱动层做进一步映射--从设备的逻辑空间映射到物理空间(即设备的物理地址空间),进而驱动底层物理设备工作。

    对于设备文件,则文件的逻辑空间通常就等价于设备的逻辑空间,然后从设备的逻辑空间映射到设备的物理空间,再驱动底层的物理设备工作。

    2.设备驱动程序和内核之间的接口

    Linux系统和设备驱动程序之间使用标准的交互接口。无论是字符设备、块设备还是网络设备的驱动程序,当内核请求它们提供服务时,都使用同样的接口。

    Linux提供了一种全新的机制,就是“可安装模块”。可安装模块是可以在系统运行时动态地安装和拆卸的内核模块。利用这个机制,可以根据需要在不必对内核重新编译连接的条件下,将可安装模块动态插入运行中的内核,成为其中一个有机组成部分,或者从内核卸载已安装的模块。设备驱动程序或与设备驱动紧密相关的部分(如文件系统) 都是利用可安装模块实现的。

    在应用程序界面上,利用内核提供的系统调用来实现可安装模块的动态安装和拆卸。但通常情况下,用户是利用系统提供的插入模块工具和移走模块工具来装卸可安装模块。插入模块的工作主要如下:

    (1) 打开要安装的模块,把它读到用户空间。这种“模块”就是经过编译但尚未连接的.o文件。

    (2) 必须把模块内涉及对外访问的符号(函数名或变量名)连接到内核,即把这些符号在内核映像中的地址填入该模块需要访问这些符号的指令及数据结构中。

    (3) 在内核创建一个module数据结构,并申请所需的系统空间。

    (4) 最后,把用户空间中完成了连接的模块映像装入内核空间,并在内核中“登记”本模块的有关数据结构(如file_operations结构),其中有指向执行相关操作函数的指针。

    如前所述,Linux系统是一个动态的操作系统。用户根据工作中的需要,会对系统中设备重新配置,如安装新的打印机、卸载老式终端等。这样,每当Linux系统内核初启时,它都要对硬件配置进行检测,很有可能会检测到不同的物理设备,就需要不同的驱动程序。

    在构建系统内核时,可以使用配置脚本将设备驱动程序包含在系统内核中。在系统启动时对这些驱动程序初始化,它们可能未找到所控制的设备,而另外的设备驱动程序可以在需要时作为内核模块装入到系统内核中。

  11. 晴 于 2005-09-21 18:26:56发表:

    设备管理

    设备管理是操作系统五大管理中最复杂的部分。与Unix系统一样,Linux系统采用设备文件统一管理硬件设备,从而将硬件设备的特性及管理细节对用户隐藏起来,实现用户程序与设备无关性。在Linux系统中,硬件设备分为两种,即块设备和字符设备。

    1.特别文件

    用户是通过文件系统与设备接口的,所有设备都作为特别文件,从而在管理上就具有一些共性。 (1)每个设备都对应文件系统中的一个索引节点,都有一个文件名。设备的文件名一般由两部分构成,第一部分是主设备号,第二部分是次设备号。

    主设备号代表设备的类型,可以惟一地确定设备的驱动程序和界面,如hd表示IDE硬盘,sd表示SCSI硬盘,tty表示终端设备等;次设备号代表同类设备中的序号,如hda表示IDE主硬盘,hdb表示IDE从硬盘等。

    (2)应用程序通常可以通过系统调用open( )打开设备文件,建立起与目标设备的连接。

    (3)对设备的使用类似于对文件的存取。打开设备文件以后,就可以通过read( )、write( )、ioctl( )等文件操作对目标设备进行操作。

    (4)设备驱动程序都是系统内核的一部分,它们必须为系统内核或它们的子系统提供一个标准的接口。例如,一个终端驱动程序必须为Linux内核提供一个文件I/O接口;一个SCSI设备驱动程序应该为SCSI子系统提供一个SCSI设备接口,同时SCSI子系统也应为内核提供文件I/O和缓冲区。

    (5)设备驱动程序利用一些标准的内核服务,如内存分配等。另外,大多数Linux设备驱动程序都可以在需要时装入内核,不需要时可以卸载下来。

    图6 设备驱动分层结构示意图

  12. 晴 于 2005-09-21 18:26:05发表:

    当虚拟文件系统根据需要计算出一个散列值时,VFS就将该散列值作为访问散列表的索引,从散列表中得到指向相应的索引节点队列的指针。如果在所指的队列中包含要查找的索引节点,则说明该索引节点包含在高速缓存中,然后将找到的索引节点的访问计数加1,表明又有一个进程在使用该索引节点。

    否则,必须找到一个空闲的VFS索引节点,并且从底层的文件系统中读取该索引节点,然后把新的索引节点放到对应的散列队列中。

    为了加速对常用目录的存取,VFS还提供一个目录高速缓存。当实际文件系统读取一个目录的时候,就把目录的详细信息添加到目录缓存中。下一次查找该目录时,系统就可以在目录缓存中找到此目录的有关信息。

    目录缓存也采用散列表的方法进行管理。表中每一项都是一个指针,指向有相同散列值的目录缓存队列。散列值是利用文件系统所在设备的号码和目录名来计算的。由于高速缓存的容量不可能很大,所以在使用过程中需要对缓存中的目录进行替换。

    VFS采用LRU算法(最近最少使用算法)来替换缓存中的目录项,其思想是把最近最不经常使用的目录项替换掉。其方法是VFS维护一个目录缓存链表,当第一次查找一个目录项时,该目录项就被放入目录缓存中,同时也加到第一层LRU链表的末尾。如果此时缓存已满,将替换LRU链表中最前面一个目录项,把它放入缓存中。

    以后该目录项再次被存取时,它将被提升到第二层LRU链表的末尾。同样,若缓存已满,则替换该链表中最前面的目录项。这样一来,经常用到的目录项就不会出现在链表的前面,只有那些最近不常用的项才逐步移动到链表的开头,从而被替换掉。

    7.数据块缓冲区

    Linux系统采用多重缓冲技术,来平滑和加快文件信息从内存到磁盘的传输。当从盘上读数据时,如果数据已经在缓冲区中,则核心就直接从中读出,而不必从盘上读;仅当所需数据不在缓冲区中时,核心才把数据从盘上读到缓冲区,然后再由缓冲区读出。核心尽量想让数据在缓冲区停留较长时间,以减少磁盘I/O的次数。

    在系统初启时,核心根据内存大小和系统性能要求分配若干缓冲区。一个缓冲区由两部分组成,即存放数据的缓冲区和一个缓冲控制块(又称缓冲首部 buffer_head,其中包含指向相应缓冲区的指针和记载缓冲区使用情况的信息)。缓冲区和缓冲控制块是一一对应的,系统是通过缓冲控制块来实现对缓冲区的管理。

    所有处于“空闲”状态的buffer_head都链入自由链中,它只有一条。具有相同散列值(是由设备的标识符和数据块的块号生成的)的缓冲区组成一条散列队列,可以有多个散列队列。每个缓冲区总是存在于一个散列队列中,但其位置是动态可变的。每个队列都被一个指针所指示,这些指针构成一个散列表,其形式与图5相似。

    当进程想从物理块设备上读取数据块或打算把数据块写到物理块设备上时,核心要查看该数据块是否已在缓冲池中,如果未在,则为该块分配一个空闲的缓冲区。当核心用完缓冲区后,要把它释放,链入自由链。对数据块缓冲区的管理也采用LRU算法。

  13. 晴 于 2005-09-21 18:25:27发表:

    5.文件系统的安装与拆卸

    Linux文件系统可以根据需要随时装卸,从而实现文件存储空间的动态扩充。在系统初启时,往往只安装有一个文件系统,即根文件系统,其上的文件主要是保证系统正常运行的操作系统代码文件,以及若干语言编译程序、命令解释程序和相应的命令处理程序等构成的文件。

    此外,还有大量的用户文件空间。根文件系统一旦被安装上,则在整个系统运行过程中是不能卸下的,它是系统的基本部分。

    其它的文件系统(例如由软盘构成的文件系统)可以根据需要(如从硬盘向软盘复制文件),作为子系统动态地安装到主系统中。经过安装之后,主文件系统与子文件系统就构成一个有完整目录层次结构的、容量更大的文件系统。

    这种安装可以高达几级。也就是说,若干子文件系统可以并列安装到主文件系统上,也可以一个接一个地串连安装到主文件系统上。已安装的子文件系统也可从整个文件系统上卸下来,恢复安装前的独立状态。

    当超级用户试图安装一个文件系统时,Linux系统内核必须首先检查有关参数的有效性。VFS首先应找到要安装的文件系统。通过查找由 file_systems指针指向的链表中的每一个file_system_type数据结构,来搜索已知的文件系统(该结构中包含文件系统的名字和指向 VFS超级块读取程序地址的指针),当找到一个匹配的名字,就可以得到读取文件系统超级块的程序地址;

    接着要查找作为新文件系统安装点的VFS索引节点,并且同一目录下不能安装多个文件系统;VFS安装程序必须分配一个VFS超级块 (super_block),并且向它传递一些有关文件系统安装的信息;申请一个vfsmount数据结构(其中包括存储文件系统的块设备的设备号、文件系统安装的目录和一个指向文件系统的VFS超级块的指针),并使它的指针指向所分配的VFS超级块。

    当文件系统安装以后,该文件系统的根索引节点就一直保存在VFS索引节点缓存中。

    卸下文件系统的过程基本上与安装文件系统的过程相反。首先验证被卸文件系统是可卸载的,如果该文件系统中的文件当前正被使用,则该文件系统不能卸载;

    如果文件系统中的文件或目录正在使用,则VFS索引节点高速缓存中可能包含对应的VFS索引节点;如果相应的节点标志为“被修改过”,则该文件系统不能被卸载。完成以上过程,则释放对应的VFS超级块和安装点,从而卸下该文件系统。

    6.VFS索引节点缓存和目录缓存

    为了加快对系统中所有已安装文件系统的存取,VFS提供了索引节点缓存--把当前使用的索引节点保存在高速缓存中。

    为了能很快地从中找到所需的VFS索引节点,采用散列(Hash)方法。其基本思想是,VFS索引节点在数据结构上被链入不同的散列队列,具有相同散列值的VFS索引节点在同一队列中。设置一个散列表,其中每一项包含一个指向VFS索引节点散列队列的头指针。散列值是根据文件系统所在块设备的标识符和索引节点号码计算出来的,如图5所示。
    图5 散列结构示意图

  14. 晴 于 2005-09-21 18:24:41发表:

    4.Linux文件系统的逻辑结构

    Linux系统中每个进程都有两个数据结构用来描述进程与文件相关的信息,其中一个是fs_struct结构,它包含两个指向VFS索引节点的指针,分别指向root(即根目录节点)和pwd(即当前目录节点);

    另一个是files_struct结构,它保存该进程打开文件的有关信息,如图4所示。每个进程能够同时打开的文件至多是256个,分别由fd[0] ~fd[255]所表示的指针指向对应的file结构。前面在I/O重定向中用到的文件描述字(如0、1、2等)其实就是fd指针数组的索引下标。

    在file结构中,f_mode是文件打开的模式,如“只读”、“只写”、“读写”等;f_pos是文件当前的读写位置;

    f_flag包含许多标志位,用以表示文件的一些属性;f_count表示对该文件的共享计数;f_inode指向VFS中该文件的索引节点;f_op是指向file_operations结构的指针,该结构中包含了对该文件进行操作的各种例程。利用f_op可以针对不同的文件定义不同的操作函数。

    Linux系统进程启动时自动打开三个文件,即标准输入、标准输出和标准错误输出,它们的文件描述字分别是0、1和2。如果在进程运行时进行输入输出重定向,则这些文件描述字就指向给定的文件,而不是标准的终端输入/输出。

    每当进程打开一个文件时,就从files_struct结构中找一个空闲的文件描述字,使它指向打开文件的描述结构file。对文件的操作要通过file结构中定义的文件操作例程和VFS索引节点的信息来完成。

    图4 Linux文件系统的逻辑结构

  15. 晴 于 2005-09-21 18:23:34发表:

    不同的文件系统通过不同的程序来实现其各种功能。VFS定义了一个名为file_operations的数据结构,这个数据结构成为VFS与各个文件系统的界面。

    每种文件系统都有自己的file_operations数据结构,结构中的成分是指向文件操作的函数指针,例如,open是指向具体文件系统的打开文件的函数指针。这样,在VFS之上的用户程序中对文件的操作就不涉及具体的文件系统,经file_operations数据结构的转换才跳转到具体文件系统上。

    2.VFS超级块

    VFS和ext2文件系统一样也使用超级块和索引节点来描述和管理系统中的文件。每个安装的文件系统都有一个VFS超级块,其中包含以下主要信息:

    (1)设备标识符。这是存储文件系统的物理块设备的设备标识符,如系统中第一个IDE磁盘/dev/hda1的标识符是0x301。

    (2)索引节点指针。安装索引节点指针指向被安装的子文件系统的第一个索引节点;覆盖索引节点指针指向安装文件系统目录(安装点)的索引节点。根文件系统的VFS超级块中没有覆盖索引节点指针。

    (3)数据块大小。文件系统中数据块的字节数。

    (4)超级块操作集。指向一组超级块操作例程的指针,VFS利用它们可以读写索引节点和超级块。 (5)文件系统类型。指向所安装的文件系统类型的指针。

    (6)文件系统的特殊信息。指向文件系统所需要信息的指针。

    可以看出,VFS超级块的结构比ext2文件系统的超级块简单,主要增加的是超级块操作集,它用于对不同文件系统进行操作,对于超级块本身并无作用。

    3.VFS索引节点

    VFS中每个文件和目录都有一个且只有一个VFS索引节点。VFS索引节点仅在系统需要时,才保存在系统内核的内存及VFS索引节点缓存中。

    VFS索引节点包含的主要内容有所在设备的标识符、惟一的索引节点号码、模式(所代表对象的类型及存取权限)、用户标识符、有关的时间、数据块大小、索引节点操作集(指向索引节点操作例程的一组指针)、计数器(系统进程使用该节点的次数)、锁定节点指示、节点修改标识,以及与文件系统相关的特殊信息。

  16. 晴 于 2005-09-21 18:23:06发表:

    6.位示图

    图1中示出了每个块组包含一个块位示图和一个索引节点位示图。位示图(Bitmap)利用一串二进位的值来反映该块组中数据块的分配情况,也称作位向量(Bit Vector)法。

    位示图好象是一个很大的棋盘,每个盘格(一个二进位)对应着块组中的一个数据块,如果数据块是空闲的,则其对应位是0;如果数据块已经分配出去,则对应位是1。

    例如,设下列数据块是空闲的2、3、4、5、8、9、10、11、12、13、17、18、25、26、27……则块位示图的表示为100001100000011100111111000…… 块位示图的大小取决于块组的大小。

    当数据块的大小为1K字节,而块组的大小为8192时,该位示图恰好占一个数据块。在ext2文件系统中,用于索引节点的数据块数量取决于文件系统的参数,而索引节点的位示图则不会超出一个数据块。

    虚拟文件系统

    Linux系统可以支持多种文件系统,为此,必须使用一种统一的接口,这就是虚拟文件系统(VFS)。通过VFS将不同文件系统的实现细节隐藏起来,因而从外部看上去,所有的文件系统都是一样的。

    1.VFS系统结构

    图3示出VFS和实际文件系统之间的关系,从图中可以看出,用户程序(进程)通过有关文件系统操作的系统调用进入系统空间,然后经由VFS才可使用 Linux系统中具体的文件系统。也就是说,VFS是建立在具体文件系统之上的,它为用户程序提供一个统一的、抽象的、虚拟的文件系统界面。

    这个抽象的界面主要由一组标准的、抽象的有关文件操作构成,以系统调用的形式提供给用户程序,如read( )、write( )和lseek( )等。所以,VFS必须管理所有同时安装的文件系统。它通过使用描述整个VFS的数据结构和描述实际安装的文件系统的数据结构,来管理这些不同的文件系统。

    图3 VFS和实际文件系统之间的关系

  17. 晴 于 2005-09-21 18:22:15发表:

    图2的左部是索引节点(即I节点),其中含有对应文件的状态和管理信息。一个打开文件的索引节点放在系统内存区,与文件存放位置有关的索引信息是索引节点的一个组成部分,它是由直接指针、一级间接指针、二级间接指针和三级间接指针构成的数组。

    前12项作为直接指针。直接指针所指向的盘块中放有该文件的数据,这种盘块称为直接块。而一级间接指针所指向的盘块(间接块)中放有直接块的块号表。为了通过间接块存放文件数据,核心必须先读出间接块,找到相应的直接块项,然后从直接块中读取数据。

    二级间接指针所指向的盘块中放有一级间接块号表。同样,三级间接指针所指向的盘块中放有二级间接块号表。因此,利用前12个直接指针可以存放的文件不超过 12KB。如果文件大小超过12KB,则可以用一级间接指针;若更大,则可用二级间接指针;以此类推,使用三级间接指针最大的文件可以是16GB。

    5.ext2中的目录项

    在ext2文件系统中,目录文件包含有下属文件与子目录的登记项。当创建一个文件时,就构成一个目录项,并添加到相应的目录文件中。一个目录文件可以包含很多目录项,每个目录项(如ext2文件系统的ext2_dir_entry_2)包含的信息如下:

    (1)索引节点号。这是相应文件在数据块组中的索引节点号码,即检索索引节点表数组的索引值。

    (2)目录项长度。记载该目录项占多少字节。

    (3)名字长度。记载相应文件名的字节数。

    (4)文件类型。用一个数字表示文件的类型,例如,1表示普通文件,2表示目录,3表示字符设备文件,4表示块设备文件等。

    (5)文件名字。文件名(不包括路径部分)的最大长度为255个字符。

    每个目录的前两个目录始终是标准的“.”和“..”,它们分别代表目录自身和其父目录。当用户需要打开某个文件时,首先要指定该文件的路径和名称,文件系统根据路径和名称搜索对应的索引节点,找到该文件的数据块,从而读取文件中的数据。

    例如,要读取文件/home/mengqc/m1.c,文件系统首先按照超级块中根目录的索引节点找到根目录的数据块,从中找到表示home文件的目录项,得到相应的索引节点号码;接着在home所对应的索引节点中找到存放home数据块的地址,进而从相应的数据块中找到mengqc对应的目录项,得到相应的索引节点号码;再由mengqc目录文件中获取m1.c文件的索引节点号码,通过这个索引节点就可以访问m1.c文件了。

  18. 晴 于 2005-09-21 18:21:43发表:

    3.索引节点(Inode)

    索引节点又被称为I节点,每个文件都有惟一一个索引节点。ext2文件系统的索引节点起着文件控制块的作用,利用这种数据结构可对文件进行控制和管理。每个数据块组中的索引节点都保存在索引节点表中。数据块组中还有一个索引节点位示图,它用来记录系统中索引节点的分配情况----哪些节点已经分配出去了,哪些节点尚未分配。

    索引节点有盘索引节点(如ext2_inode)和内存索引节点(如inode) 有两种形式。盘索引节点存放在磁盘的索引节点表中,内存索引节点存放在系统专门开设的索引节点区中。

    所有文件在创建时就分配一个盘索引节点。当一个文件被打开,或者一个目录成为当前工作目录时,系统内核就把相应的盘索引节点复制到内存索引节点中;当文件被关闭时,就释放其内存索引节点。

    盘索引节点和内存索引节点的基本内容是相同的,但二者存在很大差别。盘索引节点包括文件模式、描述文件属性和类型主要内容。

    ◆文件属主信息,包括文件主标识号和同组用户标识号。

    ◆文件大小,即文件的字节大小。

    ◆时间戳,包括索引节点建立的时间、最近访问时间、最后修改时间等。

    ◆文件链接计数。

    ◆数据块索引表。利用多重索引表的结构存放指向文件数据块的指针。

    内存索引节点除了具有盘索引节点的主要信息外,还增添了反映该文件动态状态的项目,例如,共享访问计数(i_count),表示在某一时刻该文件被打开以后进行访问的次数。

    当它为0时,该索引节点被放到自由链中,表示它是空闲的;队列结构,通过几个list_head结构可以动态地链入到内存中的若干队列中,可加快检索索引节点的速度。

    4.多重索引结构

    普通文件和目录文件都要占用盘块存放其数据。为了用户使用方便,系统一般不应限制文件的大小。如果文件很大,那么不仅存放文件信息需要大量盘块,而且相应的索引表也必然很大。在这种情况下,把索引表整个放在内存是不合适的,而且不同文件的大小不同,文件在使用过程中很可能需要扩充空间。

    单一索引表结构已无法满足灵活性和节省内存的要求,为此引出多重索引结构(又称多级索引结构)。在这种结构中采用了间接索引方式,即由最初索引项中得到某一盘块号,该块中存放的信息是另一组盘块号;而后者每一块中又可存放下一组盘块号(或者是文件本身信息)。

    这样间接几级(通常为1~3级),最末尾的盘块中存放的信息一定是文件内容。ext2文件系统就采用了多重索引方式,如图2所示。

    图2 索引节点结构示意图

  19. 晴 于 2005-09-21 18:20:49发表:

    2.块组的构造

    从图1中可以看出,每个块组重复保存着一些有关整个文件系统的关键信息,以及真正的文件和目录的数据块。每个块组中包含超级块、组描述结构、块位图、索引节点(即I节点)位图、索引节点表和数据块。

    (1) 超级块(Superblock)

    超级块中包含有文件系统本身的大小和形式的基本信息,系统管理员可以利用这些信息使用和维护文件系统。每个块组都一个超级块。一般情况下,当安装文件系统时,系统只读取数据块组1中的超级块,将其放入内存,直至该文件系统被卸载。

    超级块中包含以下内容:

    ◆块数,用于安装时确认是ext2文件系统的超级块。

    ◆修订级别,这是文件系统的主版本号和次版本号。

    ◆安装计数和最大安装数,系统用来决定文件系统是否应该全面地进行检查。

    ◆块组号码,包含此超级块的数据块组的号码。

    ◆数据块大小,文件系统创建后数据块的大小就固定了,一般为1024、2048或4096个字节。

    ◆每组数据块的个数,即块组中数据块的个数,文件系统创建后它就固定了。

    ◆空闲块,文件系统中空闲块的个数。

    ◆空闲索引节点,文件系统中空闲索引节点的数目。

    ◆第一个索引节点,文件系统中第一个索引节点的号码。在ext2根文件系统中,第一个索引节点是根目录(/)的入口。

    (2) 块组描述结构(Block Group Descriptors)

    每个数据块组都有一个描述它的数据结构,即块组描述结构,其中包含以下信息:

    ◆数据块位示图,这一项表示数据块位示图所占的数据块数。块位示图反映出数据块组中数据块的分配情况,在分配或释放数据块时要使用块位示图。

    ◆索引节点位示图,这一项表示索引节点位示图所占的数据块数。索引节点位示图反映出数据块组中索引节点分配的情况,在创建或删除文件时要使用索引节点位示图。

    ◆索引节点表,数据块组中索引节点表所占的数据块数。系统中的每一个文件都对应一个索引节点,每个索引节点都由一个数据结构来描述。

    ◆空闲块数、空闲索引节点数和已用目录数。

    一个文件系统中的所有数据块组描述结构组成一个数据块组描述结构表。每一个数据块组在其超级块之后都包含一个数据块组描述结构表的副本。实际上,ext2文件系统只使用块组1中的数据块组描述结构表。