Giter Club home page Giter Club logo

file_management's Introduction

File_Management

基于qt5界面库的文件管理模拟系统

项目需求

  • 在内存中开辟一个空间作为文件存储器,在其上实现一个简单的文件系统
  • 退出这个文件系统时,需要该文件系统的内容保存到磁盘上,以便下次可以将其恢复到内存中来
  • 文件存储空间管理自选一种方法
  • 空闲空间管理自选一种方法
  • 文件目录采用多级目录结构,目录项目中应包含文件名、物理地址、长度等信息。
  • 文件系统应提供如下操作:格式化、创建子目录、删除子目录、显示目录、更改当前目录、创建文件、打开文件、关闭文件、写文件、读文件、删除文件

开发/运行环境

  • 开发环境:Windows 10 1809
  • 开发语言:C++11
  • 开发工具:QT Creator 4.9.0
  • 开发框架:QT 5.12.2

设计过程

一定程度上借鉴了以前的设计经验,并做出一些简化和改进

系统架构

系统架构

  • 用户界面,即用户直接进行操作的图形窗口界面
  • 文件操作,即用户可以进行的文件操作
  • 数据操作接口,即实现上层文件操作的相关数据结构的操作接口
  • 虚拟磁盘,即该文件系统开辟的内存空间,所有的数据操作都是基于该内存空间进行的
  • 真实磁盘,关闭该文件系统时,创建一个真实文件,保存文件系统的数据到真实磁盘,以便下次打开可以恢复到内存中来

该文件系统实现了一定的封装性,即用户无法了解到文件操作层以下的实现,一方面简化了用户的操作,另一方面也保障了文件系统的稳定性。

用户界面

整个文件系统有3个窗口:

  • 主窗口
    分为两个部分,一部分展示了当前目录的信息(文件/目录列表),另一部分展示了用户可以进行的文件操作
  • 输入窗口
    用于接受用户的输入(磁盘大小,文件/目录名)
  • 读文件/写文件窗口
    用于给用户提供文件的读写操作

数据结构:块组(BlockGroup)

为了简化模拟,这里的块组(卷)相当于一个虚拟磁盘

class BlockGroup
{
public:
    int disk_size;      //磁盘大小
    int block_num;      //数据块数量
    SuperBlock superblock;      //超级块
    GroupDescriptor groupdescriptor;     //组描述符
    QList<DataBlock*> DataBlockList;     //数据块列表
    QList<Inode*> InodeList;     //索引结点列表

    int current_inode_index;    //当前目录
};

卷大小

这里的卷大小只包含了存储数据块的大小,即不包含存储文件控制块的大小

数据块数量

为了简化以及方便操作,用于记录该卷所分配到的数据块的数量

超级块

用于描述该卷中文件控制块FCB(索引结点inode)的信息

  • FCB的数量
  • 空闲索引的数量
struct SuperBlock{  //超级块
    int inodeNUM;
    int freeinodeNUM;
    SuperBlock(int num = 2048, int free_num = 2048):inodeNUM(num), freeinodeNUM(free_num){}
};

组描述符

用于记录索引结点和数据块的空闲空间列表,这里采用位图的方式进行记录

  • 数据块块位图
    描述该卷中数据块的使用情况,True代表数据块空闲,False代表数据块已被占用
  • FCB位图 描述该卷中FCB的使用情况,True代表FCB空闲,False代表FCB已被占用
struct GroupDescriptor{    //组描述符
    bool* DataBlockBitArray;
    bool* InodeBitArray;
};

数据块列表

用于记录该卷所包含的所有数据块

  • 数据块 一个数据块有一个固定长度的QString(相当于std::String),大小为4KB
struct DataBlock{   //数据块
    QString data;
    DataBlock(){
        data.resize(2048);
        for(int i = 0; i < 2048; i++){
            data[i] = ' ';
        }
    }
};

FCB表(索引结点表)

用于记录该卷所分配到的所有FCB项,FCB表的大小由超级块给出

  • FCB项
    • 文件类型
    • 文件名
    • 文件大小
    • 创建时间
    • 更新时间
    • 所占数据块数量(文件类型为普通文件有效)
    • 所占数据块索引表(文件类型为普通文件有效)
    • 所包含的子文件FCB项数量(文件类型为目录文件有效)
    • 所包含的子文件的FCB项索引(文件类型为目录文件有效)
    • 父目录的FCB项的索引
