分享
 
 
 

虚拟文件系统 (VFS) 简介

王朝other·作者佚名  2006-11-24
窄屏简体版  字體: |||超大  

前言

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里,当super block被更动之后,s_dirt会被设为1。s_type的型别是file_system_type,这是一种来描述档案系统的结构,在这里,是用来记录这个super block是属于那一个档案系统。有关这种型别,我们将会在super block的管理中探讨。当我们使用档案系统时,第一步就是要做mount的动作,在mount的时候,还需要给它参数,像是mount成只读或可擦写等,这些参数就是记录在s_flags里。在Linux或UNIX里,magic number通常是用来做识别用的,而档案系统的magic number就是设在s_magic字段里,像目前Ext2的magic number就是0xEF53。从档案系统的super block我们可以读取到这个档案系统任一个档案,但是,前提是我们必须要先知道这个档案系统的根目录在那里才可以。就像给我们一个绝对路径我们可以找到那个档案,但是,找的方式是先从根目录,再往下层去找。因此,super block必须记录它所代表的档案系统根目录在那里,这就记录在s_root里。

5. super block的synchronization

unsigned char s_lock;

struct wait_queue *s_wait;

上面这两个字段是用来做super block的synchronization。s_lock记录着目前super block是否被锁住,如果是,其值为1,若不是,则为0。s_wait是一个wait queue的结构,被放到queue里行程将会进入sleep的状态,直到被叫醒为止。基本,如果要改变super block的内容,需要先呼叫lock_super()锁住super block以免产生race condition。改完之后则要呼叫unlock_super()将lock释放掉。而lock_super()跟unlock_super()就是利用这两个字段来做的。

6. 管理Quota的函式

这个字段所记录的是一组的quota管理函式,档案系统本身可以自己提供一套的quota管理函式,不过,很幸运的是VFS本身已经提供了一组通用的quota管理函式。目前Linux的档案系统中只有ext2有支持quota管理,同时,ext2是直接使用VFS所提供的函式。VFS所提供的这组函式定义在里,存放在dquot_operations变量里。如果档案系统要使用quota的函式时,必须先将dq_op这个字段填好,但是,要注意的是,dq_op这个字段并不是在super block初始化时填入的,它是当档案系统已经mount好,才在由激活quota的程序填入的,这一点跟其它字段倒是不太相同,在Linux里,dq_op的填入是经由quotaon这个程序去呼叫quotactl()这个系统呼叫去做的。

7. 储存档案系统本身资料的字段

super_block结构是所有档案系统所共同使用的一个结构,但是,除了共同的部分之外,档案系统之间也有着相当的差异性,因此,为协调此差异性,在super_block结构有一个字段是专门来存放各个档案系统所独自享有的信息。这些信息不用说当然是在呼叫档案系统提供的read_super()时所填入的。在Kernel 2.2.1里,这个字段是这样子的

union {

struct minix_sb_info minix_sb;

struct ext2_sb_info ext2_sb;

struct hpfs_sb_info hpfs_sb;

struct hfs_sb_info hfs_sb;

struct adfs_sb_info adfs_sb;

struct qnx4_sb_info qnx4_sb;

void *generic_sbp;

} u;

因为每个super_block在同一时间内最多只会记录一个档案系统的资料,所以,这个字段是union。像ext2_sb就是专门存放ext2档案系统本身所额外需要的信息,由ext2_read_super()函式填入的。

8. 管理inode的字段

一个档案系统里有许多的inode,但是,有的inode可能因为使用者的关系,其内容被更改,此时,我们称此inode为dirty。所有的dirty inode都应该被记录,以便在适当时候写入disk,而这个存放的位置在super block是蛮适合的。每一个super_block代表一个档案系统,把这个档案系统的dirty inode记录在它自己的super_block中应该是不错的想法。在Kernel 2.2.1中在super_block里跟inode有关的字段有4个。

struct inode *s_ibasket;

short int s_ibasket_count;

short int s_ibasket_max;

struct list_head s_dirty;

