如何用C/C++写一个Linux文件系统模拟器
Posted 狱典司
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何用C/C++写一个Linux文件系统模拟器相关的知识,希望对你有一定的参考价值。
用C/C++写一个Linux文件系统模拟器
- 1. 程序演示
- 1. 登录--login
- 2. 切换目录--- cd
- 3. 展示文件列表---ls
- 4. 查看物理块使用情况以及物理块存储内容 — df
- 5. cd命令的延伸 :
- 6. 查看当前目录名和父目录名 —— now
- 7. 创建文件—— touch
- 8. 打开文件 —— open
- 9. 查看当前活动文件 —— ASL
- 10. 写文件操作 —— write
- 11. 读文件的全部内容 —— cat
- 12. head -num 读取文件头num行
- 13. tail -num 读取头文件num行
- 14. 关闭文件 —— close
- 15. 递归查找文件 —— find
- 16. 复制文件 —— cp
- 17. 移动文件 —— move
- 18. 创建文件夹 —— mkdir
- 19. 删除文件夹 —— rm -rf
- 20. 删除文件 —— rm -f
- 2. 代码部分
- 3.结语
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++程序 - 安装与配置