如何用C/C++写一个Linux文件系统模拟器

Posted 狱典司

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何用C/C++写一个Linux文件系统模拟器相关的知识,希望对你有一定的参考价值。

1. 程序演示


home目录下包含如下文件

其中root文件夹内存有account.txt,用于存储用户登录信息,在虚拟磁盘初始化时将之读入用户组结构并用于验证登录。

运行程序:

1. 登录–login

运行程序后只能先登录才能进行操作,不然会系统提示进行登录,输入help可以查看命令帮助文件。

2. 切换目录— cd

3. 展示文件列表—ls


对应本地真实目录可知,初始化成功,目录树建立成功,且各个文件/文件夹的大小、路径、文件类型、读写权限、所属用户都初始化成功!

4. 查看物理块使用情况以及物理块存储内容 — df




df命令中展示了已占用物理块的存储内容:
(1)complete file of xxx : 表明该物理块存储了一个完整的文件
(2)parts of xxx: 表明该物理块存储了一个文件的部分内容
(3)IndexBlock of xxx: 表明该物理块为索引物理块
可见初始化时占用物理块为462块,大约占用了虚拟磁盘462KB的空间,由于在该虚拟磁盘中文件夹都单独占用一个索引物理块,且所有的索引物理块不存储具体文件内容,故其空间开支稍大于实际空间开支,可由比较本地实际目录大小(436KB)可证明。

5. cd命令的延伸 :

(1)cd ./ :返回当前目录
(2)cd …/ :返回上一级目录
(3)cd home :返回用户根目录

6. 查看当前目录名和父目录名 —— now

7. 创建文件—— touch


同时在本地文件中也进行了创建文件的操作

8. 打开文件 —— open

9. 查看当前活动文件 —— ASL


若输入的文件名不正确会有相应提示。

10. 写文件操作 —— write

(1) write -a 追加写入文件
(2) write -c 覆盖写入文件

若是没有将文件打开就要写入则会提示先将文件打开;

成功写入。

注意在写入文件后,文件的大小以及所属父目录的大小都得到更新。

11. 读文件的全部内容 —— cat


若没有将文件打开就进行读操作则会进行提示。

下面读取inode.txt为例,由于文件较长,只截取开头和结尾部分(inode.txt为编写该项目的过程中留下的一份备份txt文件):

12. head -num 读取文件头num行

13. tail -num 读取头文件num行

14. 关闭文件 —— close

15. 递归查找文件 —— find

16. 复制文件 —— cp


本地文件夹同步实现了复制操作

而且物理块也实现了相应的变化,增加了新文件占用的物理块:

17. 移动文件 —— move

18. 创建文件夹 —— mkdir

19. 删除文件夹 —— rm -rf

在新建的OSmenu文件夹中新建两个文件(一个创建,一个通过复制创建)后进行删除


本地文件夹中也同步了操作

下面进行删除,可以将文件夹以及其中的文件都删掉

文件夹也同步了该操作

20. 删除文件 —— rm -f


本地文件夹也同步了相应操作


2. 代码部分

2.1 本项目使用的结构体

代码中使用到的结构体信息如下:

2.1.1 用户结构

用户结构体中包含用户的用户名和密码,以及该用户的个人根目录。

