[作業系統實踐][簡單的檔案系統]
阿新 • • 發佈:2019-01-28
簡單檔案系統的實現
要求
記憶體中開闢一塊
虛擬磁碟空間
作為檔案儲存分割槽,在其上實現一個簡單的基於多級目錄的單使用者單任務系統
中的檔案系統。在退出該檔案系統的使用時,虛擬檔案系統以一個檔案的方式儲存到磁碟中,以便下次可以把它恢復到記憶體的虛擬儲存空間
實際實現
- 以上兩點均實現
能處理絕對路徑和相對路徑的命令
- 例如 :
cd /home/zy/Desktop/
這樣的絕對路徑cd ../hah/1/2
這樣的相對路徑
mkdir
,rmdir
,cd
,creat
,rm
均支援open_path
是open
的升級版,也是支援上述函式實現的主要函式。
- 例如 :
包裝
open
,read
,close
cat
直接列印檔案內容。- 檢查檔案是否開啟,如果打開了直接進行下一步,沒有就開啟
read
出所有內容- 如果之前不是開啟的那麼就關閉檔案。
截圖
建立目錄樹,
/home
是使用者的根目錄/home
下有/zy
/zy
下有/Documents
,/Desktop
,/Viedeos
,Music
等在
/home/zy/Documents
目錄下建立一個檔案Hello.txt
- 並輸入內容
Hello World!Fisrt
,能正確顯示長度和檔案內容。 - 測試了
creat
,open
,close
,read
,write
等基本用法
- 並輸入內容
利用
creat /home/zy/hellozy.txt
在/home/zy
下建立了一個hellozy.txt
- 測試了
creat
在路徑下的用法
- 測試了
測試了
mkdir
,cd
,rmdir
在路徑下也能正常工作。其餘幾個類似的同理,OVER
可以改進的地方
- 有一些BUG還待處理
- 完善異常處理機制
- 完善檔案資訊,包括建立時間,修改時間。
- 嘗試實現多工的檔案系統。
以下程式碼並非最終版本,之後還略有修改,詳細程式碼存放在github
OS.h
幾個常量定義
#include <cstdio> #include <memory.h> #include <string> #include <iostream> #include <malloc.h> #include <time.h> using namespace std; /*常量定義*/ #define Path "/home" //根目錄 #define BLOCKSIZE 1024 //磁碟塊大小 #define BLOCKCOUNT 1000 //盤塊大小 #define MAXOPENFILE 10 //能開啟最多的檔案數 #define DISKSIZE (BLOCKSIZE*BLOCKCOUNT)//磁碟大小 #define END -1 const int FCBCOUNT = BLOCKSIZE/sizeof(FCB);//一個塊的最多FCB數量
DISK,DirFile,USEROPEN,FCB定義
DISK定義
- 總共1000個磁碟塊
FAT1
:4個FAT2
:4個根目錄
1個其餘資料
991個
程式碼:
/*------------------磁碟------------------------*/
struct DISK
{
int FAT1[BLOCKCOUNT];//磁碟塊0-3代表FAT
int FAT2[BLOCKCOUNT];//磁碟塊4-7代表FAT2
DirFile RootDir; //根目錄 磁碟塊8
char Data[BLOCKCOUNT-9][BLOCKSIZE];//目錄和其他檔案 磁碟塊9~1000
};
DirFile 定義
程式碼:
/*-----------------目錄檔案---------------------*/
struct DirFile{
FCB fcb[FCBCOUNT]; //檔案控制塊
void init(int father,int self)
{
//給根目錄建立.. 和 . 序號0放".", 序號1放".."
memset(fcb,0,sizeof(fcb));
fcb[1].free=fcb[0].free=1;
fcb[1].attribute=fcb[0].attribute=1;
fcb[1].first=father;
fcb[0].first=self;
memcpy(fcb[0].filename,".",sizeof("."));
memcpy(fcb[1].filename,"..",sizeof(".."));
}
};
FCB
struct FCB
{
char filename[12]; //檔名
char attribute;//0表示目錄,1表示資料檔案
int time;//建立時間
int data;//建立日期
int first;//起始盤號
int length;//長度
char free;//表示目錄項是否為空
};
USEROPEN
struct USEROPEN
{
FCB fcb;
char dir[80];//相應開啟檔案所在的目錄名
int count;//讀寫指標在檔案的位置
char fcbstate;//是否修改了檔案的FCB內容,修改了置為1,否則置為0
char topenfile;//表示該使用者表項是否被佔用,1就是被佔用,0就是沒有被佔用
char fatherfilename[12];//上一層目錄的名字
int pos;
};
main.cpp
解析
- 全域性變數的宣告
- 簡單的處理命令列的讀入。
ls
函式
程式碼
#include "OS.h"
using namespace std;
/*-------------函式宣告------------------------*/
void help();
int cd(char *dirname);
int startsys();
int format();
int mkdir(char *dirname);
int rmdir(char *dirname);
int close(int fd);
int open(char *filename);
int creat(char *filename);
int rm(char *filename);
int filewrite(int fd);
int dowrite(int fd,char *text,int len, char wstyle);
int fileread(int fd,int len);
int doread(int fd,int len,char *text);
void exitsys();
/*--------------全域性變數-------------------------*/
char* myvhard;//虛擬磁碟起始地址
string currentdir="/home";//當前目錄
string cmd; //讀取指令
USEROPEN openfilelist[MAXOPENFILE];//檔案開啟表
USEROPEN *ptrcuridr;//當前目錄在檔案開啟表的位置
DISK* disk;//將內容結構化
char command[50];//檔名標示符
/*--------------------- 顯示目錄函式 ---------------*/
void ls() {
int BlockDirNum = (ptrcuridr->fcb).first;
DirFile *dir = (DirFile *) disk->Data[BlockDirNum - 8];
for (int i = 0; i < FCBCOUNT; i++) {
if (dir->fcb[i].free == 1) {
if (dir->fcb[i].attribute == 0)
printf("%10s---length:%5d----File\n",dir->fcb[i].filename,dir->fcb[i].length);
else
printf("%10s---length:%5d----Directory\n",dir->fcb[i].filename,dir->fcb[i].length);
}
}
}
int main() {
printf("Welcome the OS FileSystem\n");
printf("input 'help' get more information\n\n\n");
// freopen("E:\\OSFileSystem\\a.in","r",stdin);
startsys(); //Init the System
int len;
while(1)
{
cout<<currentdir+">";
cin>>cmd;
if(cmd=="help"){ //幫助
help();
}
else if(cmd=="mkdir"){
cin>>command;
mkdir(command);
}
else if(cmd=="cd"){
cin>>command;
cd(command);
}
else if(cmd=="exit") {
break;
}
else if(cmd=="rmdir"){
cin>>command;
rmdir(command);
}
else if(cmd=="ls"){
ls();
}
else if(cmd=="open"){
cin>>command;
open(command);
}
else if(cmd=="close"){
cin>>command;
close(atoi(command));
}
else if(cmd=="creat"){
cin>>command;
creat(command);
}
else if(cmd=="rm"){
cin>>command;
rm(command);
}
//
else if(cmd=="write"){
cin>>command;
filewrite(atoi(command));
}
else if(cmd=="read") {
cin >> command >> len;
fileread(atoi(command),len);
}
else if(cmd=="exitsys"){
exitsys();
}else {
printf("The cmd is not exits\n");
}
}
}
startsys.cpp
分析
int format()
- 分配磁碟空間
- 初始化根目錄
- 加入
.
和..
兩個子目錄
- 加入
int startsys()
- 申請磁碟空間
- 載入之前的磁碟,如果沒有就申請。
- 把根目錄載入進檔案開啟表。
程式碼
#include "OS.h"
/*--------------全域性變數-------------------------*/
extern char* myvhard;//虛擬磁碟起始地址
extern string currentdir;//當前目錄
extern string cmd; //讀取指令
extern USEROPEN openfilelist[MAXOPENFILE];//檔案開啟表
extern USEROPEN *ptrcuridr;//當前目錄在檔案開啟表的位置
extern DISK* disk;//將內容結構化
extern char command[50];//檔名標示符
/*--------------------------------磁碟格式化函式-------------------*/
int format() {
memset(myvhard,0,DISKSIZE);
//建立根目錄,在磁碟塊8
//前九個被FAT1+FAT2+root佔用
for(int i=0;i<9;i++){
disk->FAT1[i]=disk->FAT2[i]=-2;//-2代表被佔用
}
DirFile *dir=(DirFile *)disk->Data[8-8];//注意Data和FAT的區別
//初始化根目錄.
dir->init(8,8);
return 1;
}
/*--------------------------------進入檔案系統函式--------------------*/
int startsys() {
myvhard=(char *)malloc(DISKSIZE); //申請1024*1000磁碟空間
disk=(DISK *)myvhard;
FILE *fp=fopen("myfsys","r");
if(fp!=NULL)
{
printf("|-------------------------------------------|\n");
printf("|-----------myfsys is loading---------------|\n");
printf("|-------------------------------------------|\n\n");
fread(myvhard,sizeof(char),DISKSIZE,fp);
fclose(fp);
}
else{
printf("|-------------------------------------------|\n");
printf("|-----------myfsys is not exit--------------|\n");
printf("|--File system is being created now --------|\n");
printf("|-------------------------------------------|\n\n");
format();
}
//初始化使用者開啟表
memset(openfilelist,0,sizeof(openfilelist));
//將根目錄開啟,首先修改fcb裡的內容
openfilelist->fcb.first=8;
//檔案開啟表項的內容
openfilelist[0].topenfile=1;
strcpy(openfilelist->dir,"");
strcpy(openfilelist->fcb.filename,"home");
strcpy(openfilelist->fatherfilename,"");
ptrcuridr=&openfilelist[0];
openfilelist[0].pos=0;
//當前目錄設定為根目錄
currentdir=Path;
return 1;
}
OPEN.CPP
分析
處理好
.
和..
兩個子目錄維護好
openfilelist
裡的每個值
程式碼
#include "OS.h"
/*--------------全域性變數-------------------------*/
extern char* myvhard;//虛擬磁碟起始地址
extern string currentdir;//當前目錄
extern string cmd; //讀取指令
extern USEROPEN openfilelist[MAXOPENFILE];//檔案開啟表
extern USEROPEN *ptrcuridr;//當前目錄在檔案開啟表的位置
extern DISK* disk;//將內容結構化
extern char command[50];//檔名標示符
/*-----------------------------開啟檔案函式--------------------*/
int open(char *filename){
//檢查要被開啟檔案是否存在
int BlockDirNum = (ptrcuridr->fcb).first;
DirFile *dir = (DirFile *) disk->Data[BlockDirNum - 8];
int Fileaddr = -1;
for (int i = 0; i < FCBCOUNT; i++) {
if (dir->fcb[i].free == 1 && strcmp(dir->fcb[i].filename, filename) == 0)//表項被使用,且是目錄,且檔名相等
{
Fileaddr = i;//檔案存在
break;
}
}
//檔案不存在 輸出-1
if(Fileaddr==-1){
printf("file does not exist\n");
return -1;
}
//檢查開啟檔案表是否還有空表項,沒有報錯,有則記錄
int OpenFileaddr=-1;
for(int i=0;i<MAXOPENFILE;i++) {
if (openfilelist[i].topenfile == 0) {
OpenFileaddr=i;
}
}
//沒有空表了
if(OpenFileaddr==-1) {
printf("File open table is full \n");
return -1;
}
//如果又要開啟一個根目錄,那麼直接返回0
if(dir->fcb[Fileaddr].first==8)
{
OpenFileaddr=0;
if(ptrcuridr->fcb.first==8)
return 0;
}
//為該檔案填寫檔案開啟表項
//檢查是否已經開啟
//需要一個temp來表示實際的dir值
char temp[300];
if(strcmp(filename,"..")==0)
{
strcpy(temp,ptrcuridr->dir);
int len1=strlen(ptrcuridr->dir);
int len2=strlen(ptrcuridr->fatherfilename);
temp[len1-len2-1]=0;
}
else
{
char buffer[80];
memset(buffer,0,sizeof(buffer));
strcat(strcat(strcat(buffer,ptrcuridr->dir),"/"),ptrcuridr->fcb.filename);
strcpy(temp,buffer);
}
for(int i=1;i<MAXOPENFILE;i++) {
//"."一定是被打開了
if ((openfilelist[i].topenfile == 1 && strcmp(openfilelist[i].fcb.filename, filename) == 0 &&
strcmp(openfilelist[i].dir,temp) ==0 )||(strcmp(filename,".")==0))
{
printf(" The file has been opened !\n");
return -1;//無效返回-1
}
}
if(strcmp(filename,"..")==0)
{
openfilelist[OpenFileaddr].fcb=dir->fcb[Fileaddr];
//名字是錯的,會是"..";正確的名字在工作塊的父親名字
strcpy(openfilelist[OpenFileaddr].fcb.filename,ptrcuridr->fatherfilename);
//曾經的路徑減去名字減去'/' 就是新的
strcpy(openfilelist[OpenFileaddr].dir,ptrcuridr->dir);
int len1=strlen(openfilelist[OpenFileaddr].dir);
int len2=strlen(ptrcuridr->fatherfilename);
openfilelist[OpenFileaddr].dir[len1-len2-1]=0;
//找新的fathername,通過分析dir來得到
char test[20];
strcpy(test,openfilelist[OpenFileaddr].dir);
char *q;
int len=strlen(test);
for(int i=0;i<len;i++)
{
if(test[i]=='/'&&i!=len-1)
q=test+i+1;
}
strcpy(openfilelist[OpenFileaddr].fatherfilename,q);
}
else
{
openfilelist[OpenFileaddr].fcb=dir->fcb[Fileaddr];
char buffer[80];
memset(buffer,0,sizeof(buffer));
strcat(strcat(strcat(buffer,ptrcuridr->dir),"/"),ptrcuridr->fcb.filename);
strcpy(openfilelist[OpenFileaddr].dir,buffer);
strcpy(openfilelist[OpenFileaddr].fatherfilename,ptrcuridr->fcb.filename);
}
openfilelist[OpenFileaddr].pos=OpenFileaddr;
openfilelist[OpenFileaddr].count=0;
openfilelist[OpenFileaddr].fcbstate=0;
openfilelist[OpenFileaddr].topenfile=1;
//返回檔案描述符fd,此時的fd跟下標相同,一般不同.
if(openfilelist[OpenFileaddr].fcb.attribute==0)//如果是檔案,輸出檔案開啟符號
printf("File Open Success,The fd is %d\n",OpenFileaddr);
return OpenFileaddr;
}
OPEN_PATH.CPP
分析
讓系統函式都能處理絕對路徑和相對路徑,而不僅僅是當前目錄下的檔案了。
cd
,mkdir
,creaet
,rm
,rmdir
使用的方式是拆分路徑的元素,然後分析元素
不斷的呼叫
open
和close
來實現。
程式碼
#include "OS.h"
int open(char *filename);
int close(int fd);
/*--------------全域性變數-------------------------*/
extern char* myvhard;//虛擬磁碟起始地址
extern string currentdir;//當前目錄
extern string cmd; //讀取指令
extern USEROPEN openfilelist[MAXOPENFILE];//檔案開啟表
extern USEROPEN *ptrcuridr;//當前目錄在檔案開啟表的位置
extern DISK* disk;//將內容結構化
extern char command[50];//檔名標示符
/*----------------------更改當前目錄函式---------------------
讀入一條檔案路徑,返回一個開啟的fd
支援絕對路徑和相對路徑.
輸出,如果路徑正確,返回檔案開啟後的fd.
------------------------------------------------------*/
int open_path(char *dirname) {
//如果dirname是絕對路徑
int fd=0, ok = 1, fdtemp = -1;//ok代表是否找到dirname所指的檔案,fdtemp存臨時的,用來關閉
USEROPEN *temp = ptrcuridr;//暫時保管一下ptrcuridr原始值,可能要回溯
if (dirname[0] == '/') {
ptrcuridr = openfilelist;//我們的openfilelist[0]永遠是根目錄
//使工作目錄暫時指向根目錄
char *p = strtok(dirname + 1, "/");//用“/”分割 dirname[1]開始的字串
if (p != NULL) p = strtok(NULL, "/");//跳過/home
while (p) {
fd = open(p);
if (fd == -1) {
ok = 0;
if (fdtemp != -1) //離開前記得關檔案
close(fdtemp);//把上個開啟的檔案關掉
break;
}
ptrcuridr = openfilelist + fd;
if (fdtemp != -1)
close(fdtemp);//把上個開啟的檔案關掉
fdtemp = fd;
p = strtok(NULL, "/");
}
}
//如果是相對路徑
else {
int len=strlen(dirname);
dirname[len]='/';
dirname[len+1]=0;
char *p = strtok(dirname, "/");//用“/”分割 dirname[1]開始的字串
while (p) {
fd = open(p);
if (fd == -1) {
ok = 0;
if (fdtemp != -1) //離開前記得關檔案
close(fdtemp);//把上個開啟的檔案關掉
break;
}
ptrcuridr = openfilelist + fd;
if (fdtemp != -1)
close(fdtemp);//把上個開啟的檔案關掉
fdtemp = fd;
p = strtok(NULL, "/");
}
}
ptrcuridr = temp;
//輸出資料
if (ok == 1)
return fd;
else
return -1;
}
close.cpp
分析
- 記得把檔案開啟表的東西儲存
程式碼
#include "OS.h"
int open_path(char* dirname);
/*--------------全域性變數-------------------------*/
extern char* myvhard;//虛擬磁碟起始地址
extern string currentdir;//當前目錄
extern string cmd; //讀取指令
extern USEROPEN openfilelist[MAXOPENFILE];//檔案開啟表
extern USEROPEN *ptrcuridr;//當前目錄在檔案開啟表的位置
extern DISK* disk;//將內容結構化
extern char command[16];//檔名標示符
/*---------------關閉檔案函式-----------------*/
int close(int fd){
//檢查fd的合法性
if(fd>=MAXOPENFILE||fd<=0){
printf(" Is not a legitimate fd \n");
return -1;
}else if(openfilelist[fd].topenfile==0){
printf("filewrite ERROR:The File Don't Open\n");
return -1;
}
//檢查使用者開啟檔案表表項的`fcbstate`欄位,如果是1,則需要將該檔案的FCB的內容儲存的虛擬磁碟上該檔案的目錄項
//方法是:開啟該檔案的父目錄檔案,已覆蓋寫方式呼叫do_wirte()將欲關閉的FCB寫入父目錄檔案的相應盤塊.
if(openfilelist[fd].fcbstate==1){
char buffer[30]="..";
int fatherfd=open_path(buffer);
if(fatherfd!=-1) {
//找到相應的盤號
int pan1 = openfilelist[fatherfd].fcb.first;
int area1 = -1;
//盤號上相應的位置
DirFile *dirson = (DirFile *) (disk->Data[pan1 - 8]);
for (int i = 0; i < FCBCOUNT; i++) {
if (dirson->fcb[i].free == 1 &&
strcmp(dirson->fcb[i].filename, openfilelist[fd].fcb.filename) == 0) {
//找到了該檔案,覆蓋fcb
dirson->fcb[i] = openfilelist[fd].fcb;
break;
}
}
//關檔案
if(fatherfd!=0)
close(fatherfd);
}
}
//回收該檔案佔據的使用者開啟表表項(clear),topenfile欄位置0
memset(openfilelist+fd,0,sizeof(openfilelist[0]));
openfilelist[fd].topenfile=0;
//返回
return 0;
}
mkdir.cpp
分析
- 利用
open_path
來返開啟一個父級目錄,並返回fd
- 將當前工作目錄
ptrcuridr
的原始值儲存下來,然後將其賦值給那個父級目錄。 - 最後還原
ptrcuridr
程式碼
#include "OS.h"
int close(int fd);
int open_path(char *dirname);
int FileSubstr(char *str);
/*--------------全域性變數-------------------------*/
extern char* myvhard;//虛擬磁碟起始地址
extern string currentdir;//當前目錄
extern string cmd; //讀取指令
extern USEROPEN openfilelist[MAXOPENFILE];//檔案開啟表
extern USEROPEN *ptrcuridr;//當前目錄在檔案開啟表的位置
extern DISK* disk;//將內容結構化
extern char command[50];//檔名標示符
int mkdir(char *dirname)
{
char newdir[20];
char dirpath[60];
USEROPEN *tempp = ptrcuridr;//暫時保管一下ptrcuridr原始值,可能要回溯
int k=FileSubstr(dirname),fd=-1;
if(k!=-1) {
dirname[k] = 0;
memset(newdir, 0, sizeof(newdir));
memset(dirpath, 0, sizeof(dirpath));
strcpy(newdir, dirname + k + 1);
strcpy(dirpath, dirname);
fd = open_path(dirpath);
if(fd!=-1) {
ptrcuridr = openfilelist + fd;
dirname = newdir;
}
else {
printf("error\n");
return -1;
}
}
//-------------以下為一天前的程式碼-----------------------//
//讀取當前目錄的地址
int BlockDirNum=(ptrcuridr->fcb).first;
DirFile *dir=(DirFile *)disk->Data[BlockDirNum-8];
//遍歷檔案目錄,檢查是否有檔名相同的檔案或目錄,並找一個沒有被使用的目錄空閒表項
int temp=-1, DirFreeItems =-1;
for(int i=0;i<FCBCOUNT;i++) {
if (dir->fcb[i].free == 1 && strcmp(dir->fcb[i].filename, dirname) == 0)//表項被使用,且是目錄,且檔名相等
{
temp = i;//重名的表項
break;
}
else if (dir->fcb[i].free == 0) {
DirFreeItems = i;//目錄空閒表項
}
}
//如果檔名已存在,報錯並退出
if(temp!=-1)
{
printf("mkdir: cannot create directory '%s': Directory exists\n",dirname);
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 0;
}
//如果沒有空閒位置,報錯退出
if(DirFreeItems==-1)
{
printf("mkdir: cannot create directory '%s': Directory is full \n",dirname);
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 0;
}
//檢查FAT中是否有空閒的盤塊
int FATFreeItems=-1;
for(int i = 0;i < BLOCKCOUNT;i++)
{
if(disk->FAT1[i] == 0) {//沒被使用的塊標記為0
FATFreeItems=i;//找到了一個空閒塊
break;
}
}
//如果FAT沒有空閒塊,報錯退出
if(FATFreeItems==-1)
{
printf("mkdir: cannot create directory '%s': Disk is full \n",dirname);
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 0;
}
/*----------------開始新建目錄-------------------*/
//修改長度,fcbstate置為1
ptrcuridr->fcbstate=1;
ptrcuridr->fcb.length++;//不包括.和..的
//分配FAT的空閒塊,2表示被目錄使用
disk->FAT1[FATFreeItems]=disk->FAT2[FATFreeItems]=2;
//將改塊分配到 當前目錄的空閒專案下
strcpy(dir->fcb[DirFreeItems].filename,dirname);
dir->fcb[DirFreeItems].free=1;//被使用
dir->fcb[DirFreeItems].attribute=1;//是目錄
dir->fcb[DirFreeItems].first=FATFreeItems;//分配FAT空閒塊
dir->fcb[DirFreeItems].length=0;
dir->fcb[DirFreeItems].data=0;//時間之後弄
dir->fcb[DirFreeItems].time=0;//時間之後弄
//進入下一次目錄,初始化新獲得的塊(重置給予"."和"..")
dir=(DirFile*)(disk->Data[FATFreeItems-8]);
dir->init(BlockDirNum,FATFreeItems);
/*----------------恢復現場------------------------*/
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 1;
}
rmdir.cpp
分析
- 跟
mkdir
類似
程式碼
#include "OS.h"
int close(int fd);
int open_path(char *dirname);
int FileSubstr(char *str);
/*--------------全域性變數-------------------------*/
extern char* myvhard;//虛擬磁碟起始地址
extern string currentdir;//當前目錄
extern string cmd; //讀取指令
extern USEROPEN openfilelist[MAXOPENFILE];//檔案開啟表
extern USEROPEN *ptrcuridr;//當前目錄在檔案開啟表的位置
extern DISK* disk;//將內容結構化
extern char command[50];//檔名標示符
/*-------------------------------刪除子目錄函式---------------------*/
int rmdir(char *dirname) {
char newdir[20];
char dirpath[60];
USEROPEN *tempp = ptrcuridr;//暫時保管一下ptrcuridr原始值,可能要回溯
int k=FileSubstr(dirname),fd=-1;
if(k!=-1) {
dirname[k] = 0;
memset(newdir, 0, sizeof(newdir));
memset(dirpath, 0, sizeof(dirpath));
strcpy(newdir, dirname + k + 1);
strcpy(dirpath, dirname);
fd = open_path(dirpath);
if(fd!=-1) {
ptrcuridr = openfilelist + fd;
dirname = newdir;
}
else {
printf("error\n");
return -1;
}
}
/*---------使用open_path更新----------------------*/
//檢查檔案是否存在
int BlockDirNum = (ptrcuridr->fcb).first;
DirFile *dir = (DirFile *) disk->Data[BlockDirNum - 8];
int temp = -1;
for (int i = 0; i < FCBCOUNT; i++) {
if (dir->fcb[i].free == 1 && dir->fcb[i].attribute == 1 &&
strcmp(dir->fcb[i].filename, dirname) == 0)//表項被使用,且是目錄,且檔名相等
{
temp = i;//檔案存在
break;
}
}
//要刪除的目錄不存在
if(temp == -1) {
printf("rmdir: failed to remove '%s': No such file or directory\n",dirname);
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 0;
}
//判斷子目錄是否為空,不包括0和1,也可以用length,懶得用
DirFile *dirson=(DirFile*)(disk->Data[dir->fcb[temp].first-8]);
int flag=-1;
for(int i=2; i < FCBCOUNT; i++ ) {
if (dir->fcb[i].free == 1){
flag=1;
break;
}
}
//要刪除的目錄不為空
if(flag==-1)
{
printf("rmdir: failed to remove '%s': Directory not empty\n",dirname);
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 0;
}
//檢查目錄是否已開啟,開啟就關閉
//回收該目錄檔案所佔據的磁碟塊,修改FAT
disk->FAT1[dir->fcb[temp].first]=disk->FAT1[dir->fcb[temp].first]=0;
//當前目錄檔案中清空該目錄檔案的目錄項,memset清理乾淨
memset(&(dir->fcb[temp]),0,sizeof(dir->fcb[temp]));
//修改長度,表項的fcbstate置為1
ptrcuridr->fcbstate=1;
ptrcuridr->fcb.length--;//不包括.和..的
/*-----------------恢復現場-------------*/
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 1;
}
cd.cpp
分析
- 善於利用
open_path
十分簡單的實現一個跳轉到任意路徑的函式
程式碼
#include "OS.h"
int open(char *filename);
int close(int fd);
int open_path(char *dirname);
/*--------------全域性變數-------------------------*/
extern char* myvhard;//虛擬磁碟起始地址
extern string currentdir;//當前目錄
extern string cmd; //讀取指令
extern USEROPEN openfilelist[MAXOPENFILE];//檔案開啟表
extern USEROPEN *ptrcuridr;//當前目錄在檔案開啟表的位置
extern DISK* disk;//將內容結構化
extern char command[50];//檔名標示符
/*----------------------更改當前目錄函式---------------------*/
int cd(char *dirname) {
USEROPEN *temp = ptrcuridr;//暫時保管一下ptrcuridr原始值,可能要回溯
int fd=open_path(dirname);
if (fd != -1) {
//關掉舊的描述符
int old=temp->pos;
//獲取舊的描述符
if (old != 0) //根目錄描述符不關
close(old);
//工作目錄指向它
ptrcuridr = openfilelist + fd;
//當前目錄賦值
currentdir= ptrcuridr->dir ;
currentdir+= '/';
currentdir+= ptrcuridr->fcb.filename;
return 0;
}
else {
ptrcuridr = temp;
printf("No such file or directory\n");
return 0;
}
}
檔案操作
creat.cpp
分析
- 有了
open_path
so easy
程式碼
#include "OS.h"
int close(int fd);
int open_path(char *dirname);
int FileSubstr(char *str);
/*--------------全域性變數-------------------------*/
extern char* myvhard;//虛擬磁碟起始地址
extern string currentdir;//當前目錄
extern string cmd; //讀取指令
extern USEROPEN openfilelist[MAXOPENFILE];//檔案開啟表
extern USEROPEN *ptrcuridr;//當前目錄在檔案開啟表的位置
extern DISK* disk;//將內容結構化
extern char command[50];//檔名標示符
int creat(char *filename){
//為新檔案分配一個空閒開啟檔案表項,如果沒有空閒表項就返回 -1
//檢查開啟檔案表是否還有空表項,沒有報錯,有則記錄
int OpenFileaddr=-1;
for(int i=0;i<MAXOPENFILE;i++) {
if (openfilelist[i].topenfile == 0) {
OpenFileaddr=i;
}
}
//沒有空表了
if(OpenFileaddr==-1) {
printf("File open table is full \n");
return -1;
}
/*-----------開啟路徑所指的父目錄---------*/
char newdir[20];
char dirpath[60];
USEROPEN *tempp = ptrcuridr;//暫時保管一下ptrcuridr原始值,可能要回溯
int k=FileSubstr(filename),fd=-1;
if(k!=-1) {
filename[k] = 0;
memset(newdir, 0, sizeof(newdir));
memset(dirpath, 0, sizeof(dirpath));
strcpy(newdir, filename + k + 1);
strcpy(dirpath, filename);
fd = open_path(dirpath);
if(fd!=-1) {
ptrcuridr = openfilelist + fd;
filename = newdir;
}
else {
printf("error\n");
return -1;
}
}
//檢查重名
int BlockDirNum = (ptrcuridr->fcb).first;
DirFile *dir = (DirFile *) disk->Data[BlockDirNum - 8];
int temp = -1,DirFreeItems =-1;
for (int i = 0; i < FCBCOUNT; i++) {
if (dir->fcb[i].free == 1 &&
strcmp(dir->fcb[i].filename, filename) == 0)//表項被使用,且是目錄,且檔名相等
{
temp = i;//有重名,可能檔案,可能資料夾
break;
}
else if (dir->fcb[i].free == 0) {
DirFreeItems = i;//目錄空閒表項
}
}
//如果檔名已存在,報錯並退出
if(temp!=-1)
{
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
printf("creat: cannot create file '%s': File exists\n",filename);
return 0;
}
//如果沒有空閒位置,報錯退出
if(DirFreeItems==-1)
{
printf("creat: cannot create file '%s': Directory is full \n",filename);
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 0;
}
//檢查FAT中是否有空閒的盤塊
int FATFreeItems=-1;
for(int i = 0;i < BLOCKCOUNT;i++)
{
if(disk->FAT1[i] == 0) {//沒被使用的塊標記為0
FATFreeItems=i;//找到了一個空閒塊
break;
}
}
//如果FAT沒有空閒塊,報錯退出
if(FATFreeItems==-1)
{
printf("mkdir: cannot create directory '%s': Disk is full \n",filename);
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 0;
}
/*----------------開始新建檔案-------------------*/
//修改長度,fcbstate置為1
ptrcuridr->fcbstate=1;
ptrcuridr->fcb.length++;//不包括.和..的
//準備好新檔案的FCB的內容,檔案屬性為資料檔案,長度0.
//分配FAT的空閒塊,-3表示被檔案使用,-1表示檔案結尾
disk->FAT1[FATFreeItems]=disk->FAT2[FATFreeItems]=-1;
//將改塊分配到 當前目錄的空閒專案下
strcpy(dir->fcb[DirFreeItems].filename,filename);
dir->fcb[DirFreeItems].free=1;//被使用
dir->fcb[DirFreeItems].attribute=0;//是檔案
dir->fcb[DirFreeItems].first=FATFreeItems;//分配FAT空閒塊
dir->fcb[DirFreeItems].length=0;
dir->fcb[DirFreeItems].data=0;//時間之後弄
dir->fcb[DirFreeItems].time=0;//時間之後弄
//檔案新建完畢
//填寫檔案開啟表項
openfilelist[OpenFileaddr].fcb=dir->fcb[DirFreeItems];
char buffer[80];
memset(buffer,0,sizeof(buffer));
strcat(strcat(strcat(buffer,ptrcuridr->dir),"/"),ptrcuridr->fcb.filename);
strcpy(openfilelist[OpenFileaddr].dir,buffer);
strcpy(openfilelist[OpenFileaddr].fatherfilename,ptrcuridr->fcb.filename);
openfilelist[OpenFileaddr].pos=OpenFileaddr;
openfilelist[OpenFileaddr].count=0;
openfilelist[OpenFileaddr].fcbstate=0;
openfilelist[OpenFileaddr].topenfile=1;
//關閉檔案走人
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
//返回檔案描述符fd,此時的fd跟下標相同,一般不同.
printf("File Creat Success,The fd is %d\n",OpenFileaddr);
return OpenFileaddr;
}
rm.cpp
定義
- 記得遍歷連結串列釋放檔案的空間
- 連結串列的結尾是
-1
- 連結串列的結尾是
程式碼
#include "OS.h"
int close(int fd);
int open_path(char *dirname);
int FileSubstr(char *str);
/*--------------全域性變數-------------------------*/
extern char* myvhard;//虛擬磁碟起始地址
extern string currentdir;//當前目錄
extern string cmd; //讀取指令
extern USEROPEN openfilelist[MAXOPENFILE];//檔案開啟表
extern USEROPEN *ptrcuridr;//當前目錄在檔案開啟表的位置
extern DISK* disk;//將內容結構化
extern char command[50];//檔名標示符
/*--------------------------刪除檔案函式---------------------*/
int rm(char *filename){
/*--------------------------開啟上級目錄---------------------*/
char newdir[20];
char dirpath[60];
USEROPEN *tempp = ptrcuridr;//暫時保管一下ptrcuridr原始值,可能要回溯
int k=FileSubstr(filename),fd=-1;
if(k!=-1) {
filename[k] = 0;
memset(newdir, 0, sizeof(newdir));
memset(dirpath, 0, sizeof(dirpath));
strcpy(newdir, filename + k + 1);
strcpy(dirpath, filename);
fd = open_path(dirpath);
if(fd!=-1) {
ptrcuridr = openfilelist + fd;
filename = newdir;
}
else {
printf("error\n");
return -1;
}
}
//呼叫read,判斷目錄下檔案是否存在
int BlockDirNum = (ptrcuridr->fcb).first;
DirFile *dir = (DirFile *) disk->Data[BlockDirNum - 8];
int temp = -1;
for (int i = 0; i < FCBCOUNT; i++) {
if (dir->fcb[i].free == 1 && dir->fcb[i].attribute == 0 &&
strcmp(dir->fcb[i].filename, filename) == 0)//表項被使用,且是檔案,且檔名相等
{
temp = i;//檔案存在
break;
}
}
//要刪除的目錄不存在
if(temp == -1) {
printf("rm: failed to remove '%s': No such file or directory\n",filename);
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 0;
}
//檢查該檔案是否開啟,若開啟則關閉
char buffer[80];
memset(buffer,0,sizeof(buffer));
strcat(strcat(strcat(buffer,ptrcuridr->dir),"/"),ptrcuridr->fcb.filename);
for(int i=1;i<MAXOPENFILE;i++) {
//"."一定是被打開了
if ((openfilelist[i].topenfile == 1 && strcmp(openfilelist[i].fcb.filename, filename) == 0 &&
strcmp(openfilelist[i].dir,buffer) ==0 ))
{
printf(" The file been opened,Now Close it !\n");
close(i);
break;
}
}
//回收磁碟,一個連結串列
int TEMP=0;
for(int p=dir->fcb[temp].first;p!=-1;p=TEMP)
{
TEMP=disk->FAT1[p];
disk->FAT1[p]=disk->FAT2[p]=0;
}
//清空該目錄項,free欄位為0,
memset(&(dir->fcb[temp]),0,sizeof(dir->fcb[temp]));
//修改長度,表項的fcbstate置為1
ptrcuridr->fcbstate=1;
ptrcuridr->fcb.length--;//不包括.和..的
/*-----------------恢復現場-------------*/
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 1;
}
dowrite.cpp
分析
- 每次循處理一塊磁碟
- 檔案指標轉換為邏輯塊塊號
blockno
和 塊內偏移blockoff;
程式碼
#include "OS.h"
int open_path(char* dirname);
/*--------------全域性變數-------------------------*/
extern char* myvhard;//虛擬磁碟起始地址
extern string currentdir;//當前目錄
extern string cmd; //讀取指令
extern USEROPEN openfilelist[MAXOPENFILE];//檔案開啟表
extern USEROPEN *ptrcuridr;//當前目錄在檔案開啟表的位置
extern DISK* disk;//將內容結構化
extern char command[16];//檔名標示符
/*---------------實際寫檔案函式----------------*/
int dowrite(int fd,char *text,int len, char wstyle) {
//申請1024位元組的緩衝區buf
char *buf = (char *) malloc(1024);
if (buf == NULL) {
printf("MALLOC ERROR b\n");
return -1;
}
int tmplen = 0;
int textpos = 0;
int textlen = strlen(text);
//將檔案指標轉換為邏輯塊塊號blockno 和 塊內偏移blockoff;
/*--------------------分配-----------------*/
while(tmplen<len) {
int blockno = (openfilelist[fd].count) / 1024;
int blockoff = (openfilelist[fd].count) % 1024;
//尋找磁碟塊blockno
int currentblock = 0;
int cnt = 0;
for (int p = openfilelist[fd].fcb.first; p != -1; p = disk->FAT1[p]) {
cnt++;
currentblock = p;
if (cnt == blockno + 1)
break;
}
int pre = currentblock;
if (cnt != blockno + 1)//如果找不到這樣的一塊,那麼還需要給它分配blockno+1-cnt塊
{
//從currentblock開始分配
for (int i = 1; i <= blockno + 1 - cnt; i++) {
//檢查FAT中是否有空閒的盤塊
int FATFreeItems = -1;
for (int i = 0; i < BLOCKCOUNT; i++) {
if (disk->FAT1[i] == 0) {//沒被使用的塊標記為0
FATFreeItems = i;//找到了一個空閒塊
break;
}
}
//如果FAT沒有空閒塊,報錯退出
if (FATFreeItems == -1) {
printf("FAT IS FULL\n");
return -1;
}
disk->FAT1[pre] = FATFreeItems;
pre = FATFreeItems;
}
}
//如果是覆蓋寫,或者塊內偏移off不等於0,則將blkno的虛擬磁碟塊全部寫入buff中,否則memset
if (wstyle == 2 || blockoff != 0) {
memcpy(buf, disk->Data[currentblock - 8], 1024);
}
//將text中的內容暫存到緩衝區buff的第off位元組開始的位置,直到緩衝區滿
for (int i = blockoff; i < 1024 && textpos < textlen && tmplen<len; i++) {
buf[i] = text[textpos];
textpos++;
tmplen++; //讀入長度
}
memcpy(disk->Data[currentblock - 8], buf, 1024);
openfilelist[fd].count += tmplen;
}
free(buf);
return tmplen;
}
filewrite.cpp
分析
- 實際的寫函式
程式碼
#include "OS.h"
int open_path(char* dirname);
int dowrite(int fd,char *text,int len, char wstyle);
/*--------------全域性變數-------------------------*/
extern char* myvhard;//虛擬磁碟起始地址
extern string currentdir;//當前目錄
extern string cmd; //讀取指令
extern USEROPEN openfilelist[MAXOPENFILE];//檔案開啟表
extern USEROPEN *ptrcuridr;//當前目錄在檔案開啟表的位置
extern DISK* disk;//將內容結構化
extern char command[16];//檔名標示符
/*---------------寫檔案函式----------------*/
int filewrite(int fd) {
int way = 0;
//檢查fd的有效性
if (fd >= MAXOPENFILE || fd <= 0) {
printf("filewirt ERROR:Is not a legitimate fd \n");
return -1;
}else if(openfilelist[fd].topenfile==0){
printf("filewrite ERROR:該檔案沒有被開啟\n");
return -1;
}
while (1) {
//提示等待使用者輸入寫方式
printf(" ------Please enter the way to write---------\n ");
//1 : 截斷寫 2: 覆蓋寫 3: 追加寫
printf(" ------1:TRUNC 2:OVER 3:APPEND---------\n ");
scanf("%d", &way);
if (1 <= way && way <= 3) break;
else printf("Input Error,Please Try Again\n");
}
// 如果是截斷寫,釋放檔案除第一塊外的磁碟空間內容
//記憶體使用者開啟表中檔案長度為0,,讀寫指標置為0
if (way == 1) {
//釋放檔案除第一塊外的磁碟空間內容
int TEMP = 0;
int ok = 1;
for (int p = openfilelist[fd].fcb.first; p != -1; p = TEMP) {
TEMP = disk->FAT1[p];
if (ok != 1) {
disk->FAT1[p] = disk->FAT2[p] = 0;
}
else {
ok = 0;
}
}
//長度置為0
openfilelist[fd].fcb.length = 0;
//讀寫指標置為0
openfilelist[fd].count = 0;
}
//如果是追加寫,修改檔案的當前讀寫指標到檔案的末尾
else if (way == 3) {
openfilelist[fd].count = openfilelist[fd].fcb.length;
}
//提示使用者,輸入內容通過CTRL+Z結束,使用者可分多次輸入寫入內容,每次用回車結束
printf(" Input CTRL+D end the input\n ");
int temp=0;
char buffer[3000];
while(gets(buffer)!=0){
int len=strlen(buffer);
buffer[len]='\n';
buffer[len+1]='\0';
int ret=dowrite(fd,buffer,strlen(buffer),way);
if(ret==-1) {
return -1;
}
else temp+=ret;
}
//如果當前讀寫指標位置大於長度,則更新長度,並置fcbstate置1
if(openfilelist[fd].count>openfilelist[fd].fcb.length) {
openfilelist[fd].fcb.length = openfilelist[fd].count;
openfilelist[fd].fcbstate=1;
}
//返回實際寫入的位元組
return temp;
}
doread.cpp
分析
- 每次讀一片磁碟
程式碼
#include "OS.h"
int open_path(char* dirname);
int dowrite(int fd,char *text,int len, char wstyle);
/*--------------全域性變數-------------------------*/
extern char* myvhard;//虛擬磁碟起始地址
extern string currentdir;//當前目錄
extern string cmd; //讀取指令
extern USEROPEN openfilelist[MAXOPENFILE];//檔案開啟表
extern USEROPEN *ptrcuridr;//當前目錄在檔案開啟表的位置
extern DISK* disk;//將內容結構化
extern char command[16];//檔名標示符
/*------------------實際讀檔案函式--------------------------*/
//text的指向那個讀出資料的使用者地址
int doread(int fd,int len,char *text){
//申請1024位元組的緩衝區buf
char *buf = (char *) malloc(1024);
if (buf == NULL) {
printf("MALLOC ERROR b\n");
return -1;
}
int tmplen = 0;
int textpos = 0;
//將最終指標轉換為邏輯塊塊號blockno 和 塊內偏移blockoff;
while(tmplen<len) {
int blockno = (openfilelist[fd].count) / 1024;
int blockoff = (openfilelist[fd].count) % 1024;
//尋找磁碟塊blockno
int currentblock = 0;
int cnt = 0;
for (int p = openfilelist[fd].fcb.first; p != -1; p = disk->FAT1[p]) {
cnt++;
currentblock = p;
if (cnt == blockno + 1)
break;
}
memcpy(buf, disk->Data[currentblock - 8], 1024);
//
for (int i = blockoff; i < 1024 && tmplen<len && openfilelist[i].count<openfilelist[i].fcb.length; i++) {
text[textpos] = buf[i];
textpos++;
tmplen++; //讀入長度
openfilelist[fd].count;
}
memcpy(disk->Data[currentblock - 8], buf, 1024);
}
free(buf);
return tmplen;
}
fileread.cpp
分析
- 實際的讀函式
程式碼
#include "OS.h"
int open_path(char* dirname);
int dowrite(int fd,char *text,int len, char wstyle);
int doread(int fd,int len,char *text);
/*--------------全域性變數-------------------------*/
extern char* myvhard;//虛擬磁碟起始地址
extern string currentdir;//當前目錄
extern string cmd; //讀取指令
extern USEROPEN openfilelist[MAXOPENFILE];//檔案開啟表
extern USEROPEN *ptrcuridr;//當前目錄在檔案開啟表的位置
extern DISK* disk;//將內容結構化
extern char command[16];//檔名標示符
/*---------------讀檔案函式----------------*/
const int MAXSIZE=1024*50;
int fileread(int fd,int len){
char text[MAXSIZE];
memset(text,0,sizeof(text));
//檢查fd的有效性
if (fd >= MAXOPENFILE || fd <= 0) {
printf("filewirt:Is not a legitimate fd \n");
return -1;
}else if(openfilelist[fd].topenfile==0){
printf("filewrite ERROR:The File Don't Open\n");
return -1;
}
//呼叫do_read()讀取指定檔案的len位元組內容到text[]中.
int rt=doread(fd,len,text);
//如果do_read()返回值為負,則顯示出錯資訊,否則將text[]中的內容顯示到螢幕上;
if(rt==-1){
printf("READ FAIL");
return -1;
}else{
//輸出text的內容
for(int i=0;i<len;i++){
printf("%c",text[i]);
}
printf("\n");
}
}
exitsys.cpp
分析
- 儲存並退出
- 關閉所有開啟的檔案
程式碼
#include "OS.h"
int open_path(char* dirname);
int close(int fd);
/*--------------全域性變數-------------------------*/
extern char* myvhard;//虛擬磁碟起始地址
extern string currentdir;//當前目錄
extern string cmd; //讀取指令
extern USEROPEN openfilelist[MAXOPENFILE];//檔案開啟表
extern USEROPEN *ptrcuridr;//當前目錄在檔案開啟表的位置
extern DISK* disk;//將內容結構化
extern char command[16];//檔名標示符
/*--------------退出檔案系統函式------------------*/
void exitsys(){
FILE * fd=fopen("myfsys","w");
//關閉所有開啟的檔案
for(int i=0;i<MAXOPENFILE;i++){
if(openfilelist[i].topenfile==1)
close(i);
}
fwrite(myvhard,sizeof(char),DISKSIZE,fd);
fclose(fd);
free(myvhard);
exit(0);
}
幾個無關緊要的函式
#include "stdio.h"
void help()
{
printf("\n");
printf("-----------------------help------------\n");
printf("format :-------Format The Disk.\n");
printf("exit :-------Exit OS File System AND **NOT SAVE**\n");
printf("exitsys :-------Exit OS File System AND SAVE")
printf("cd dirname :-------Change Directory\n");
printf("mkdir dirname :-------Make Directory.\n");
printf("rmdir dirname :-------Delete Directory.\n");
printf("ls dirname :-------List Directory .\n");
printf("creat filename:-------Creat File\n");
printf("write fd :-------Wirte File\n");
printf("read fd :-------Read File\n");
printf("rm filename:-------Remove File\n");
printf("open filename:-------Open File\n");
printf("close fd :-------Close File\n");
printf("open_path\n");
printf("--------------------------------------\n\n");
}
和自己實現的分割字串
//
// 切割字串,例如/A/B/C/D 切割成 /A/B/C 和 D
//
#include "OS.h"
/*--------------全域性變數-------------------------*/
extern char* myvhard;//虛擬磁碟起始地址
extern string currentdir;//當前目錄
extern string cmd; //讀取指令
extern USEROPEN openfilelist[MAXOPENFILE];//檔案開啟表
extern USEROPEN *ptrcuridr;//當前目錄在檔案開啟表的位置
extern DISK* disk;//將內容結構化
extern char command[50];//檔名標示符
/*-----------------------------------------------*/
int FileSubstr(char *str){
int len=strlen(str);
int cnt=0,flag=0;
for(int i=1;i<len-1;i++)
{
if(str[i]=='/')
{
cnt++;
flag=i;
}
}
if(cnt==0) return -1;
else return flag;
}