基于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++实现的文件系统(简易版——内存空间操作)的主要内容,如果未能解决你的问题,请参考以下文章
安装win7 64位旗舰版需要啥配置 win764位最低要求配置说明
基于SpringBoot 2.x开发的简易版图书管理系统(实现对图书的CRUD)