enum FileType{
    UNKNOWN, COMMON, DIRECTORY
};
struct Inode{   //索引结点
    FileType file_type;
    QString file_name;
    int file_size;
    QDateTime create_time;
    QDateTime update_time;
    int block_num;
    QList<int> DataBlockVec;
    int child_num;
    QList<int> ChlidInodeVec;
    int father_index;
    Inode(){
        file_name = "";
        file_type = FileType::UNKNOWN;
        block_num = 0;
        child_num = 0;
        father_index = -1;
        file_size = 0;
        create_time = QDateTime();
        update_time = QDateTime();
        DataBlockVec = QList<int>();
        ChlidInodeVec = QList<int>();
    }
};

当前FCB项索引

为了方便文件系统的管理实现,这里引入一个用于记录当前文件系统所处目录的FCB项的索引

关键文件操作实现细节

文件系统的保存与读取

虚拟磁盘的关键信息按照一定的格式储存在真实磁盘的disk.txt文件中,因为格式相对固定且每次打开文件系统都会从读取disk.txt文件来初始化虚拟磁盘,所以一旦修改了disk.txt文件中的数据,有可能导致文件系统打开出错。除非能恢复到正确的格式,否则只能删除disk.txt文件来使得文件系统可重新启动

文件/目录的创建与删除

创建

文件/目录的创建时,文件系统通过在FCB位图中找到并分配一个空闲的FCB(索引结点)来记录这个文件的信息,如果没有找到空闲的FCB则会返回错误。文件创建并不分配数据块,只有在写入的时候才分配数据块。

bool BlockGroup::createFile(FileType filetype, QString filename)    //创建文件/目录
{
    if(superblock.freeinodeNUM == 0){
        return false;
    }
    superblock.freeinodeNUM--;
    int inode_index = FindFirstFreeInode();
    if(inode_index == -1)
        return false;
    Inode *inode = InodeList[inode_index];

    //修改目录项
    inode->file_type = filetype;
    inode->file_name = filename;
    inode->create_time = QDateTime::currentDateTime();
    inode->update_time = inode->create_time;
    inode->father_index = current_inode_index;

    groupdescriptor.InodeBitArray[inode_index] = false;
    //修改父目录项
    if(current_inode_index == -1){  //判断是否为根目录
        return true;
    }
    Inode *father_inode = InodeList[current_inode_index];
    father_inode->ChlidInodeVec.append(inode_index);
    father_inode->child_num++;
    father_inode->update_time = QDateTime::currentDateTime();

    return true;
}

在修改这个分配到的FCB时,也要修改该目录的FCB的信息。

删除

文件/目录在删除时,需要分为两种不同的情况,删除文件和删除目录。

删除文件时,只需将该文件占用的FCB和数据块释放,即在FCB位图和数据块位图对应位置改为True便可。因为每次分配新的FCB和数据块都会覆盖写入,因此在删除时不必清除其中的内容。

删除目录时,如果该目录下存在子文件子目录,则需要递归地删除该目录下的文件,而且要注意避免重复删除修改父目录FCB

void BlockGroup::deleteFile(int inodeindex)     //删除文件/目录
{
    Inode *inode = InodeList[inodeindex];
    //修改父目录项
    int fatherindex = inode->father_index;
    if(fatherindex == current_inode_index){     //避免重复删除
        Inode *fatherinode = InodeList[fatherindex];
        fatherinode->ChlidInodeVec.removeOne(inodeindex);
        fatherinode->child_num--;
        updateInode(fatherinode);
    }
    //删除该文件/目录
    if(inode->file_type == FileType::COMMON){   //普通文件直接清除数据块
        for(int i = 0; i < inode->block_num; i++){
            groupdescriptor.DataBlockBitArray[inode->DataBlockVec[i]] = true;
        }
    }
    else{
        if(inode->child_num != 0){      //目录文件需要递归删除子目录
            for(int i = 0; i < inode->child_num; i++){
                deleteFile(inode->ChlidInodeVec[i]);
            }
        }
    }
    InodeClear(inode);
    groupdescriptor.InodeBitArray[inodeindex] = true;
    superblock.freeinodeNUM++;
}

