首页 > 开发 > 综合 > 正文

虚拟文件系统 (VFS) 简介

2024-07-21 02:37:49
字体:
来源:转载
供稿:网友

  前言
  
  linux是目前蛮热门的一个操作系统。很多人都知道它很是免费的,而且它也很稳定,更重要的是,它不会出现蓝色画面。可是,你知道吗? Linux所支持的档案系统高达十几个,除了为它量身打造的Ext2之外,它还支持了Minix,FAT,VFAT,NFS,NTFS…等等。 有没有想过,它是怎么做到使得可以同时支持十多个档案系统呢? 没错,就是VFS,也就是这篇文章的重点。在这篇文章里,我会跟各位介绍Linux档案系统的结构,VFS所扮演的角色。
  
  Linux档案系统结构
  图:http://linuxfab.cx/Columns/17/Image2.gif
  Linux的档案系统在外型上其实跟UNIX档案系统是一样的。它是一个反转过来的树。最上层是系统的根目录,也就是"/"。系统根目录底下可以是目录也可以是档案,目录里也可以包含目录,档案等。如此就形成一个反转过来的树。我们知道,在Windows,假如你有二个partition,一个叫C,另一个叫D。当你要到D这个partition时,只要打"D:"就可以了。但是在Linux里可不是这样。要去读取另一个partition的资料必须要经由mount的动作。像
  
  mount -t ext2 /dev/hda3 /mnt
  
  就会将硬盘第三个partition挂在/mnt这个目录底下。Mount完之后,/mnt原本的内容会看不到,只会看到hda3里的内容。其中/mnt我们称为hda3的mount point。而/mnt这个目录则是被hda3所cover。经过mount以后,我们就可以经由/mnt去读取hda3的内容,就好象hda3的内容本来就放在/mnt底下一样。整个过程,如图1所示。
  
  图1(a)是原本的档案结构,图1(b)则是hda3这个partition的内容,将hda3 mount到/mnt之后,整个档案系统就变成图1(c)的样子。不管如何,Linux会保持其档案系统为一个tree的外形。这样mount下去,我们很轻易可以推想到,从根目录开始的这个tree很有可能包含好几种的档案系统,可能挂在/mnt上的是Ext2档案系统,挂在/home上的是FAT,而挂在/cdrom上的则是iso9660档案系统。我们知道,当使用者去读取这些目录里的内容时,他本身是不用去管这个目录挂的档案系统是什么。基本,使用者也不会感到有什么不同。而就PRogrammer的观点来看,我们也不会说去读/mnt里的档案和去读/home里的档案要下不同的参数。Linux是怎么做到这一点的呢? 它就是利用VFS来做到的。
  
  1. VFS架构
  
  Linux档案系统其实可以分为三个部分,第一部分叫Virtual File System Switch,简称VFS。这是Linux档案系统对外的接口。任何要使用档案系统的程序都必须经由这层接口来使用它。另外二部分是属于档案系统的内部。其中一个是cache,另一个就是真正最底层的档案系统,像Ext2,VFAT之类的东西,整个Linux档案系统可以用图2来表示
  
  为了避免困扰,底下我们所讲的档案系统都是指Ext2,FAT等底层的档案系统,至于包含VFS,Ext2,Buffer Cache等等我们总称为VFS。
  
  在图2里,我们可以清楚的看到当Kernel要使用档案系统时,都是经由VFS这层接口来使用。刚才我们有提到一个问题,就是当使用者或程序设计师去读取一个档案的内容时,它不会因为这个档案位于不同的档案系统就需要使用不同的方式来读取。因为这件事VFS已经帮我们做了。当我们要读取的档案位于CDROM时,VFS就自动帮我们把这个读取的要求交由iso9660档案系统来做,当我们要读取的档案在FAT里时,VFS则自动呼叫FAT的函式来帮我们做到。当然,有需要时,VFS也会直接透过Disk driver去读取资料。但是当我们要求读写档案时,难道iso9660或FAT档案系统会直接透过driver去读写吗? 不是的。就像PC上除了内存之外,还有一层的cache来加快速度,在Linux档案系统其实也是有一个Cache的机制以加快速度。叫做Buffer Cache。底层的档案系统要读写磁盘上的资料时都要经过Buffer Cache。假如资料在Buffer Cache里有的话,就直接读取,假如没有的话,才透过Buffer Cache要求driver去读写。除了Buffer Cache之外,其实,Linux档案系统里还有一个Cache,叫Directory Cache。你知道吗? 假如我们去统计使用者的行为的话,ls这种命令其实占的比重是蛮大的。每次的ls或读写档案其实都要对目录的内容做search。因此,假如在目录这方面能做个Cache的话,那系统整统的速度就会再往上提升。Directory Cache的功能就在此。其实,Linux档案系统里还有一个Cache,叫Inode Cache。故名思义,它是针对Inode做的Cache。Directory Cache跟Inode Cache其实关系是很密切的。
  
  
  2. 档案的表示
  
  从使用者的观点来看,我们可以用档案的绝对路径来代表某个档案而不会出错。在VFS里,它并不是用路径来代表档案的。它是用一个叫Inode的东西来代表的。基本上,档案系统里的每一个档案,系统都会给它一个Inode,只要Inode不一样,就表示这二个档案不是同一个,假如两个的Inode一样,就表示它们是同一个档案。其实,Inode是VFS所定义的,而我们知道VFS里包含了好几种的档案系统,并不是每一个档案系统都会有Inode的这种概念。
