zoukankan      html  css  js  c++  java
  • 简单虚拟文件系统的设计与实现【转】

    本文转载自:https://blog.csdn.net/chenxugl/article/details/7007201

    虚拟文件系统(VFS)是由Sunmicrosystems公司在定义网络文件系统(NFS)时创造的。它是物理文件系统与服务之间的一个接口层,是对整个操作系统每个文件系统的抽象,因此,严格的说,它并不是具体的文件系统。它存在于内存中,不存在任何外存空间。

           无意中阅读到云风在github上的Windsoul源码,感受到了高手代码的扎实,也体会到了思考后的设计之美。虽然早前Linux已实现了VFS,但对于初学者来说过于复杂且需要丰富的阅读Linux源码的经验,每每想到剖析某文件系统的痛苦,内心总有些许气馁。通常情况下,阅读代码往往比写代码来得困难得多。因此,培养良好的阅读兴趣,懂得欣赏他人优秀的设计艺术显得尤为重要了。

    一、初始化

            阅读代码的关键在于确定在何处开始阅读。大部分时间内我们选择从代码的初始化开始。

    虚拟文件系统虽然不同于具体文件系统,但基本原理是一致的,包括初始化。对于一个文件系统,要想使用它,必须得先知道它的根目录。Windsoul是通过VfsInit函数进行初始化的。如下

    [cpp] view plain copy
     
    1. Int VfsInit(void)  
    2.   
    3. {  
    4.   
    5.       vfsMount(“_”,”native”,”.”);  
    6.   
    7. }  



    函数vfsMount的主要流程是首先取得”.”的绝对路径,然后为nativefs结构体变量fs分配内存,fs->sz的值为该路径的字符串大小,fs->root存储该路径。好,我们先放下源码仔细思考一下,既然我们保存了某个路径的信息,我们该如何操作它呢?答案是多种的,可读、可写、可定位等等。如果将这些信息进行汇编,就可以得到一个文件系统的基本数据结构。云风是这么定义的:

    [cpp] view plain copy
     
    1. struct filesystem {  
    2.   
    3.        void * fs;  
    4.   
    5.        int (*list)(void *fs,const char *name, atom *buffer, int sz);  
    6.   
    7.        int (*chsize)(void *fs,const char *name, size_t sz);  
    8.   
    9.        int (*create)(void*fs,const char *name, char mode);  
    10.   
    11.        size_t (*size)(void *fs, const char *name);  
    12.   
    13.        int (*read)(void *fs ,const char *name, size_t pos, void *buffer, int sz);  
    14.   
    15.   int (*write)(void *fs , const char *name,size_t pos, const void *buffer, int sz);  
    16.   
    17. };  



    其中fs指向的是刚才填充的nativefs类型变量fs。

    vfsMount函数会将struct filesystem * fs进行填充:

    [cpp] view plain copy
     
    1. fs = memoryPermanent(sizeof(*fs));  
    2.   
    3.              memset(fs,0,sizeof(*fs));  
    4.   
    5.              fs->fs = f;  
    6.   
    7.              fs->list = nativefsList;  
    8.   
    9.              fs->create = nativefsCreate;  
    10.   
    11.              fs->read = nativefsRead;  



    接着,会调用void pathMount(const char * root, struct filesystem *fs)函数,将刚才的fs作为实参传进去。这个函数将实现文件的挂载。

    我们现在看看他是怎样实现的。首先,他维护了一个struct map *g_fs全局变量:

    [cpp] view plain copy
     
    1. struct map {  
    2.   
    3.       int freenode;  
    4.   
    5.       int size;  
    6.   
    7.       struct node* buffer;  
    8.   
    9. };  



    Freenode指向可用的文件节点首地址,size表示,buffer则指向了一片空间,该空间存储着各个文件节点。而这些节点的数据结构是:

    [cpp] view plain copy
     
    1. struct node {  
    2.   
    3.       struct value *value;  
    4.   
    5.       struct key *key;  
    6.   
    7.       struct node *next;  
    8.   
    9. };  



    通过这个结构体可知,各个文件节点形成了一个单链表。Key存储的是路径的哈希值,value指向的是该文件节点的struct filesystem类型的文件句柄。

    因此,pathMount函数的实现如下:

    [cpp] view plain copy
     
    1. voidpathMount(const char * root, struct filesystem *fs)  
    2.   
    3. {  
    4.   
    5.       struct map_op op;  
    6.   
    7.       op.op = MAP_INSERT;  
    8.   
    9.       op.key.p = atomString(root);  
    10.   
    11.       op.value = fs;  
    12.   
    13.       void * old = mapSearch(g_fs,&op);  
    14.   
    15.       if (old!=NULL) {  
    16.   
    17.              logFatal("%s is alreadymounted.",atomToString(op.key.p));  
    18.   
    19.       }  
    20.   
    21. }  



    函数mapSearch是将文件句柄插入到buffer指向的哈希表中。原理图如下:

    二、vfsOpen函数操作

    既然我们已经得到了一个系统初始化的结构体,那么操作就变得简单了。这里只是拿vfsOpen函数作示范,其他操作类似,不在赘述。

    假设我们要在当前目录下新建一个文件“a/b.dat”,那么我们该怎么做呢?当然我们先得到b.dat文件名,其次是得到目录a。Windsoul维护了两个哈希表,一个为上图所示,一个为存储目录的哈希表,下图所示:

    文件夹a首先跟struct hash_string中的str值进行比较,也就是跟最近使用的目录比较。如果相等,则返回该哈希表项;如果不成功,则将a填充到str里,作为当前使用的目录。最后,将a重新进行一次哈希,在全局变量g_fs维护的哈希表中找到其所对应的struct filesystem fs句柄,最后进行具体的文件操作。

    [cpp] view plain copy
     
    1. struct file {  
    2.   
    3.       struct filesystem *fs;  
    4.   
    5.       const char * name;  
    6.   
    7.       size_t size;  
    8.   
    9.       size_t pos;  
    10.   
    11.       int mode;  
    12.   
    13. };  
    14.   
    15.    
    16.   
    17. struct file *vfsOpen(atom pathname, const char * m)  
    18.   
    19. {  
    20.   
    21.       const char *name=NULL;  
    22.   
    23.       struct filesystem * fs = pathGet(pathname,&name);  
    24.   
    25.       if (fs == NULL)  
    26.   
    27.              return NULL;  
    28.   
    29.       int mode = 0;  
    30.   
    31.       if (strchr(m,'r')) {  
    32.   
    33.              mode |= MODE_READ;  
    34.   
    35.              if (fs->read == NULL)  
    36.   
    37.                     return NULL;  
    38.   
    39.       }  
    40.   
    41.       if (strchr(m,'w')) {  
    42.   
    43.              mode |= MODE_WRITE;  
    44.   
    45.              if (fs->write == NULL)  
    46.   
    47.                     return NULL;  
    48.   
    49.       }  
    50.   
    51.       if (mode & (MODE_WRITE|MODE_READ)) {  
    52.   
    53.              if(!fs->create(fs->fs,name,'q')) {  
    54.   
    55.                     if(!fs->create(fs->fs,name,'c')) {  
    56.   
    57.                            return NULL;  
    58.   
    59.                     }  
    60.   
    61.              }  
    62.   
    63.       } else if (mode & MODE_WRITE) {  
    64.   
    65.              if(!fs->create(fs->fs,name,'c')) {  
    66.   
    67.                     return NULL;  
    68.   
    69.              }  
    70.   
    71.       } else if (mode & MODE_READ) {  
    72.   
    73.              if(!fs->create(fs->fs,name,'q')) {  
    74.   
    75.                     return NULL;  
    76.   
    77.              }  
    78.   
    79.       } else {  
    80.   
    81.              return NULL;  
    82.   
    83.       }  
    84.   
    85.    
    86.   
    87.       size_t sz = fs->size(fs->fs,name);  
    88.   
    89.       if (sz == NONE_EXIST) {  
    90.   
    91.              return NULL;  
    92.   
    93.       }  
    94.   
    95.       struct file *f = memoryAlloc(sizeof(structfile));  
    96.   
    97.       f->fs = fs;  
    98.   
    99.       f->name = name;  
    100.   
    101.       f->size = sz;  
    102.   
    103.       f->pos = 0;  
    104.   
    105.       f->mode = mode;  
    106.   
    107.       return f;  
    108.   
    109. }  



    关键操作是pathGet函数。它的功能就是实现在g_map中修改其当前目录,并在g_fs找到struct filesystem类型的文件句柄,最后产生一个文件。

    三、其他

    Windsoul的其他操作无非是修改g_map所对应的当前目录字符,这里就不一一阐述了,而整个文件系统是对读写文件的一次包装,这样有利于安装我们常人的思维操作文件,这个概念跟内存管理(如内存池)是一致的。

    如果在细细回味这些代码,你会发现一些具体力道的设计。当然这些设计是有根据的,比如整个项目中都穿插这原子(Atom)的概念,其实这里是引自《C语言接口与设计》的某些内容。这里给我们的启示是平时多阅读代码,起码多阅读一些书籍,并且学以致用,有自己的思考,只有这样才能达到高手的境界。

  • 相关阅读:
    【转】Android——设置颜色的三种方法
    Eclipse Android安装APP时覆盖安装问题
    自定义数组,实现输出改数组的长度、最大值和最小值
    用程序实现对数组a[45,96,78,6,18,66,50]中的元素进行排序
    PHP面试题2
    PHP面试题
    gulp
    移动端base.css
    笔记
    mouseover和mouseout事件在鼠标经过子元素时也会触发
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/9210342.html
Copyright © 2011-2022 走看看