使用B+树作为文件系统的主要数据结构,用来储存文件描述符,文件描述符用来储存文件的具体信息(在磁盘上的位置,大小,时间等)。

文件描述符参考了FAT32中用来描述文件信息的结构,但有较大的区别。每个文件描述符32占用字节,分为两种:用于描述符文件信息的描述符和用于储存文件名的描述符,两种结构以及相关结构如下:

//文件属性
typedef struct {byte dpl : 2;            //文件权限;分区权限 0,1,2,3byte extname : 1;        //下一项描述符类型,=0,文件描述符;=1,扩展文件名描述符byte isext : 1;            //本描述符是否为扩展描述符,=1byte data : 1;            //=1文件正常,=0此文件可能存在问题;作为分区描述符时,=1说明此分区是系统分区,byte en_folder : 1;        //说明floder项有效,以此文件名作为后面文件的目录基准,同目录下的文件应该只有一个文件的此项为1byte hide : 1;            //=1隐藏文件;=1隐藏分区byte del : 1;            //=1已删除文件,相当于放入回收站,标记删除,为了保证可以恢复;=1分区删除
}fileAtt;//文件日期结构
typedef struct {uint32 s : 6;        //秒uint32 m : 6;        //分uint32 h : 5;        //时uint32 day : 5;        //日uint32 month : 4;    //月uint32 year : 6;    //年,使用是将此值加上基准年份等于时间
}fDate, * _fdate;//用于描述符文件信息的文件描述符
typedef struct {byte ms : 7;            //创建时间的10毫秒位,ms*10=大约的创建时间,现在感觉这项没什么用byte dis : 1;            //=0扩展文件名描述符,=1文件描述符,此项恒等于1fileAtt fatt;            //文件属性char name[FTNAME_SIZE];            //文件名,占用6字节fDate createDate;        //创建日期fDate lastVisitDate;    //最后访问日期fDate lastModifiedDate;    //最后修改日期uint32 size;                //文件长度,单位4kb,因此一个文件最大为4096GB=4TBuint32 position;            //在分区内的偏移位置,单位4kb,最大检索16tb,因此一个分区的最大为16tbuint16 offset : 12;        //文件占用的最后一个4kb内的偏移uint16 extnum : 4;            //扩展描述符数量,最大为15个,当此值=15时,应当查看最后一个扩展描述符,确定是否还有扩展描述符uint16 folder;                //文件夹包含数量,如果fatt.en_folder=1,说明此文件描述符组为文件夹描述符,则folder包含了该文件夹的文件数量
}fileTable, * _filetable;//用于储存文件名的文件名描述符
typedef struct extName {byte size : 5;          //本项文本串长度byte ext : 1;          //1=有扩展项,此项为了避免文件描述符指出的15项的限制,以期能够储存更多文件项byte start : 1;            //=1起始项,为扩展文件名描述符组的首项byte dis : 1;          //=0扩展文件名描述符,=1文件描述符,此项应该恒等于0char name[31];            //文件名
}extName, * _extname;//文件项联合体,一个文件项可能是文件描述符或者扩展文件名描述项
typedef union {fileTable ft;            //文件描述符extName en;          //扩展文件名描述符
}fileItems, * _fileitems;

在内部节点中,所有描述符均为文件名描述符,因为B+树的内部节点只是用来帮助查找文件,并不储存文件信息。

在叶结点中,文件名描述符跟在文件描述符后面(如果文件名太长的话),由于一个文件描述符储存的信息有限,因此做了以下规定:

1.如果时间太大,则使用多个描述符叠加的方法,比如文件描述符的创建时间createDate项,基准年份假定为2020,创建年份为2100,因此createDate.year项储存的值应当为80,但是日期结构最大只能储存64,因此需要将80二进制形式的低6位放进第一个描述符,然后第二个描述符储存剩下的高位。如果还存不进可以以此类推,几乎可以储存无限长的时间。