`typedef struct User_Type
{
string username;//用户名
string password;//密码
struct FSct_Type *userFile;//指向该用户的"根"目录
}User;

2.1.2 文件控制块

该文件控制块结构在文件结构中的集合构成了文件目录。

typedef struct FCB{
User *Owner[12];//指向用户数据结构User的指针
int innerNum; //内部号
string fileName; //文件名
string type; // 文件类型
double length; //文件长度
long int authority; //读写权限
int phNum; //物理块号
struct PHB_Type *firstBlock; //首物理块
}FCB;

2.1.3 文件结构

文件结构中包含了以下信息:
(1)文件的名称;
(2)文件的路径;
(3)文件的所属用户组(考虑到可以实现文件共享);
(4)以及该目录下的文件目录表(由12个文件控制块组成,即限制了每个文件夹最多放置12个文件);
(5)文件内容(由于文件类型较多,此处的文件内容暂时用”parts of xxx”等来替代);
(6)子文件/子目录指针(与文件控制块fcb相对应,共12个);
(7)父目录指针(只有一个);

typedef struct FSct_Type{
string fileName;
string filePath;
User *Owner[12]; //用户账号
FCB *fcb[12]; //文件目录
char content[50]; //文件内容
struct FSct_Type *child[12]; //指针数组,指向二级文件结构
struct FSct_Type *pre; //指针,指向父目录
}FSct;

2.1.4 物理块结构

物理块结构包含了以下信息:
(1)物理块号;
(2)每个物理块的大小:该项目规定每个物理块的大小为1024B,即1KB;
(3)物理块状态标识符,空闲状态为0,占用状态为0;
(4)物理块索引表:当所存储的文件大小大于1KB时,其首物理块存储一个物理块索引表;
(5)索引指针:当所存储的文件大小大于1KB时,指向其他实际存储文件信息的物理块;
(8)物理块所存内容:由于文件类型较多,并不一定都可按字符读出保存到虚拟磁盘,此处的文件内容暂时用”parts/complete/index of xxx”等来替代。

typedef struct PHB_Type{
long int phNum; //物理块号
long int chunckSize = 1024; //每个块大小为1024B 即1KB
int state = 0; //物理块状态标识符
long int indexList[300]; //该块所保存的索引表
struct PHB_Type *child[300]; //索引表指向的块
string content;//该块所保存的内容
}PHB;

2.1.5 活动符号名表

活动符号名表用于存储打开文件的信息,该结构包含以下信息:
(1)文件名;
(2)对应文件控制块的内部号;
(3)文件路径(便于区分不同目录下的同名文件);
(4)指向活动文件表的指针。

typedef struct Active_symbol_list{
string fileName;
int innerNum;//内部号
string path;//文件路径
struct Active_file_list *afl;
struct Active_symbol_list *next;
}ASL;

2.1.6活动文件表
活动文件表用于存储文件目录的及本条目,即文件控制块。

typedef struct Active_file_list{
FCB *fcb;//由基本文件目录项(FCB块)组成的数组
struct Active_file_list *next;
}AFL;

2.2 完整代码

#include <pthread.h>
#define NUM_THREADS 1 //多线程并发数 
#include <iostream>
#include<fstream>  
#include<string>
#include<cstring>
#include<iomanip>
#include <malloc.h>
#include <vector>
#include <io.h>
#include <Windows.h>  
#include <sys\\stat.h>
using namespace std;
#define BUFFER_SIZE 

string homePath("C:\\\\Users\\\\SeanWayen\\\\Desktop\\\\home");
string path("C:\\\\Users\\\\SeanWayen\\\\Desktop\\\\home"); 
string tmpPath = homePath;

//用户数据结构
typedef struct User_Type
{
	string username;//用户名
	string password;//密码
	struct FSct_Type *userFile;//指向该用户的"根"目录 ,即在注册或从文件导入数据生成一个 用户时要给用户指向一个文件 
}User;

 //文件控制块 
typedef struct FCB{
	User *Owner[12];//指向用户数据结构User的指针 
	int innerNum;//内部号 
	string fileName;//文件名 
	string type;// 文件类型 
	double length;//文件长度 
	long int authority;//读写权限 
	int phNum; //物理块号 
	struct PHB_Type *firstBlock; 
}FCB;

//文件结构 
typedef struct FSct_Type{
	int i;
	string fileName;
	string filePath; 
	User *Owner[12];//用户账号 
	FCB *fcb[12]; //文件目录 
	char content[50];//文件内容 (待处理) 
	struct FSct_Type *child[12];//指针数组,指向二级文件结构 
	struct FSct_Type *parent;//指针,指向父目录 
	struct FSct_Type *pre;//指针,指向父目录 
}FSct;

//物理块结构
typedef struct PHB_Type{
	long int phNum;//物理块号 (在程序初始化时要自动生成) 
	long int chunckSize = 1024;//每个块大小为1024B 即1KB
	int state = 0;//初始化时 所有块都为0表示空闲,被占用时状态为1 
	long int indexList[300];//该块所保存的索引表 
	struct PHB_Type *child[30];//索引表指向的块 
	string content;//该块所保存的内容 
}PHB; 


//活动符号名表 
typedef struct Active_symbol_list{
	string fileName;
	int innerNum;//内部号 
	string path;//文件路径 
	struct Active_file_list *afl;
	struct Active_symbol_list *next;
}ASL;

//活动文件表 
typedef struct Active_file_list{
	FCB *fcb;//由基本文件目录项(FCB块)组成的数组 
	struct Active_file_list *next;
}AFL; 




PHB phBlock[1024*1024];//一共2^20个block,每个block大小为1KB,虚拟磁盘共1GB 
User uGroup[12];//用户组数组,后期要将文件中的用户读入 
FSct *UserFile[12];//12个用户对应12个用户根目录 
//FSct *home = (FSct *)malloc(sizeof(FSct));//home目录 
FSct *home = new FSct;
string userPath[12];
ASL *Shead = new ASL;//活动符号名表  表头 
AFL *Fhead = new AFL;//活动文件表   表头 
int FileNum_Curr = 0; //当前文件数目 

//引导函数
void help(){
	cout <<endl; 
	cout << "*******************欢迎使用多级文件系统*******************" << endl<<endl;
	cout << "        命令                    说明                      " << endl;
	cout << "        login                   登录                      " << endl;
	cout << "        cd                      更改当前目录              " << endl;
	cout << "        ls                      展示文件列表              " << endl;
	cout << "        touch                   创建文件                  " << endl;
	cout << "        rm -f                   删除文件                  " << endl;
	cout << "        mkdir                   创建目录                  " << endl;
	cout << "        rm -rf                  递归地删除目录及其文件    " << endl;
	cout << "        open                    打开文件                  " << endl;		
	cout << "        close                   关闭文件                  " << endl;
	cout << "        cat                     读文件到控制台            " << endl;		
	cout << "        tail -num               显示文件尾num行           " << endl;
	cout << "        head -num               显示文件头num行           " << endl;
	cout << "        write -c                写入文件(覆盖)          " << endl;
	cout << "        write -a                写入文件(追加)          " << endl;		
	cout << "        find                    递归地查找文件            " << endl;
	cout << "        cp                      复制文件                  " << endl;
	cout << "        move                    移动文件                  " << endl;
	cout << "        export                  导出文件                  " << endl;
	cout << "        import                  导入文件                  " << endl<<endl;
	cout << "*******************欢迎使用多级文件系统*******************" << endl;
	cout <<endl;
} 


//查看用户组 
void check_uGroup(){
	cout<<"——————————" <<endl<<"查看用户组"<<endl;
	for(int i=0; i<sizeof(uGroup); i++){
	    if(uGroup[i].username.length()!=0){
	       	cout<<"uGroup["<<i<<"]:"<<endl;
	       	cout<<"username:"<<uGroup[i].username<<endl;
	       	cout<<"password:"<<uGroup[i].password<<endl;
			}else{
			 	break;
			}
	   	}
	cout<<"——————————" <<endl;
}

//查看物理块使用情况 
void check_phBlock(){
	cout<<"——————————" <<endl<<"查看物理块使用情况"<<endl;
	double Use = 0.0;
	double NotUse = 0.0;
	double UsingRate;
	cout<<"已被使用的物理块号:"<<endl; 
	for(long int i=0; i<1024*1024; i++){
	    if(phBlock[i].state==1){
	       	cout<<i<<"|";
	       	Use++;
		 }else{
			NotUse++;
			}
	}
	UsingRate = Use/(Use+NotUse);
	
	cout<<endl<<"已使用物理块数目:"<<Use<<endl;
	cout<<"空闲物理块数目:"<<NotUse<<endl; 
	cout<<"物理块总数:"<<Use+NotUse<<endl; 
	cout<<"磁盘利用率:"<< UsingRate<<endl;
	cout<<"——————————" <<endl;
}

//读取用户组uGroup信息 

int account(string path){
    ifstream in(path);
    string line;
    int i=0;
 
    if(in) // 有该文件
       while (getline (in, line)){
       	string info = line;
       	string ID = info.substr(0,info.find_first_of(","));
       	string PW = info.substr(info.find_first_of(",")+1,info.length());
       	uGroup[i].username = ID;
       	uGroup[i].password = PW;
       	//cout<<"账号:"<<uGroup[i].username<<endl<<"密码:"<<uGroup[i].password<<endl; 
       	i++;
       	//cout<< line <<endl;
	   }  
       
        
    else // 没有该文件
       cout <<"no such file" << endl;
       
    in.close();

    return i; 
}


int index(){
	int num = -1;
	int flag= 0;
	
	
while(num == -1){
	
	cout<<"localhost: " ;
	string tmpOrder;
	cin>>tmpOrder;
	
	//登录界面 
	if(tmpOrder == "login"){
		cout<<"user: ";
		string ID;
		cin>>ID;
		
		cout<<"passwords: ";
		string PW;
		cin>>PW;
		
		for(int i=0;i<sizeof(uGroup);i++){
			if(uGroup[i].username == ID && uGroup[i].password == PW){
				num = i;
				flag =1 ;
				break;
			}
		}
	}
	
	else if(tmpOrder == "help"){
		help();
	}
	
	else{
		cout<<"please login first !"<<endl;
	}
}
	
	
	if(flag == 0){
		cout<<"No such account!"<<endl; 
		return -1;
	} 
	
	else if(flag == 1){
		cout<<"Login succed !"<<endl;
		return num;
	}
}



int REGISTER()//(1)向用户组增添新成员 (2)初始化该用户的文件目录  //目前相当于初始化 
{

	cout << "请输入用户名:";
	string userName;
	cin >> userName;
	cout << "请输入密码:";
	string passWord;
	cin >> passWord;
	

	for(int i=0; i<sizeof(uGroup); i++){
		
		if (uGroup[i].username == userName)
		{
			cout<<i<<endl;
			cout<< uGroup[i].username<<endl;
			
			cout << "注册失败,该用户名已存在" << endl;
			break;
		}
		if(uGroup[i].username.length()==0){
	
			uGroup[i].username = userName;
	        uGroup[i].password = passWord;
	        cout << "注册成功" << endl;
	        
	        home->fcb[i] = new FCB;
	        home->fcb[i]->innerNum = i;
	        home->fcb[i]->fileName = uGroup[i].username;//强制命名为用户名,用户无权限修改该名称
			home->fcb[i]->authority = 666;//默认新建目录权限
			home->fcb[i]->type = "dir";//文件类型为目录 
			home->fcb[i]->phNum = i;// 假设一级目录 默认块号 为i,物理块i中存储一个索引表; 
			
			home->fcb[i]->firstBlock = new PHB;//将该FCB指向其首物理块
			home->fcb[i]->firstBlock->state = 1; //表示此块已被占用 
			home->fcb[i]->firstBlock->phNum = i;//物理块内部标识的物理块号
			phBlock[i]. state = 1;  //在物理外存上进行标识,方便为新文件分配空间 
			phBlock[i].content = "用户根目录"+i; 
			 
			home->child[i] = new FSct;
			home->child[i]->fileName = uGroup[i].username;
			home->child[i]->Owner[0] = new User;//为用户文件中的结构体指针开辟空间 
            home->child[i]->Owner[0]->username=uGroup[i].username;
	        break;  
		}
	}
	return 1;
}



//获取文件大小 
double get_length(string filePath){
	double sizeKB = 0;
			
	ifstream fin( filePath );
	if( fin.is_open() )
	{
	    fin.seekg( 0, ios::end );
        double size = fin.tellg();
        if(size == 0){
		    sizeKB 如何用Android NDK编译FFmpeg

零基础 | 如何用VS Code写C/C++程序 - 安装与配置

如何用C/C++模拟post提交数据,获得http相应。

LINUX下如何用G++编c++,给一个详细的实例就好

如何用matlab模拟一个逻辑回归的方程啊,求大神帮忙写代码

如何用制作python操作系统,怎么做引导?