基于C++实现的文件系统(简易版——内存空间操作)

Posted 流楚丶格念

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于C++实现的文件系统(简易版——内存空间操作)相关的知识,希望对你有一定的参考价值。

文章目录

基于C++实现的文件系统(简易版——内存空间操作)

一、实现的相关操作:

所有操作都不支持从根目录索引,即都只能访问当前目录下的文件,不能用/a/b越级访问

1.1 目录相关操作

说明命令
列出目录下所有文件:ls
切换目录:cd newdir
显示目录:pwd
创建目录:mkdir dirName
删除目录和目录下所有文件:rmdir dirName
修改目录名或文件名:mv oldName newName

1.2 文件相关操作

说明命令
创建文件(大小以1KB为单位):touch filename fileSize
删除文件:rm filename
从上一次后读取size字节的文件内容:read fileName size
从头开始读取size字节的文件内容:reread fileName size
从文件尾写入内容:write fileName content
清空文件,从头写入:rewrite fileName content

1.3 系统操作

说明命令
使用命令帮助:help
退出系统:quit

二、系统层次结构:

系统接口:是该文件系统提供给用户可以使用的命令接口,如ls,mkdir,touch等

文件管理:是系统对于文件和目录层次的管理的,规定了FCB结构,目录结构等,包含了对接口的实现。

磁盘管理:是系统最底层直接对内存空间的管理,如磁盘空闲空间管理,磁盘空间的分配方式等。

三、磁盘管理

盘块大小:以1KB的空间作为系统盘块,用以分配的基本单位

分配方式:系统采用连续分配的方式,在磁盘上划定要求的连续盘块分配给文件使用

空间管理:系统采用位示图的方法,标记了磁盘上所有盘块的使用情况。

使用systemStartAddr标记了整个系统的起始地址,以1KB作为盘块划分,所以本系统共有100K个盘块。位示图用char[]表示,所以位示图大小为100KB。位示图存储在系统起始位置,在系统初始化时,0~99号盘块默认被存储位示图使用。

3.1 磁盘向文件管理提供的接口说明:

磁盘向文件管理提供的接口说明:

DiskOperate.h
#ifndef DISKOPERATE_H_INCLUDED
#define DISKOPERATE_H_INCLUDED
//磁盘操作接口
#define system_size 100*1024*1024   //系统大小
#define block_szie 1024 //盘块大小
#define block_count system_size/block_szie //系统盘块数目

//初始化系统
void initSystem();
//磁盘分配
int getBlock(int blockSize) ;
//获得盘块的物理地址
char* getBlockAddr(int blockNum);
//获得物理地址的盘块号
int getAddrBlock(char* addr);
//释放盘块、
int releaseBlock(int blockNum, int blockSize);
//退出系统
void exitSystem();
#endif // DISKOPERATE_H_INCLUDED

3.2 文件层次管理说明

系统用char[]数组作为位示图保存了每一个盘块的使用状态,而且采取了连续分配的方式,对于目录表和FCB都规定直接使用一个盘块,文件又是规定好大小不能扩展的,所以实现起来减少了很多FAT,索引表,那些离散分配所需的连接方式,单纯练手的话,还是十分简易的。

FileOperate.h (部分内容)
//目录项结构:
struct dirUnit
    char fileName[59];  //文件名
    char type;  //文件类型,0目录, 1文件
    int startBlock; //FCB起始盘块
;
//一个目录项包含了文件名和文件类型,当文件为目录时,起始盘块指示了目录表所在的盘块号,当文件为文件时,起始盘块指示了FCB所在的盘块号。
#define dirTable_max_size 15    //目录表项最大值
//目录表结构:
struct dirTable 
    int dirUnitAmount;//目录项数目
    dirUnit dirs[dirTable_max_size];//目录项列表
;
/*
本系统规定一个目录表只占用一个盘块,一个目录项大小为64B,所以一个目录表中最多可含15个目录项,dirUnitAmount记录每个目录表中已含有的目录项数目。系统在初始化时,会自动生成一个空的根目录表存放于磁盘中,作为用户的初始位置,用户所有的目录和文件都这个表为根进行树状目录结构的展开。
当创建一个目录表时,系统会自动为目录表加上一项名为”..”的目录项,指示父目录表的位置。
*/