该删除算法按照深度优先遍历,自底向上地删除该目录下的所有子文件,最后再删除自身

当前目录的更改

该文件系统只提供单步目录的更改,即当前目录只能更改为其他有父/子关系的目录。当前目录的更改有两种情况,分别是进入下级目录和返回上级目录。
因为我在块组中记录了当前目录FCB的索引,所以更改目录只需更改当前目录FCB索引的值便可以实现当前目录的更改,而索引的值可以通过当前目录FCB中存储的子文件FCB索引和父目录FCB索引来获得。

文件的读取与写入

文件的读取相对简单,只需根据该文件FCB中的数据块索引,获取对应的数据块,并将数据连接起来便可

QString BlockGroup::readFileFromVirtualDisk(int inodeindex)     //从虚拟磁盘读文件
{
    Inode *inode = InodeList[inodeindex];
    QString content = "";
    for(int i = 0; i < inode->block_num; i++){
        DataBlock* datablock = DataBlockList[inode->DataBlockVec[i]];
        QString temp = datablock->data;
        content = content + temp;
    }
    return content;
}

文件的写入相对复杂。

  • 先遍历该文件已经占有的数据块,并覆盖写入数据
  • 在遍历完成所占有数据块后,存在两种情况,数据块不足和数据块多余
    • 对于数据块不足的情况,需要循环分配空闲的数据块直到所有数据写入完成
    • 对于数据块多余的情况,需要将多余的数据块释放掉,即将其位图置为True
bool BlockGroup::writeFileToVirtualDisk(int inodeindex, QString content)    //写文件到虚拟磁盘
{
    Inode *inode = InodeList[inodeindex];
    int in_point = 0;       //文件字符串写指针
    int blockcount = 0;

    bool islack = true;     //数据块缺少标记
    for(; blockcount < inode->block_num; blockcount++){                     //将现有的数据块重写入
        DataBlock *datablock = DataBlockList[inode->DataBlockVec[blockcount]];
        for(int j = 0;j < 2048; j++){
            if(in_point < content.length()){
                datablock->data[j] = content[in_point];
                in_point++;
            }else{
                datablock->data[j] = ' ';
                islack = false;
            }
        }

    }

    if(islack == true){     //数据块不足,需要开辟更多空间
        while(in_point < content.length()){
            int datablockindex = FindFirstFreeDataBlock();
            if(datablockindex == -1){//空闲数据块不足
                updateInode(inode);
                return false;
            }
            DataBlock *datablock = DataBlockList[datablockindex];
            for(int j = 0;j < 2048; j++){
                if(in_point < content.length()){
                    datablock->data[j] = content[in_point];
                    in_point++;
                }else{
                    datablock->data[j] = ' ';
                }
            }
            inode->DataBlockVec.append(datablockindex);
            inode->block_num++;
            groupdescriptor.DataBlockBitArray[datablockindex] = false;
        }
    }else{      //数据块充足,判断是否存在多余数据块进行释放
        int excessNum = inode->block_num - blockcount - 1;
        for(int i = 0 ;i < excessNum; i++){
            groupdescriptor.DataBlockBitArray[inode->DataBlockVec.takeLast()] = true;
            inode->block_num--;
        }
    }

    updateInode(inode);
    return true;
}

其他实现过程可以直接参考源文件,所有代码都有必要的注释。

存在问题

读写文件时的问题

在处理数据块内部碎片时,我使用了' '进行填充,也因为这个空格符,在下一次读取文件时会读取到若干个空格,写文件的时候就在这些空格后面写入,而无法接续上一次的末尾,而且会存在浪费空间的现象。
另外,文件无法记录写入文件时的字体和颜色,在下一次的读取文件时会自动转化为系统默认的字体和颜色。

文件大小的问题

因为该文件系统最小的空间单位为数据块,因此文件大小是由文件占用数据块的数量决定的,即数据块*4KB,而无法精确到文件实际大小。

file_management's People

Contributors

lupus666 avatar hule1 avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.