2.描述符的size、position、offset项,这两个项一般不存在值太大的情况,因此不可叠加,但由于文件可能是是分散储存的,这种情况下需要使用多个描述符来储存这三项。一组连续的用来描述同一个文件的文件描述符成为文件描述符组,最大的情况由1个首文件描述符、15个扩展文件描述符、15个扩展文件名描述符构成,因此一个文件最多允许使用31个描述符来表示自己,最多允许将一个文件拆成16个位置来储存,不够的话就需要试着整理磁盘碎片了(当然,我也设想过可以把一个文件拆成多个子文件来储存,但这样可能会降低效率,还是使用强制性的规定比较好)

3.folder项用来储存文件夹内包含的一级文件数量(文件夹内的第一层文件),只有在fatt项的en_folder属性=1时才有效,此时文件描述符组储存的时文件夹,无用的项要置空。

4.每一个文件描述符组对应一个文件或文件夹,文件名是绝对路径,例:"test/a.txt",表示在根目录的test文件夹下的a.txt文件。这样做是为了方便计算机索引磁盘文件。

5.由于是绝对路径,所以文件夹描述符组后面若干个描述符组就是该文件夹内部的文件,文件夹操作便需要逐个读取其后面的文件描述符组,因此当一个文件夹内部包含的文件过多(特别是文件夹过多),会使展示文件夹列表变得非常慢,我暂时没想都解决办法。

6.文件描述符组在节点内部以文件名顺序进行排序。

由于B+树的结构,使节点具有两种类型,叶节点和内部节点。

叶节点大小为80kb,可以储存2409个文件描述符,除了是B+树的一部分,所有叶节点也可以组成一个双向循环链表,第一个节点储存了文件名最小的文件描述符,被称为首叶节点,按照B+树的删除插入方式,正常情况下第一个叶节点不会改变。当然,以后重写代码的时候可能会修改。

内部节点大小为52kb,可以储存1401个文件名描述符,用来索引叶节点。

结构如下:

//B+树内部节点,52kb
#define BNODE_NUM 1401
#define BNODE_SIZE 52*1024
#define CHILD_TYPE_BNODE 0  //childType属性,内部节点类型
#define CHILD_TYPE_LNODE 3  //childType属性,叶节点类型
typedef struct BTreeNode {extName name[BNODE_NUM];          //文件名描述符数组uint32 child[BNODE_NUM + 1];     //子节点指针数组,可能为内部节点或叶节点,使用时,要强制转换为结点指针后使用uint16 name_off[BNODE_NUM - 1];     //记录每个文件名的起始下标,从第二个文件名开始uint32 parent;          //父节点指针uint16 name_off_num:14;              //记录name_off数组的有效长度uint16 childType:2;                  //子节点类型,0=内部节点,3=叶节点uint16 namenum;                     //记录name数组的长度
}BTreeNode, * _btreenode, * _bn;//B+树叶节点
#define LNODE_NUM 2409
//叶节点,80kb
#define LNODE_SIZE 80*1024
typedef struct LeafNode {fileItems fi[LNODE_NUM];           //文件描述符数组uint16 file_off[LNODE_NUM - 1];        //文件偏移数组,描述文件的起始描述符位下标,由于第一个文件项的下标肯定为0,因此从第二个文件描述符开始。uint16 finum;                     //fi数组有效项的长度uint16 file_off_num;                //file_off数组有效项的数量uint32 prev;              //前驱节点uint32 next;              //后继节点uint32 parent;            //父节点指针
}LeafNode, * _leafnode, * _ln;

两种节点大小不一样就是为了4kb对齐,52kb和80kb为最小公倍数,如果太大,在写入磁盘时会写的太多,也会影响查找效率。同时,由于硬盘的同一个扇区如果被写入次数太多,会缩短扇区寿命,特别是固态硬盘,是有写入次数限制的,因此,节点在被读取后,只有发生修改,才可以写入硬盘。我也想过是否应该在某个节点写入达到一定次数之后,重写到其他地方,目前没有实现。

另一点,由于所有文件名不可能是一样长的,所以B+树中的数组并非等长的,因此设计了偏移数组,来索引每个文件描述符组的第一个描述符。但是为了减少占用储存空间,偏移数组不索引描述符数组第一个元素,因为肯定是从0开始的,不过现在想真的不怎么值得,让程序变得更加复杂了。

