模拟UNIX(linux)文件系统
操作系统课程设计
一、实验内容
1、 题目:模拟UNIX(linux)文件系统
[问题描述]
在任一OS下,建立一个大文件,把它假象成一张盘,在其中实现一个简单的 模拟UNIX文件系统 。
二、概要设计
在现有机器硬盘上开辟20M的硬盘空间(利用一个循环操作,在Disk中写入20M的零,创建一个20M的文件即是),作为设定的硬盘空间。
磁盘块物理模型如下
文件则是指具有文件名的若干相关元素的集合。
文件属性主要如下
文件名:实现了按名存取,文件名和目录文件允许重名。
文件类型:可以从不同的角度来规定文件的类型。普通文件、管道文件、块文件。
文件长度:指文件的当前长度,长度的单位可以是KB。
文件的物理位置:文件在磁盘中物理的存储,并打印出来。
此次UNIX文件系统最简单的目录结构。整个文件系统中只建立一张目录表,每个文件一个目录项,目录项含有文件相关信息。每建立一个新文件要先检索所有的目录项保证文件名唯一。然后找出一空白目录项填入相关信息,并修改状态位。删除文件是找到对应目录项,回收所占用空间,清除该目录。
逻辑结构如下
文件名 |
索引节点编号 |
文件名1 |
INode |
文件名2 |
INode |
文件名3 |
INode |
…. |
…. |
Unix文件系统当文件很多时,文件目录要占用大量的盘块。在查找目录的过程中,可能需要多次启动磁盘读入目录文件的盘块。在检索目录文件中只用到了文件名,显然,文件的物理地址等文件的描述信息在检索目录时不需调入内存。为此,可以把文件名与文件描述信息分开。使文件描述信息单独形成一个索引结点。
把文件描述信息单独形成一个称为索引结点的数据结构,简称为inode;文件目录中的每个目录项,则仅由文件名及指向该文件所对应的inode的指针所构成。这样,为找到一个文件的平均启动磁盘的次数减少很多
模型如下:
Root |
Fan.txt |
A |
B |
|
|
存储空间的分配与回收
成组链接法
首先,建立操作系统课程的设计模型。这个系统将利用一个20M的文件作为自己的磁盘空间,设计时由于一个盘块占用1KB,所以20M空间可以产生20480个盘块系统本身将0# - 30#块作为系统区,所以用户区便剩下20450个盘块,每50个盘块为一组,一共可以分为409个组。
将每一组含有的盘块总数N和该组的盘块号,记入其前一组的第一个盘块的S.free(1)~S.free(50)。这样由各组的第一个盘块形成了一条链。
将第一组的盘块总数和所有的盘块号,记入空闲盘块号栈中,作为当前可供分配的空闲盘块号。最末一组只有49个盘块,其盘块号分别记入其前一组的S.free(1)~S.free(99)中,而在S.free(0)中则存放0,作为空闲盘块链的结束标志。
基本功能
1、初始化
2、建立文件
3、建立子目录
4、打开文件
5、删除文件
6、删除目录
7、显示目录
三、详细设计和编码
正规文件
i 结点文件类型 目录文件
(共1byte) 块设备
管道文件
。物理地址(索引表)
共有13个表项,每表项2byte
。文件长度 4byte
。联结计数 1byte
struct INode{
FileSpec fileSpec;
short iaddr[13];
int fileLength;
int linkCount;
};
文件名 14byte
(5)目录项信息
i 结点号 2byte
struct DirChild
{
char filename[14];
short i_BNum ;
};
struct DirNode{
DirChild childItem[64];
short DirCount;
};
定义磁盘文件名
const char FileName[]="os.txt";
默认为空的文件名
const char NullName[]="0000000000000";
默认目录文件的长度
const int DirLen=1;
默认超级块的快号
const short SSNum=-1; //super block num
定义枚举类型,普通,目录,块文件,管道文件
enum FileSpec{NORMAL,DIR,BLOCK,PIP};//0,1,2,3
short SS[51]; //超级栈,指针为SS[0],保存当前可用盘快
short freeBlockNum=0; //当前可用盘快所在组记录的盘快号
short freeTotalB=20450; 文件总长度
short freeDirNode[29]; //可用索引节点栈
short freeDirCount=30; //索引节点栈指针
short currDirNum; //当前目录所在的磁盘号
short currINum;
DirNode *currDir; //当前目录节点
INode *iNode; //当前iNode 节点
系统调用函数列表
系统调用原型 |
功能 |
入口、出口参数说明 |
Void ArrarEqual(short arr[51],short begin,short end) |
arr[51]数组赋值,在成组链接初始化时使用 |
|
void BWrite(short arr[51],short diskNum) |
往磁盘中写入短数组 |
|
void BWriteArr(short arr[512],short diskNum ) |
重构BWrite,实现一个数组的写入,数组长度不确定
|
|
void BRead(INode *iNode,short diskNum) |
从磁盘中读出iNode节点 |
|
void BRead(short arr[51],short diskNum) |
从磁盘中读出数组
|
|
void BReadArr(short arr[512],short diskNum) |
从磁盘中读出数组,成组链接多级索引使用 |
|
BWrite(DirNode *currDir,short diskNum) |
写入一个目录项 |
|
Void BWrite(INode *iNode,short diskNum) |
写入一个iNode |
|
Void AssAnEmpty() |
分配一个空闲的普通快 |
|
short AssAnDir() |
分配一个空闲的目录快 |
|
short IsFileExist(DirNode *currDir,char fileName[14]) |
判断一个文件是否存在 |
-1不存在,否则返回文件所在磁盘号 |
bool IsFile(INode *iNode,short diskNum) |
判断一个文件是一个普通文件 |
|
bool IsDir(INode *iNode,short diskNum) |
判断一个文件是一个索引文件 |
|
void CreateINode(INode *iNode,FileSpec fileSpec,short linkCount,short length) |
创建一个iNode,并分配磁盘空间 |
|
void CleanINode(INode *iNode) |
清空iNode信息,并分配磁盘空间 |
|
void InsertDir(DirNode *currDir,char fileName[14],short blockNum) |
将当前目录项插入到内存中目录项中 |
|
void ShowFileInfo(INode *iNode,char fileName[14]) |
打开一个文件,显示文件信息 |
|
void ShowBlockInfo(INode *iNode) |
打开一个文件,显示文件占用磁盘空间信息 |
|
void DelDirItem(DirNode *currDir,char fileName[14]) |
删除索引中一项 |
|
void CallBackOne(short diskNum) |
回收一块空余磁盘片 |
|
void CallBackINode(short diskNum) |
回收文件的iNOde节点 |
|
主要函数列表
函数原型 |
功能 |
入口、出口参数说明 |
Format |
创建20M磁盘 |
|
void Init(DirNode *currDir,FileSpec fielSpec,char filename[14],INode *iNode,short diskNum) |
初始化,创建个目录节点并初始化超级栈
|
|
Init() |
初始化索引栈 |
|
void Linkdisk() |
成组链接初始化 |
|
void InitCreate(DirNode *currDir,FileSpec fielSpec,char filename[14],INode *iNode,short diskNum)
|
创建一个文件节点,并分配INOde和磁盘空间
|
|
void Create(DirNode *currDir,char fileName[14],INode *iNode,short length,FileSpec fileSpec) |
存在文件,并分配iNOde节点和磁盘空间 |
|
|
|
|
void Mf(DirNode *currDir,char fileName[14],INode *iNode,short length) |
创建一个文件 |
|
void Md(DirNode *currDir,char fileName[14],INode *iNode,short length) |
在当前目录创建一个子目录 |
|
void Open(DirNode *currDir,char fileName[14],INode *iNode) |
打开文件,显示文件信息 |
|
void Del(DirNode *currDir,char fileName[14],INode *iNode) |
删除一个文件 |
|
void Rd(DirNode *currDir,char fileName[14],INode *iNode) |
删除一个目录 |
|
void Dir(DirNode *currDir,INode *iNode) |
显示目录项的内容 |
|
void exit(DirNode *currDir,INode *iNode) |
退出,并销毁资源 |
|
void AuthorMessage() |
打印版权信息 |
|
void help() |
提示信息 |
|
主框架
初始化命令——format
利用文件创建命令可在硬盘上创建一个名为Disk的20M的空间,并将该磁盘空间整体假想成一个独立硬盘,我们将在这张封闭的磁盘上进行各种关于文件的操作。Format 具体操作如下:
(1) 利用一个循环操作,在Disk中写入20M的零。
(2) 然后将0#块,即超级块中写入31# - 80#的盘块号,并将全部可用剩余盘块数FreeBlock=20450记录在0#块中。
(3) 同时将80#,130#,180#(间距50)等408个(第409个组头不存在下一组)盘组的组头中写入成组连接好的下一组盘块号。
2、系统初始化——自动完成
假如Disk已被格式化完毕,那么系统便将进行下一步,那就是初始化。初
始化主要完成将根目录项读入内存,便于文件操作,并将0#超级块中保存的当前可用盘块号写入内存中的超级栈中,具体操作如下:
(1) 初始化一个超级栈,将超级块中保存的当前可分配盘块号读入栈中。
(2) 定义一个全局变量FreeBlock,用来记录当前全部可用的盘块总数。
3、建立文件——mf filename
如果终端发来的命令格式为mf filename,则可以判断出这是建立文件的命
令。要想成功建立一个文件,首先需要判断在同级目录下是否有重名的文件,并且需要判断是否有足够的空间允许建立一个指定长度的文件。mf(代表make file)具体操作如下:
(1) 判断所要建立的文件是否重名。首先将终端输入的文件名filename在RootDir[640]进行检索,看是否可以找到相同的名字,假如找到了相同的名字还不能立刻判断是重名了,因为同级目录下允许文件夹与文件重名,然后利用DirItem结构中的最后2个Byte要找到暂时重名文件的i结点,从i结点中读出该文件的属性,看是为正规文件还是文件夹。如果重名了,系统会提示,否则进行下一步。
(2) 如果不重名,则要根据用户输入的文件长度来判断当前的剩余盘块总数是否足够分配,这项工作不仅仅是用终端输入来的length与剩余盘块总数FreeBlock比较,因为Unix本身采取混合索引方式,所以根据文件的长度会分配不同的索引盘块数。所以判断盘块数是否足够同时要计算出相应的索引盘块数。如果文件数据本身盘块数加上相应的索引盘块数不会超过总剩余盘块数,则可进行分配。
4、建立子目录——md filename
建立子目录,即建立一个文件夹。其可以看作是建立文件的一个特殊情况,
建立一个文件夹的过程和上述的建立文件非常相似,也要判断重名,也要判断是否盘块数足够分配,以及分配时所要考虑到的全部情况(文件夹利用0级索引)。
5、 打开文件——open filename(显示文件所占的盘块)
(a) 这个命令就是所谓的读文件命令,在这个模拟Unix的系统中,我们将显示文件所占的盘块以及文件的相关信息。要打开文件,当然首先要判断是否有这样一个文件名,然后才是读出全部的盘块数
6、 删除文件——del filename
删除文件同创建文件都是非常复杂的操作,要有多种情况需要考虑。要想成功删除一个文件,首先需要判断在同级目录下是否有该文件名,然后在根据该文件为几级索引文件进行相应的回收
7、 删除目录——rd filename
就如同建立目录好似建立文件的一种特殊情况,删除目录也和删除文件差不多,但是有一点是绝对不同的,要特别注意。当子目录下建立了文件或者文件夹时,即表示子目录不为空,那么系统本身为了保持数据的完整性,是不对该目录进行删除操作的,而提示用户该目录不为空。除非用户删除了该目录下的全部文件后,返回到根目录,此时子目录为空,才允许系统删除该文件夹。删除时的具体过程同删除0级索引文件极为相似,这里就不作过多说明。文件流程如删除文件,此处不再赘述。
8、 显示目录——dir(即显示目录下的信息,包括文件、子目录等)
Dir是一个很容易实现的命令,在目录下输入此命令,即可以获得目录下的信息,包括文件、子目录等。
11、正常退出指令exit
不要小看了这条指令,它可以帮你完成许多被你遗忘的操作,由于此次代码编写超过了3000行,而且涉及到大量的文件操作函数,所以难免会对文件的关闭,栈的释放等结束工作考虑不周全,为了提高数据的可靠性与完整性,建议退出系统时用此命令,它可以自动完成文件的关闭。
程序源代码
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
const char FileName[]="os.txt";
const char NullName[]="0000000000000";
const int DirLen=1;
const short SSNum=-1; //super block num
enum FileSpec{NORMAL,DIR,BLOCK,PIP};//0,1,2,3
//i节点结构信息
struct INode{
FileSpec fileSpec;
short iaddr[13];
int fileLength;
int linkCount;
};
struct DirChild
{
char filename[14];
short i_BNum ;
};
struct DirNode{
DirChild childItem[64];
short DirCount;
};
short SS[51]; //超级栈,指针为SS[0]
short freeBlockNum=0; //当前可用盘快所在组记录的盘快号
short freeTotalB=20450;
short freeDirNode[29]; //可用索引节点栈
short freeDirCount=30; //索引节点栈指针
short currDirNum; //当前目录所在的磁盘号
short currINum;
DirNode *currDir;
INode *iNode;
//================================================================
//函数描述:创建20M磁盘
//入口参数:无
//返回值: 无
//===============================================================
void Format()
{
cout<<"系统正在初始化......"<<endl;
// 打开文件
FILE *f = fopen(FileName,"w+");
if (f == NULL)
{
cout<<"程序创建错误,请重新输入"<<endl;
return ;
}
for(int i=0;i<20971520;i++)//20971520=20Mb,暂时2mb
fprintf(f,"%c",'0');
// 关闭文件
fclose(f);
}
//================================================================
//函数描述:数组赋值
//入口参数:无
//返回值: 无
//===============================================================
void ArrarEqual(short arr[51],short begin,short end)
{
for(short i=0;i<end-begin+1;i++)
arr[50-i]=begin+i;
}
//================================================================
//函数描述:数组赋值
//入口参数:无
//返回值: 无
//===============================================================
/*
void BWrite(short arr[51],short diskNum){
FILE *f = fopen(FileName, "r+");
if (f == NULL)
{
cout<<"写文件处错误,请重新输入"<<endl;
return ;
}
//设置文件指针
if(fseek(f,1024*diskNum,0))
cout<<"文件指针错误"<<endl;
fwrite(arr,sizeof(short),51,f);
fclose(f);
}
*/
//================================================================
//函数描述:重构BWrite,实现一个数组的写入
//入口参数:无
//返回值: 无
//===============================================================
void BWrite(short arr[51],short diskNum ){
FILE *f = fopen(FileName, "r+");
if (f == NULL)
{
cout<<"写文件处错误,请重新输入"<<endl;
return ;
}
//设置文件指针
if(fseek(f,1024*diskNum,0))
cout<<"文件指针错误"<<endl;
fwrite(arr,sizeof(short),51,f);
fclose(f);
}
//================================================================
//函数描述:重构BWrite,实现一个数组的写入,数组长度不确定
//入口参数:无
//返回值: 无
//===============================================================
void BWriteArr(short arr[512],short diskNum ){
FILE *f = fopen(FileName, "r+");
if (f == NULL)
{
cout<<"写文件处错误,请重新输入"<<endl;
return ;
}
//设置文件指针
if(fseek(f,1024*diskNum,0))
cout<<"文件指针错误"<<endl;
fwrite(arr,sizeof(short),512,f);
fclose(f);
}
//================================================================
//函数描述:重构BWrite,实现一个数组的写入
//入口参数:无
//返回值: 无
//===============================================================
void MyBWrite(short arr[51],short diskNum ){
FILE *f = fopen(FileName, "r+");
if (f == NULL)
{
cout<<"写文件处错误,请重新输入"<<endl;
return ;
}
//设置文件指针
if(fseek(f,1024*diskNum,0))
cout<<"文件指针错误"<<endl;
for(short i=0;i<51;i++)
fprintf(f,"%d",arr[i]);
fclose(f);
}
//================================================================
//函数描述:从磁盘中读出数组
//入口参数:无
//返回值: 无
//===============================================================
void MyBRead(short arr[51],short diskNum){
FILE *f = fopen(FileName, "r+");
if (f == NULL)
{
cout<<"读文件处错误,请重新输入"<<endl;
return ;
}
//设置文件指针
if(fseek(f,1024*diskNum,0))
cout<<"文件指针错误"<<endl;
for(short i=0;i<51;i++)
fscanf(f,"%d",&arr[i]);
fclose(f);
}
//================================================================
//函数描述:从磁盘中读出iNode节点
//入口参数:无
//返回值: 无
//===============================================================
void BRead(INode *iNode,short diskNum){
FILE *f = fopen(FileName, "r+");
if (f == NULL)
{
cout<<"写文件处错误,请重新输入"<<endl;
return ;
}
//设置文件指针
if(fseek(f,1024*diskNum,0))
cout<<"文件指针错误"<<endl;
fscanf(f,"%d",&iNode->fileLength);
int temp=int(fgetc(f));
switch(temp){
case 0:
iNode->fileSpec=NORMAL;
break;
case 1:
iNode->fileSpec=DIR;
break;
case 2:
iNode->fileSpec=BLOCK;
break;
case 3:
iNode->fileSpec=PIP;
break;
}
fread(iNode->iaddr,2,13,f);
fscanf(f,"%d",&iNode->linkCount);
fclose(f);
}
//================================================================
//函数描述:从磁盘中读出数组
//入口参数:无
//返回值: 无
//===============================================================
void BRead(short arr[51],short diskNum){
FILE *f = fopen(FileName, "r+");
if (f == NULL)
{
cout<<"读文件处错误,请重新输入"<<endl;
return ;
}
//设置文件指针
if(fseek(f,1024*diskNum,0))
cout<<"文件指针错误"<<endl;
fread(arr,sizeof(short),51,f);
fclose(f);
}
//================================================================
//函数描述:从磁盘中读出数组, 放入到iNOde中
//入口参数:无
//返回值: 无
//===============================================================
void BReadArr(short arr[512],short diskNum){
FILE *f = fopen(FileName, "r+");
if (f == NULL)
{
cout<<"读文件处错误,请重新输入"<<endl;
return ;
}
//设置文件指针
if(fseek(f,1024*diskNum,0))
cout<<"文件指针错误"<<endl;
fread(arr,sizeof(short),512,f);
fclose(f);
}
//================================================================
//函数描述:写入一个目录项
//入口参数:无
//返回值: 无
//===============================================================
void BWrite(DirNode *currDir,short diskNum){
FILE *f = fopen(FileName, "r+");
if (f == NULL)
{
cout<<"写文件处错误,请重新输入"<<endl;
return ;
}
//设置文件指针
if(fseek(f,long(1024*diskNum),0))
cout<<"文件指针错误"<<endl;
for(int i=0;i<64;i++)
{
fprintf(f,"%hd",currDir->childItem[i].i_BNum);
fputs(currDir->childItem[i].filename,f);
}
fclose(f);
}
//================================================================
//函数描述:写入一个iNode
//入口参数:无
//返回值: 无
//===============================================================
void BWrite(INode *iNode,short diskNum){
FILE *f = fopen(FileName, "r+");
if (f == NULL)
{
cout<<"写文件处错误,请重新输入"<<endl;
return ;
}
//设置文件指针
if(fseek(f,1024*diskNum,0))
cout<<"文件指针错误"<<endl;
fprintf(f,"%d",iNode->fileLength);
fputc(iNode->fileSpec,f);
fwrite(iNode->iaddr,2,13,f);
fprintf(f,"%d",iNode->linkCount);
fclose(f);
}
//================================================================
//函数描述:分配一个空闲的普通快
//入口参数:无
//返回值: 无
//===============================================================
short AssAnEmpty(){
short temp;
if(SS[0]>1){
SS[0]--;
temp=SS[SS[0]+1];
// SS[SS[0]+1]=-1;
freeTotalB--;//总剩余数-1
return temp;
}else{
if(SS[1]==0){
cout<<"盘片用尽"<<endl;
return -1;
}
temp=freeBlockNum;
freeBlockNum=SS[1];
BRead(SS,SS[1]);
if(temp==0){
SS[0]--;
temp=SS[SS[0]+1];
// SS[SS[0]+1]=-1;
}
freeTotalB--;
return temp;
}
}
//================================================================
//函数描述:分配一个空闲的目录快
//入口参数:无
//返回值: 无
//===============================================================
short AssAnDir(){
if(freeDirCount==0){
cout<<"无空余目录节点"<<endl;
return -1;
}
else{
freeDirCount--;
short s=freeDirNode[freeDirCount];
freeDirNode[freeDirCount]=-1;
return s; //可用索引节点栈
}
}
//================================================================
//函数描述:创建一个文件节点,并分配INOde和磁盘空间
//入口参数:无
//返回值: 无
//===============================================================
void InitCreate(DirNode *currDir,FileSpec fielSpec,char filename[14],INode *iNode,short diskNum){
// int blockNum=AssertAnEmpty();
if(fielSpec==DIR){
//init dirNode and write
int blockNum=AssAnDir();
currDirNum=blockNum;
for(int i=0;i<64;i++){
strcpy(currDir->childItem[i].filename,"0000000000000");
currDir->childItem[i].i_BNum=-1;
}
BWrite(currDir,blockNum);
/*
//init INode and write
blockNum=AssertAnEmpty();
iNode->fileLength=DirLen;
iNode->fileSpec=DIR;
iNode->iaddr[0]=blockNum;
iNode->linkCount=1;
BWrite(iNode,distNum);
//为文件分配磁盘空间
*/
}//end if(fileSpec==DIR)
}
//================================================================
//函数描述:初始化
//入口参数:无
//返回值: 无
//===============================================================
void Init(DirNode *currDir,FileSpec fielSpec,char filename[14],INode *iNode,short diskNum){
InitCreate(currDir,fielSpec,filename,iNode,diskNum);
BRead(SS,0);
}
//================================================================
//函数描述:初始化索引栈
//入口参数:无
//返回值: 无
//===============================================================
void Init(){
for(int i=0;i<30;i++)
freeDirNode[i]=30-i; //可用索引节点栈
freeDirCount=30; //索引节点栈指针
}
//================================================================
//函数描述:成组链接初始化
//入口参数:无
//返回值: 无
//===============================================================
void Linkdisk(){
//临时空闲栈
SS[0]=50;
ArrarEqual(SS,31,80);
BWrite(SS,0);
for(short i=1;i<408;i++){
SS[0]=50;
ArrarEqual(SS,i*50+31,i*50+80);
BWrite(SS,i*50+30);
BRead(SS,0);
}
ArrarEqual(SS,408*50+31,408*50+79);
SS[1]=0;//49
BWrite(SS,408*50+30);
cout<<"磁盘disk.txt完成创建,大小20MB"<<endl;
}
//================================================================
//函数描述:判断一个文件是否存在
//入口参数:无
//返回值: -1,不存在,文件号
//===============================================================
short IsFileExist(DirNode *currDir,char fileName[14]){
for(int i=0;i<64;i++){
if(strcmp(fileName,currDir->childItem[i].filename)==0)
return currDir->childItem[i].i_BNum;
}
return -1;
}
//================================================================
//函数描述:判断一个文件是一个普通文件
//入口参数:无
//返回值: -1,不存在,文件号
//===============================================================
bool IsFile(INode *iNode,short diskNum){
BRead(iNode,diskNum);
if(iNode->fileSpec==NORMAL)
return true;
else
return false;
}
//================================================================
//函数描述:判断一个文件是一个普通文件
//入口参数:无
//返回值: -1,不存在,文件号
//===============================================================
bool IsDir(INode *iNode,short diskNum){
BRead(iNode,diskNum);
if(iNode->fileSpec==DIR)
return true;
else
return false;
}
//================================================================
//函数描述:创建一个iNode,并分配磁盘空间
//入口参数:无
//返回值: 无 AssAnEmpty(),BWrite(dirChild,dir[512-i])未实现
//===============================================================
void CreateINode(INode *iNode,FileSpec fileSpec,short linkCount,short length){
iNode->fileSpec=fileSpec;
iNode->linkCount=linkCount;
iNode->fileLength=length;
//为目录磁盘,分配目录节点
if(fileSpec==DIR){
iNode->iaddr[0]=AssAnDir();
return;
}
//根据文件长度分配文件磁盘节点
//直接寻址
short i;
i=10;
short left=length;
while(left&&i){
iNode->iaddr[10-i]=AssAnEmpty();
left--;
i--;
}
if(left>0){ //一级索引
i=512;
short dir[512];
iNode->iaddr[10]=AssAnEmpty();
while(left&&i){
dir[512-i]=AssAnEmpty();
i--;
left--;
}
if(i!=0)
dir[512-i+1]=-1;//标志文件结束
BWriteArr(dir,iNode->iaddr[10]);
if(left>0){ //二级索引
short k=512;
short j=512;
short dirChild[512];
iNode->iaddr[11]=AssAnEmpty();
while(left&&k){ //二级索引1次寻址
dir[512-k]=AssAnEmpty();
while(left&&j){ //二级索引二次寻址
dirChild[512-j]=AssAnEmpty();
left--;
j--;
}
if(j!=0)
dir[512-j+1]=-1; //标志二级索引二次寻址结束
BWriteArr(dirChild,dir[512-k]);
BWriteArr(dir,iNode->iaddr[11]);//写二级索引一次寻址中盘快记录的一次寻的盘快号
k--;
}
if(k!=0)
dir[512-k+1]=-1;//标志文件结束
}
}
}
//================================================================
//函数描述:清空iNode信息,并分配磁盘空间
//入口参数:无
//返回值: 无 AssAnEmpty(),BWrite(dirChild,dir[512-i])未实现
//===============================================================
void CleanINode(INode *iNode){
iNode->fileSpec=PIP;
iNode->linkCount=-1;
iNode->fileLength=-1;
//根据文件长度非配文件磁盘节点
//直接寻址
for(short i=0;i<13;i++)
iNode->iaddr[i]=-1;
}
//================================================================
//函数描述:创建一个iNode,并分配磁盘空间
//入口参数:无
//返回值: 无
//===============================================================
void InsertDir(DirNode *currDir,char fileName[14],short blockNum){
strcpy(currDir->childItem[currDir->DirCount].filename,fileName);
currDir->childItem[currDir->DirCount].i_BNum=blockNum;
currDir->DirCount++;
return;
}
//================================================================
//函数描述:存在文件,并分配iNOde节点和磁盘空间
//入口参数:无
//返回值: 无
//===============================================================
void Create(DirNode *currDir,char fileName[14],INode *iNode,short length,FileSpec fileSpec){
int blockNum;
if(length>freeTotalB){
cout<<"当前文件超出长度"<<endl;
return;
}
CreateINode(iNode,fileSpec,0,length);
blockNum=AssAnEmpty();//分配一个空余磁盘存储iNOde
InsertDir(currDir,fileName,blockNum);
BWrite(iNode,blockNum);
CleanINode(iNode);
BWrite(currDir,currDirNum);//此处
}
//================================================================
//函数描述:创建一个文件,
//入口参数:无
//返回值: 无
//===============================================================
void Mf(DirNode *currDir,char fileName[14],INode *iNode,short length){
int blockNum=IsFileExist(currDir,fileName);
if(blockNum!=-1){//有重名名,进一步判断
if(IsFile(iNode,blockNum))
cout<<"当前文件已经存在,请重新输入文件名"<<endl;
} else{//存在文件,为索引文件,或者无重名现象,创建文件,并分配iNOde节点和磁盘空间
// BRead(iNode,1059);//此处出错
Create(currDir,fileName,iNode,length,NORMAL);
}
}
//================================================================
//函数描述:在当前目录创建一个子目录
//入口参数:无
//返回值: 无
//===============================================================
void Md(DirNode *currDir,char fileName[14],INode *iNode,short length){
int blockNum=IsFileExist(currDir,fileName);
if(blockNum!=-1){//有重名名,进一步判断
if(IsDir(iNode,blockNum))
cout<<"当前目录已经存在,请重新输入目录名"<<endl;
} else{//存在文件但为普通文件,或者无重名现象。创建文件,并分配iNOde节点和磁盘空间
Create(currDir,fileName,iNode,length,DIR);
CleanINode(iNode);
}
}//================================================================
//函数描述:打开一个文件,
//入口参数:无
//返回值: 无
//===============================================================
void ShowBlockInfo(INode *iNode){
short dir[512];
short i;
i=10;
short left=iNode->fileLength;
while(left&&i){
cout<<(iNode->fileLength-left)<<":"<<iNode->iaddr[10-i]<<" ";
left--;
i--;
}
if(left>0){
i=512;
short dir1[512];
BReadArr(dir1,iNode->iaddr[10]);
while(left&&i){
cout<<(iNode->fileLength-left)<<":"<<dir1[512-i]<<" ";
i--;
left--;
}
}
if(left>0){ //二级索引
short k=512;
short j=512;
short dirChild[512];
BReadArr(dir,iNode->iaddr[11]);
while(left&&k){ //二级索引1次寻址
BReadArr(dirChild,dir[512-k]);
while(left&&j){ //二级索引二次寻址
cout<<(iNode->fileLength-left)<<":"<<dirChild[512-j]<<" ";
left--;
j--;
}
k--;
}
}
}
//================================================================
//函数描述:打开一个文件,
//入口参数:无
//返回值: 无
//===============================================================
void ShowFileInfo(INode *iNode,char fileName[14]){
cout<<"文件名 "<<fileName;
cout<<" 文件类型 ";
switch(iNode->fileSpec){
case NORMAL:
cout<<"< 文件 > ";
break;
case DIR:
cout<<"< 目录 > ";break;
case BLOCK:
cout<<"< INode节点 > ";break;
case PIP:
cout<<"管道 ";break;
}
cout<<" "<<iNode->fileLength<<"KB"<<endl;
}
//================================================================
//函数描述:打开一个文件,
//入口参数:无
//返回值: 无
//===============================================================
void Open(DirNode *currDir,char fileName[14],INode *iNode){
int blockNum=IsFileExist(currDir,fileName);
if(blockNum==-1){//不存在该文件,退出
cout<<"该文件按不存在"<<endl;
return;
}
else{
if(IsFile(iNode,blockNum)){
ShowFileInfo(iNode,fileName);
ShowBlockInfo(iNode);
}
}
}
//================================================================
//函数描述:回收一块空余磁盘片
//入口参数:无
//返回值: 无
//===============================================================
void CallBackOne(short diskNum){
freeTotalB++;
if(SS[0]<=49){
SS[0]++;
SS[SS[0]]=diskNum;
}else{//SS[0]==50
BWrite(SS,freeBlockNum); //将空白的一组回写到上一组记录空闲盘快号的磁盘
freeBlockNum=SS[1]; //将当前空白的一组第一个盘快作为下一个盘组的记录盘
//修改超级栈
SS[1]=diskNum;
SS[0]=1;
}
}
//================================================================
//函数描述:回收文件占用的磁盘
//入口参数:无
//返回值: 无
//===============================================================
void CallBackDisk(INode *iNode){
short i;
i=10;
short left=iNode->fileLength;
while(left&&i){//直接索引回收
CallBackOne(iNode->iaddr[10-i]);
left--;
i--;
}
if(left>0){ //一级索引回收
i=512;
short dir1[512];
BReadArr(dir1,iNode->iaddr[10]);
while(left&&i){
CallBackOne(dir1[512-i]);
i--;
left--;
}
CallBackOne(iNode->iaddr[10]);
}
if(left>0){ //二级索引
short k=512;
short j=512;
short dir[512];
short dirChild[512];
BReadArr(dir,iNode->iaddr[11]);//二级索引1次寻址
while(left&&k){ //二级索引1次寻址
BReadArr(dirChild,dir[512-k]);
while(left&&j){ //二级索引二次回收
CallBackOne(dirChild[512-j]);
left--;
j--;
}
CallBackOne(dir[512-k]);//二级索引一次寻址
k--;
}
CallBackOne(iNode->iaddr[11]);
}
}
//================================================================
//函数描述:回收文件的iNOde节点
//入口参数:无
//返回值: 无
//===============================================================
void CallBackINode(short diskNum){
CallBackOne(diskNum);
}
//================================================================
//函数描述:删除索引中一项
//入口参数:无
//返回值: -1,不存在,文件号
//===============================================================
void DelDirItem(DirNode *currDir,char fileName[14]){
for(int i=0;i<64;i++){
if(strcmp(fileName,currDir->childItem[i].filename)==0){
// currDir->DirCount--;
strcpy(currDir->childItem[i].filename,NullName);
currDir->childItem[i].i_BNum=-1;
return;
}
}
cout<<"删除失败"<<endl;
}
//================================================================
//函数描述:删除一个文件
//入口参数:无
//返回值: 无
//===============================================================
void Del(DirNode *currDir,char fileName[14],INode *iNode){
short blockNum=IsFileExist(currDir,fileName);
if(blockNum==-1){//不存在该文件,退出
cout<<"文件不存在,删除失败"<<endl;
}else{
if(IsFile(iNode,blockNum)){
CallBackDisk(iNode);
DelDirItem(currDir,fileName);
CleanINode(iNode);
}else{
cout<<"文件不存在,删除失败"<<endl;
}
}
}
//================================================================
//函数描述:删除一个目录
//入口参数:无
//返回值: 无
//===============================================================
void Rd(DirNode *currDir,char fileName[14],INode *iNode){
short blockNum=IsFileExist(currDir,fileName);
if(blockNum==-1){//不存在该文件,退出
cout<<"目录不存在,删除失败"<<endl;
}else{
if(IsDir(iNode,blockNum)){
CallBackDisk(iNode);
DelDirItem(currDir,fileName);
CleanINode(iNode);
}else{
cout<<"目录不存在,删除失败"<<endl;
}
}
}
//================================================================
//函数描述:显示目录项的内容
//入口参数:无
//返回值: 无
//===============================================================
void Dir(DirNode *currDir,INode *iNode){
for(int i=0;i<currDir->DirCount;i++){
if(currDir->childItem[i].i_BNum!=-1){
BRead(iNode,currDir->childItem[i].i_BNum);
ShowFileInfo(iNode,currDir->childItem[i].filename);
}
CleanINode(iNode);
}
}
//================================================================
//函数描述:销毁资源
//入口参数:无
//返回值: 无
//===============================================================
void exit(DirNode *currDir,INode *iNode){
delete iNode;
delete currDir;
}
//================================================================
//函数描述:打印版权信息
//入口参数:无
//返回值: 无
//===============================================================
void AuthorMessage(){
cout<<"=============================================================================="<<endl;
cout<<" 操作系统课程设计"<<endl;
cout<<"=============================================================================="<<endl;
printf("单位: 济南大学信息学院计本0601班\n");
printf("作者: 范海青\n");
printf("学号: 20061204013\n");
printf("时间: 2009.4 \n");
printf(" 版权所有 翻版必究\n");
cout<<"=============================================================================="<<endl;
}
//================================================================
//函数描述:打印版权信息
//入口参数:无
//返回值: 无
//===============================================================
void help() //打印命令及解释
{
cout<<"==============================================================================\n"<<endl;
printf(" 命令使用指南\n");
printf(" 1: mf 新建文件\n");
printf(" 2: md 建立子目录\n");
printf(" 3: open 打开文件\n");
printf(" 4: del 删除文件\n");
printf(" 5: rd 删除目录\n");
printf(" 6: dir 显示目录\n");
printf(" 7: exit 退出系统\n");
printf(" 8: cls 清屏\n");
cout<<"==============================================================================\n"<<endl;
cout<<"\n请输入命令,回车确认"<<endl;
}
void main(){
bool run=true;
int length=1024;
char fileName[14];
char dirName[14];
int command;
DirNode *rootNode; //根节点
currDir=new DirNode;
currDir->DirCount=0;
rootNode=currDir;
iNode =new INode;
AuthorMessage();
Format(); //初始化
Linkdisk(); //成组连接
Init();
Init(currDir,DIR,"root\\a:\\",iNode,0);
while(run){
help();
while(true){
scanf("%d",&command);
fflush(stdin);
if(command>0&&command<9)
break;
cout<<"\n命令错误,请重新输入"<<endl;
command=-1;
}
switch(command)
{
case 1:
cout<<"\n请输入文件名"<<endl; //新建问件
cin>>fileName;
fflush(stdin);
cout<<"\n请输入文件长度,单位KB"<<endl;
cin>>length;
fflush(stdin);
if(length<0||length>freeTotalB){
cout<<"文件长度不合法\n"<<endl;
break;
}
Mf(currDir,fileName,iNode,length);
break;
case 2:
cout<<"\n请输入目录名"<<endl;
cin>>dirName;
Md(currDir,dirName,iNode,1);
break;
case 3:
cout<<"\n请输入打开文件名"<<endl; //打开文件
cin>>fileName;
Open(currDir,fileName,iNode);
break;
case 4:
cout<<"\n请输入删除文件名"<<endl; //删除文件
cin>>fileName;
Del(currDir,fileName,iNode);
break;
case 5: //建立子目录
cout<<"\n请输入目录名"<<endl;
cin>>dirName;
Rd(currDir,dirName,iNode);
break;
case 6:
Dir(currDir,iNode); //显示当前目录下的信息
break;
case 7:
exit(currDir,iNode); //退出系统
run=false;
break;
case 8:
system("cls"); //清屏
break;
default:
break;
}
}
}
四、测试数据及程序运行情况
程序启动界面
打印作者信息程序初始化并打印提示信息
五、实验过程中出现的问题及解决方法
试验过程中主要问题
对unix文件系统理解认识不到位
l 成组链接法
l 多级索引机制
l 文件的逻辑结构向物理存储结构的映射
l 空闲盘快的存储和回收
对文件存储不熟悉
l 由于txt文件中是以字符型存储且此次设计到的文件比较大,存储频繁,任何一个不注意都会影响试验几过,主要如下
l 文件读写模式模糊
l 文件读写过程中内存和文件保存信息的类型的相互转化
l 如字符,变长数组的存储和转化结构体
l 详细调试过程在此不再赘述
六、实验体会
这次的操作系统大作业的工作量比较大,我投入了很多的时间和精力才完成了系统的设计。不过这些付出都是值得的,因为我收获了更多。首先,在开发系统的过程中,我对操作系统的功能实现,特别是文件管理这一块的内容有了较深的理解,加深了对Unix文件系统中目录管理和检索快的使用、文件存储空间和成组链接法的使用、混合索引机制。这个理解程度是看书所无法达到的,唯有亲身实践才能深刻理解Unix文件系统。另外,这次开发要使用到文件存取的操作来实现数据块资料的存取,从而使我对文件存取的操作更熟悉了。同时,操作系统大作业的经历在很大程度上磨练了我的意志,同时锻炼了我个人的编程思维。而最重要的,此次操作系统课程设计让我认识到了我自身的不足,激励我加强对计算机理论的学习,让我认识到在计算机科学这个庞大的学科我还只是初学者,有永葆进取心,路漫漫其修远兮,吾将上下而求索。