//FCB结构:
struct FCB 
    int blockNum;   //文件数据起始盘块号
    int fileSize;   //文件大小,盘块为单位
    int dataSize;   //已写入的内容大小,字节为单位
    int readptr;    //读指针,字节为单位
    int link;   //文件链接数
;
/*
	文件控制块包含了文件数据的起始位置和大小。dataSize,readptr是为文件的读写操作而准备的,记录文件已写入的内容长度(不可超过文件大小),和当前读取的位置。Link记录了文件的链接数,用于文件的共享,当文件的链接数为0时,系统可以回收文件的空间。同样的,一个FCB大小为20B,但也用一个盘块保存。
	由于采用的是连续分配方式,所以系统规定文件被创建时,必须给出文件的大小,而且后期也不能修改文件的大小。
*/

四、具体代码实现

4.1 磁盘管理的实现

DiskOperate.cpp

#include"DiskOperate.h"

#include<stdio.h>
#include<stdlib.h>

 

char* systemStartAddr;  //系统起始地址

//初始化系统
void initSystem()

    //创建空间
    systemStartAddr = (char*)malloc(system_size * sizeof(char));
    //初始化盘块的位示图
    for(int i=0; i<block_count; i++)
        systemStartAddr[i] = '0';
    //用于存放位示图的空间已被占用
    int bitMapSize = block_count * sizeof(char) / block_szie;//位示图占用盘块数:100
    for(int i=0; i<bitMapSize; i++)//从零开始分配
        systemStartAddr[i] = '1';   //盘块已被使用

//退出系统
void exitSystem()

    free(systemStartAddr);

//磁盘分配
int getBlock(int blockSize)

    int startBlock = 0;
    int sum=0;
    for(int i=0; i<block_count; i++)
    
        if(systemStartAddr[i] == '0')//可用盘块
        
            if(sum == 0)//刚开始,设置开始盘块号
                startBlock = i;
            sum++;
            if(sum == blockSize)//连续盘块是否满足需求
            
                //满足分配,置1
                for(int j=startBlock; j<startBlock+blockSize; j++)
                    systemStartAddr[j] = '1';
                return startBlock;
            

        
        else//已被使用,连续已经被打断
            sum = 0;
    
    printf("not found such series memory Or memory is full\\n");
    return -1;


//获得盘块的物理地址
char* getBlockAddr(int blockNum)

    return systemStartAddr + blockNum * block_szie; //偏移量单位为字节

//获得物理地址的盘块号
int getAddrBlock(char* addr)

    return (addr - systemStartAddr)/block_szie;

//释放盘块、
int releaseBlock(int blockNum, int blockSize)

    int endBlock = blockNum + blockSize;
    //修改位示图盘块的位置为0
    for(int i=blockNum; i<endBlock; i++)
        systemStartAddr[i] = '0';
    return 0;

4.2 文件管理

首先说明全局变量

dirTable* rootDirTable; //根目录
dirTable* currentDirTable;  //当前所在目录位置
char path[200]; //保存当前绝对路径

结构体定义可以见上面文件管理

4.2.1 创建文件

命令:

touch fileName size

创建文件的过程可以为:

为文件控制块申请空间->为文件数据申请空间->创建FCB控制块->在当前目录添加相关的目录项描述

//创建文件
int creatFile(char fileName[], int fileSize)

    //检测文件名字长度
    if(strlen(fileName) >= 59)
    
        printf("file name too long\\n");
        return -1;
    
    //获得FCB的空间
    int FCBBlock = getBlock(1);
    if(FCBBlock == -1)
        return -1;
    //获取文件数据空间
    int FileBlock = getBlock(fileSize);
    if(FileBlock == -1)
        return -1;
    //创建FCB
    if(creatFCB(FCBBlock, FileBlock, fileSize) == -1)
        return -1;
    //添加到目录项
    if(addDirUnit(currentDirTable, fileName, 1, FCBBlock) == -1)
        return -1;

    return 0;