数据管理,如何确定那个扇区写入了数据,哪些扇区是空的?我决定参考linux下的ext文件系统的实现机制,使用位图来表示,0代表空,1代表被占用或者不可用(损坏的),将读写单位划分为块,每个块由若干个扇区组成,必须是2幂次方,一个位代表一个块。将每个位图放在块组的最后面,一个跨组的大小为一个块的字节数*8块,即如果一个块的大小为512字节,则块组的大小为512*8=4096个块。

对于引导扇区,也是参考fat32的设计,不过增加了一些,结构如下:

//引导扇区结构
//512BYTES
#define BOOTCodeSize (512-64-2-82)
typedef struct BOOTLoder {/*0*/ uint8 jmpBOOT[3];           //跳转代码/*3*/ char OEM[8];                //此文件系统开发者名字/*11*/  uint16 BytePerSec;          //每扇区字节数/*13*/  uint8 Unit;                 //文件系统的单元,为BytePerSec的倍数,例:若UNIT=8,BytePerSec=512,则文件系统的单位为4kb/*14*/  uint16 ResvdSecCnt;         //BOOT记录占用的单元数/*16*/    uint8 Resvered0;/*17*/  uint16 RootEntCnt;          //根目录文件最大数/*19*/    uint16 TotUnit16;           //单元总数/*21*/    uint8 Media;                //介质描述符/*22*/   uint16 BlockSize;           //每个块占用的单元数,最大为32MB/*24*/   uint16 SecPerTrk;           //每个磁道的扇区数/*26*/    uint16 NumHeads;            //磁头数/*28*/ uint32 HiddSec;             //隐藏单元数/*32*/   uint32 TotUnit32;           //如果TotUnit16=0,则由这里给出单元总数/*36*/   uint8 DrvNum;               //驱动器号/*37*/    uint8 Resvered1;            //保留字节,置空/*38*/ uint8 BootSig;              //扩展引导标记,0x29/*39*/ uint32 VolID;               //卷序列号/*43*/    char VolLab[11];            //卷标/*54*/  char FileSysType[8];        //文件系统属性/*62*/  uint16 VerNum[2];           //版本号,VerNum[0]为主版本号,VerNum[1]为子版本号/*66*/ uint16 BNodeSize;           //内部节点长度(byte)/*68*/    uint16 LNodeSize;           //叶节点长度(byte)/*70*/ uint32 LogBlockAddr;        //日志块地址,单位:块/*74*/    uint32 LBNum;               //一个日志块占用块数/*78*/   uint32 InfoBlockAddr;       //信息块地址/*82*/   uint8 boot[BOOTCodeSize];   //boot代码/*446*/ DPT dpt[4];                 //4个分区表/*510*/  uint16 BOOTSign;            //引导扇区标志,0xAA55
}BOOTLoder, * _bootloder;

日志还没设计,不过暂时预留日志的位置。

由于实现的非常之乱,变量也不规整,因此目前不开放代码。