就像FAT,事实上它跟本没有所的Inode概念。但是当VFS要求FAT去读取某个档案时,事实上它是把那个档案的Inode传给FAT去读。所以,在VFS来讲,每一个档案都有其对应的Inode,但是在底层的档案系统不见得是这种情形。因此,VFS跟底层的档案系统沟通也是经过一层的接口。比方说,VFS要open一个位于FAT的档案时,VFS会配置一个Inode,并把这个Inode传给FAT,FAT要负责填入一些资料到Inode里,必要时,也可以在Inode里加入自己所需要的资料。再打个比方,VFS要读取FAT里档案的内容,反正VFS就是把Inode给FAT,并且告诉它从那里开始读,读多少个byte。其余的就是FAT的事,它就要想办法读出来。用"上有政策,下有对策"这句话来描述VFS跟底层的档案系统的互动可能还蛮适合的。VFS的政策就是要以Inode为单位,但是底层的档案系统还是照自己的方式去存放档案,只要表面上将Inode填好,VFS要的东西给他就行了。
  
  在VFS里,Inode是档案的单位,那档案系统呢? 在VFS里底层的档案系统又是用什么来表示呢? 要讲这个之前,我们先来讲讲硬盘的layout。
  
  
  3. Disk的Layout
  图:http://linuxfab.cx/Columns/17/Image3.gif
  
  在这里所讲的disk是指硬盘,有关于floopy disk则不讨论。我们知道一颗硬盘最多可以有8个partition,其中4个是primary partition,另4个则是extended partition。所谓partition就是在逻辑上将disk做切割。所以,我们可以把一颗硬盘想象成是由最多8个partition所组成的。除了partition之外,disk的第一个扇区我们称为MBR。如图3所示
  http://linuxfab.cx/Columns/17/Image4.gif
  
  在Linux里,档案放的位置是以一个partition为单位的,也就是说一个档案系统是放在partition里,而不能跨partition的。接下来让我们来看看档案系统在partition里的layout是怎么样的,如图4所示。
  
  基本上,第一个block是boot block。用来开机用的。Super block则记录了档案系统重要的资料,接下来的东西就是记录着inode和资料的区块。
  
  在VFS里,每一个档案系统是由其super block来表示的。之所以这样,是因为super block里存放了这个档案系统重要信息。从一个档案系统的super block就可以存取这个档案系统中的任何档案。因此,在Linux里,档案系统的治理以super block为单位,从super block可以取得这个档案系统里任何一个档案的inode,从档案的inode则可以对这个档案做读写的动作,进而完成对Linux底下档案的控制。因此,Kernel分别定义了一个super_block和inode的结构来描述档案系统的super block和及inode。底下我们就分别来介绍super block和inode的结构。
  
  Super block结构
  
  super_block结构定义在里,整个结构可分为基本资料,一组用来使用super block结构的函式,一组跟quota治理有关的函式,治理super block所属档案系统inode的信息,一些字段用来做super block的synchronization,以及各个档案系统本身所特有的资料。
  
  4. 基本资料
  strUCt list_heads s_list;
  kdev_t s_dev;
  unsigned long s_blocksize;
  unsigned char s_blocksize_bits;
  unsigned char s_rd_only;
  unsigned char s_dirt;
  struct file_system_type *s_type;
  unsigned long s_flags;
  unsigned long s_magic;
  unsigned long s_time;
  struct dentry *s_root;
   
  以上这些字段是我认为super_block结构里属于基本资料的部分,在这里,我没有依照原始程序的写法依序将字段列出来,而是将相关的整理在一起。s_list这个字段是用来将super block串在一起的。在Linux里,同一时间Kernel可能会拥有好几个档案系统的super block,因此,它有它自己一套的super block治理方式,平常也许我们会另外写一个linked list,里面用一个字段存放super block,用这种方式把super block串在一起,但是,Kernel不是这样做,它也是用一个串行来把super block放在一起。但是,它把它写到super block结构里,s_list就是用来将super block串起来的。用法跟一般人写法不同,在super block的治理我将为各位介绍。
  
  s_dev是此super block所属档案系统所在的device代码。档案系统内部的治理不是用档案做单位,而是以block为存取的单位,而s_blocksize就是用来记录一个block是几个byte。因此,假如一个block是1024 byte的话,那s_blocksize为1024,而s_blocksize_bits就是10,这个字段是指一个block需要几个bit来表示。而s_rd_only从字面上来看应该是记录档案系统或super block是否只读,目前这个字段是被设为0,还没有被使用。至于s_dirt则是记录此super block的内容是否被改过,用来判定是否最后要将super block写回disk里,

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表