//创建FCB
int creatFCB(int fcbBlockNum, int fileBlockNum, int fileSize)

    //找到fcb的存储位置
    FCB* currentFCB = (FCB*) getBlockAddr(fcbBlockNum);
    currentFCB->blockNum = fileBlockNum;//文件数据起始位置
    currentFCB->fileSize = fileSize;//文件大小
    currentFCB->link = 1;//文件链接数
    currentFCB->dataSize = 0;//文件已写入数据长度
    currentFCB->readptr = 0;//文件读指针
    return 0;

//添加目录项
int addDirUnit(dirTable* myDirTable, char fileName[], int type, int FCBBlockNum)

    //获得目录表
    int dirUnitAmount = myDirTable->dirUnitAmount;
    //检测目录表示是否已满
    if(dirUnitAmount == dirTable_max_size)
    
        printf("dirTables is full, try to delete some file\\n");
        return -1;
    

    //是否存在同名文件
    if(findUnitInTable(myDirTable, fileName) != -1)
    
        printf("file already exist\\n");
           return -1;
    
    //构建新目录项
    dirUnit* newDirUnit = &myDirTable->dirs[dirUnitAmount];
    myDirTable->dirUnitAmount++;//当前目录表的目录项数量+1
    //设置新目录项内容
    strcpy(newDirUnit->fileName, fileName);
    newDirUnit->type = type;
    newDirUnit->startBlock = FCBBlockNum;
     
    return 0;


//从目录中查找目录项目
int findUnitInTable(dirTable* myDirTable, char unitName[])

    //获得目录表
    int dirUnitAmount = myDirTable->dirUnitAmount;
    int unitIndex = -1;
    for(int i=0; i<dirUnitAmount; i++)//查找目录项位置
       if(strcmp(unitName, myDirTable->dirs[i].fileName) == 0)
            unitIndex = i;
    return unitIndex;

4.2.2 删除文件

命令:

rm fileName

删除文件的流程可以分为:

查找文件在当前目录的目录项描述内容->得到FCB的描述内容->释放FCB空间和文件数据空间->从目录表中删除文件的目录项

(也就是说,在这里其实只是把用户能得到文件的索引删除,而文件的内容在没有被覆盖之前依旧是存在的)

//删除文件
int deleteFile(char fileName[])

    //忽略系统的自动创建的父目录
    if(strcmp(fileName, "..") == 0)
    
        printf("can't delete ..\\n");
        return -1;
    
    //查找文件的目录项位置
    int unitIndex = findUnitInTable(currentDirTable, fileName);
    if(unitIndex == -1)
    
        printf("file not found\\n");
        return -1;
    
    dirUnit myUnit = currentDirTable->dirs[unitIndex];
    //判断类型
    if(myUnit.type == 0)//目录
    
        printf("not a file\\n");
        return -1;
    
    int FCBBlock = myUnit.startBlock;
    //释放内存
    releaseFile(FCBBlock);
    //从目录表中剔除
    deleteDirUnit(currentDirTable, unitIndex);
    return 0;

//释放文件内存
int releaseFile(int FCBBlock)

    FCB* myFCB = (FCB*)getBlockAddr(FCBBlock);
    myFCB->link--;  //链接数减一
    //无链接,删除文件
    if(myFCB->link == 0)
    
        //释放文件的数据空间
        releaseBlock(myFCB->blockNum, myFCB->fileSize);
    
    //释放FCB的空间
    releaseBlock(FCBBlock, 1);
    return 0;

//删除目录项
int deleteDirUnit(dirTable* myDirTable, int unitIndex)

    //迁移覆盖
    int dirUnitAmount = myDirTable->dirUnitAmount;
    for(int i=unitIndex; i<dirUnitAmount-1; i++)
    
        myDirTable->dirs[i] = myDirTable->dirs[i+1];
    
    myDirTable->dirUnitAmount--;
    return 0;