其中s_dirty就是用来存放dirty inode用的。我们可以看到s_dirty的型别跟s_list的型别是一样的,所以,其实s_dirty也是一个串行。每一个inode结构里都有一个struct list_head结构的字段,s_dirty的工作就是将这个dirty inode的struct list_head结构的字段串起来,最后我们经由super_block的s_dirty就可以读取到这些dirty inode里list_head结构字段的地址,然后再经由这些字段读取到其对应的inode的地址。

除了s_dirty之外,还有三个字段是跟inode有关的,分别是s_ibasket,s_ibasket_count,s_ibasket_max。有关于这三个字段的用法目前我还不是很清楚,只是大概知道当档案系统的剩余空间太少时,Kernel会根据这三个字段的值呼叫一个callback的函式来做些处理。使用者可以在register_file_system()时将FS_IBASKET的参值传给它,系统就会激活这项功能,但是,很可惜的是,在Kernel 2.2.1中,这项功能还有bug,所以尚未正式使用。

9. 操作Super block的函式

super block里有一个字段是用来记录一组的函式,这个字段的型别是super_operations。这个结构在Kernel 2.2.1里包含了11个函式指针。这些指针是要让VFS来呼叫的。因此,这是VFS和档案系统之间的一个接口,经由这层接口,super block可以控制档案系统底下的档案或目录。

struct super_operations *s_op;

在super_block结构里,s_op就是用来记录这一组的函式。这组函式必须由写档案系统的人来提供。底下我们就来看看super_operations里各个函式应该要提供什么样的功能。

struct super_operations {

void (*read_inode) (struct inode *);

void (*write_inode) (struct inode *);

void (*put_inode) (struct inode *);

void (*delete_inode) (struct inode *);

int (*notify_change) (struct dentry *,struct iattr *);

void (*put_super) (struct super_block *);

void (*write_super) (struct super_block *);

int (*statfs) (struct super_block *,struct statfs *,int);

int (*remount_fs) (struct super_block *,int *,char *);

void (*clear_inode) (struct inode *);

void (*umount_begin) (struct super_block *);

};

不知各位有没有发现,在这11个函式里,居然没有一个函式是用来读super block的。其实也没什么好奇怪的,因为读取super block的函式早在注册档案系统时就要给了,要不然,是谁把super block读出来。因此,我们可以发现super_operations里只有write_super(),put_super()等等,而没有read_super()。以下就分别来讨论各个函式应该提供什么样的功能,如果你想自己写一个档案系统,这部分可是很重要的喔。

· write_super(sb)

故名思义,这个函式主要就是用来将sb这个super block写到磁盘上的。在正常情况下,write_super()应该要检查sb->s_dirt是否为True,只有当s_dirt为True时才将super block写回disk里。当然,我想这部分主要还是看各个档案系统是如何的implement,但是,记得write_super()最后应该要将s_dirt设为0,表示这个super block不再是dirty。还有一件事,就是write_super()应该要检查档案系统是否被mount成只读(检查sb->s_flags&MS_RDONLY),或档案系统本身就是只读,像iso9660档案系统,在这种情况下,由于系统是只读,所以,系统设计者可以不提供write_super()或让write_super()不做事。

· put_super(sb)

当档案系统被umount时,VFS就会呼叫档案系统的put_super()。所以,put_super()要做的事就是将super block所配置的buffer释放掉。此外,一般来讲,如果档案系统是写成module的话,通常在put_super()也会呼叫MOD_DEC_USE_COUNT将module的reference count减一。至于MOD_INC_USE_COUNT则是应该在read_super()时做的。除此之外,有点要注意,有的人会认为put_super()应该要将sb释放掉,但是,根据Kernel 2.2.1版的原始码看来,这部分的工作是由VFS来做的。(所谓释放掉并不是呼叫kfree()将super block所占的内存释放,而是将sb->s_dev设成0而已。VFS会自动将s_dev为0的super block视为空的,而拿来重复使用。)

· read_inode(inode)

我想大家看名字就知道了,read_inode()就是去读一个inode,并将它放到传过来的inode结构里。在VFS里,read_inode()只会被get_new_inode()呼叫,而get_new_inode()又只会被ige

[1] [2] [3] [4] 下一页

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有