尝试做一个简单的文件系统相关推荐

  1. 第四章 .net core做一个简单的登录

    项目目标部署环境:CentOS 7+ 项目技术点:.netcore2.0 + Autofac +webAPI + NHibernate5.1 + mysql5.6 + nginx 开源地址:https ...

  2. 蚂蚁研究员玉伯:做一个简单自由有爱的技术人

    玉伯 蚂蚁研究员 读完需要 10 分钟 速读仅需 1 分钟 玉伯,蚂蚁研究员,体验技术部负责人.2008 年加入淘宝,2012 年开始在支付宝致力于设计语言 Ant Design.数据可视化 AntV ...

  3. 玉伯:做一个简单自由有爱的技术人

    简介: 前端工程师如何成长?如何管理前端团队?如何打造团队文化?近日,蚂蚁研究员兼体验技术部负责人玉伯,在蚂蚁内部技术人的成长公开课上,分享了他的人生愿景和心路历程. 作者 | 玉伯 前端工程师如何成 ...

  4. Java怎么做一个简单网页呢?

    学java的同学在接触到Java web开发之后都跃跃欲试想要尝试自己开发一个页面,那么应该如何操作呢?都需要使用到哪些技术呢?下面小千就来告诉你. 需要使用到的技术 java 语言知识, jsp 知 ...

  5. 用python做一个简单GUI小软件

    用python做一个简单软件 前言 这是一个课设,用python做一个扫描王软件 我主要做的GUI部分,记录分享一下.也是第一次用python做小软件,python的方便果然是名不虚传 遇到问题 1. ...

  6. 【Unity3d】 教会你如何做一个简单的电梯系统(升降平台)

    博主第一次写博客,语言略俗,有不足之处还请指正! 由于自己还处在unity小白阶段,受2d升降平台的影响(后续我也会上传关于2d升降平台的文章),突发奇想如何用3d做一个电梯系统,查阅网上资料后,发现 ...

  7. 最年轻院士北大演讲:做一个简单纯粹的自己

    抓住事物的本质,排除干扰,做最简单合理的推断,你们就更有可能做出正确的选择和决定.事实上,最简单的.最纯粹的往往才是最深刻的,也是最持久永恒的. 回归根本,做一个简单纯粹的自己.这也应该是做人做事的指 ...

  8. pygame做一个简单的打字游戏

    pygame做一个简单的打字游戏 1.基本代码 下面的代码完成了每一秒在界面的顶部随机生成一个新的字母 # -*- coding=utf-8 -*- import pygame from pygame ...

  9. clistctrl控件最后插入在后面_用图表控件做一个简单的员工信息查询系统

    前几天在上课的时候有同学说在做人员的信息查询的时候,经常的要去做查找搜索很麻烦,能不能做一个简单的人员信息查询系统,只需要选择人员的编号就可以查询到这个员工的信息.其实要实现这个同学的需求在EXCEL ...

  10. 程序猿修仙之路--数据结构之你是否真的懂数组? c#socket TCP同步网络通信 用lambda表达式树替代反射 ASP.NET MVC如何做一个简单的非法登录拦截...

    程序猿修仙之路--数据结构之你是否真的懂数组? 数据结构 但凡IT江湖侠士,算法与数据结构为必修之课.早有前辈已经明确指出:程序=算法+数据结构  .要想在之后的江湖历练中通关,数据结构必不可少.数据 ...

最新文章

  1. [Luogu] 选学霸
  2. vs2008与IIS 7.0使用在vista上时出现的问题及解决方法(Internet Explorer 无法显示该页面)(VS2008: IE Cannot Display Web Page)...
  3. 2018CTDC风暴来袭乌镇 互联网大佬再续前缘
  4. ubuntu 安装yum_如何在 Linux 中安装微软的 .NET Core SDK | Linux 中国
  5. 最快捷的阅读实训新闻
  6. (转)OpenSSL命令---pkcs12
  7. linux zip打包_还在百度Linux命令?推荐一套我用起来特顺手的命令!(JAVA)
  8. 【DP】小明在边塞(jzoj 2147)
  9. java io流区别_Java中IO流的分类和BIO,NIO,AIO的区别
  10. python3的socket_python3的socket使用
  11. ubuntu中bash,sh,./,bash区别
  12. Linux下载Java包,Linux环境Java包的安装和环境配置
  13. 西瓜直播怎么录屏游戏
  14. MAX485芯片收发详解 实现485通信
  15. ARX中各种坐标系及Transfrom操作相关
  16. 投资学U18 股票估值 课后习题解读
  17. hue oozie spark:GC overhead limt exceed
  18. 2016基于百度地图定位
  19. 情人节情侣表空间白网页源码 html 简单易改 祝福你们幸福安康、地久天长!
  20. 自己碰到的360安全浏览器兼容模式的问题总结

热门文章

  1. 【读书笔记】企业IT架构转型之道 阿里巴巴中台战略思想和架构实战
  2. fseek函数、ftell函数、rewind函数详解
  3. 2021华为软挑再探——代码实现
  4. Spine3.8.75 下载
  5. 三菱FX3U-1PG模块与台达A2伺服连接用法
  6. SAP PK Oracle
  7. java snmp mib库接收_SNMP MIB 功能开发详细步骤
  8. 2021年游戏项目的十大编程语言:C++、Java、JavaScript、Python均在榜上
  9. XenApp and XenDesktop 7.15 LTSR CU6发布
  10. asp.net中配置使用Sqlite轻型数据库