4.2.3 目录的删除和创建

目录删除与创建命令:


目录的创建和删除与文件的操作大致相同,创建目录时,目录表项的startBlock不是FCB而是指目录的存放位置,而且还要自动为其添加多一个父目录项“…”,用于跳转。删除目录时需要注意是否递归删除目录下的所有文件和目录。详请看最后的完整代码,这里不专门复述。

4.2.4 切换目录

命令:

cd dirName

cd指令,主要就是让当前目录currentDirTable指向新的目录盘块地址就可以了,目录盘块地址可以在当前目录表的表项中找到。

//切换目录
int changeDir(char dirName[])

    //目录项在目录位置
    int unitIndex = findUnitInTable(currentDirTable, dirName);
    //不存在
    if(unitIndex == -1)
    
        printf("file not found\\n");
        return -1;
    
    if(currentDirTable->dirs[unitIndex].type == 1)
    
        printf("not a dir\\n");
        return -1;
    
    //修改当前目录
    int dirBlock = currentDirTable->dirs[unitIndex].startBlock;
    currentDirTable = (dirTable*)getBlockAddr(dirBlock);
    //修改全局绝对路径path
    if(strcmp(dirName, "..") == 0)
    
        //回退绝对路径
        int len = strlen(path);
        for(int i=len-2;i>=0;i--)
            if(path[i] == '\\\\')
            
                path[i+1]='\\0';
                break;
            
    else 
        strcat(path, dirName);
        strcat(path, "\\\\");
    

    return 0;


4.2.5 读文件

命令:

read fileName size

主要就是获得文件的初始地址,然后根据需要的读取长度size和当前的读指针进行输出,如果遇到文件尾则输出#,读写的单位都是字节。

注: reread就是先把读指针readptr置为0,然后执行读操作。

//读文件 read
int read(char fileName[], int length)

    int unitIndex = findUnitInTable(currentDirTable, fileName);
    if(unitIndex == -1)
    
        printf("file no found\\n");
        return -1;
    
    //控制块
    int FCBBlock = currentDirTable->dirs[unitIndex].startBlock;
    FCB* myFCB = (FCB*)getBlockAddr(FCBBlock);
    doRead(myFCB, length);
    return 0;

//执行读操作 
int doRead(FCB* myFCB, int length)

    //读数据
    int dataSize = myFCB->dataSize;
    char* data = (char*)getBlockAddr(myFCB->blockNum);
    //在不超出数据长度下,读取指定长度的数据
    for(int i=0; i<length && myFCB->readptr < dataSize; i++, myFCB->readptr++)
    
        printf("%c", *(data+myFCB->readptr));
    
    if(myFCB->readptr == dataSize)//读到文件末尾用#表示
        printf("#");
    //换行美观
    printf("\\n");
    return 0;

4.2.6 写文件操作

命令:

write fileName content

和读文件差不多,不过是根据当前文件的数据长度,在文件末尾给文件以字节的形式赋值上输入的content内容,数据长度不能超过文件长度

//写文件,从末尾写入 write
int write(char fileName[], char content[])

    int unitIndex = findUnitInTable(currentDirTable, fileName);
    if(unitIndex == -1)
    
        printf("file no found\\n");
        return -1;
    
    //控制块
    int FCBBlock = currentDirTable->dirs[unitIndex].startBlock;
    FCB* myFCB = (FCB*)getBlockAddr(FCBBlock);
    doWrite(myFCB, content);
    return 0;

//执行写操作
int doWrite(FCB* myFCB, 以上是关于基于C++实现的文件系统(简易版——内存空间操作)的主要内容,如果未能解决你的问题,请参考以下文章

华为机试真题 C++ 实现简易内存池

一个简易内存池(C++)

安装win7 64位旗舰版需要啥配置 win764位最低要求配置说明

基于SpringBoot 2.x开发的简易版图书管理系统(实现对图书的CRUD)

基于SpringBoot 2.x开发的简易版图书管理系统(实现对图书的CRUD)

基于socket简易版客户端,服务端交互