昆明理工大学信息工程与自动化学院学生实验报告
( 20## —2012 学年第二学期)
课程名称:操作系统 开课实验室:信自楼445 20##年5月15日
一、实验目的
用C或C++语言编写和调试一个简单的文件系统,模拟文件管理的基本功能。从而对各种文件操作命令的实质内容和执行过程有比较深入的了解。
二、实验原理及基本技术路线图(方框原理图)
用C模拟实现文件系统的管理;要求设计一个多级目录结构的文件系统,能正确描述文件控制块,采用合理的外存分配方式,能实现基本的目录及文件的操作,包括创建、删除、重命名、复制、移动等功能,并对文件有一定的存取权限控制。
三、所用仪器、材料(设备名称、型号、规格等)。
计算机一台
四、实验源程序:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream.h>
struct OpenFileTable //打开文件表数据结构
{
long offset; // 当前文件读写指针
char file_name[10]; // 文件名数组
long int file_start; // 文件起始块号
long int file_length; // 文件长度(字节)
};
struct FCB_Block //FCB数据结构
{
int flag; // 标志,-1表示未用,1表示文件用
char file_name[10]; // 文件名数组
long int file_date; // 文件建立日期
long int file_time; // 文件建立时间
long int file_start; // 文件起始块号
long int file_length; // 文件长度(字节)
};
struct Super_Block // 超级块数据结构, 文件系统的分区信息,存放在0#物理块中
{
unsigned long int fs_totalsize; // 整个分区的总磁盘物理块数
unsigned long int fs_freesize; // 分区的所有空闲磁盘物理块数
unsigned int fs_blocksize; // 文件系统的物理块大小(字节)
unsigned int fs_fat_start; // FAT的起始磁盘物理块号
unsigned int fs_fat_size; // FAT占用的磁盘物理块数
unsigned int fs_dir_start; // 根目录的起始磁盘物理块号
unsigned int fs_dir_size; // 根目录占用的磁盘物理块数
unsigned int fs_data_start; // 数据区起始磁盘物理块号
unsigned long int fs_data_size; // 数据区的磁盘物理块数
};
const char DiskName[]="FileSys.dat"; //磁盘文件名
char rw_buffer[512]; // 读写使用的缓冲区
struct FCB_Block filefcb[130]; // 读写目录使用的数据结构
struct Super_Block FsSupBlk; // 读写超级块使用的数据结构
long int fat_buffer[5000]; // 读写FAT使用的缓冲区,为简化在系统启动时全部装入内存,0为空闲
struct OpenFileTable OFT[16]; // 打开文件表,当前只使用OFT[0]
unsigned int block_size; // 物理块大小(字节)
unsigned long int total_disk_size; // 磁盘总容量(物理块数)
unsigned int total_dir_size; // 目录占有的物理块数
unsigned int total_fat_size; // FAT占有的物理块数
long int find_fcb; // 记录读FCB块的次数
FILE *fsPtr; // 模拟磁盘的文件指针
/*********************** 磁盘块的申请***********************************/
unsigned long int Get_Block(unsigned long int count) //分配count个物理快,返回首块指针,其它已经连接
{
unsigned long int tmp,firstblk,tmpcount;
unsigned long int i;
int flag=1;
if (count > FsSupBlk.fs_freesize)
{ printf(" ==== 没有足够磁盘容量,不能分配!==== \n"); return 0; }
tmpcount=0;
for(i=FsSupBlk.fs_data_start;i<=FsSupBlk.fs_totalsize;i++)//建立分配链
{
if(fat_buffer[i] == 0) //文件未占有,分配
{
if (flag==1)
{ firstblk=i; flag=-1;}
else
{ fat_buffer[tmp]=i; }
tmp=i;
fat_buffer[i]=-1;
tmpcount++;
if(tmpcount==count) //分配完成
{ FsSupBlk.fs_freesize=FsSupBlk.fs_freesize-count;//减少可分配物理块
return firstblk;
}
}
}
return -1; //分配不成功
}
/*********************** 磁盘块的回收***********************************/
void Put_Block(unsigned long int addr)
{ unsigned long int i,j;
int count;
i=addr; count=0;
while(fat_buffer[i]!=-1)
{
j=fat_buffer[i]; //下一项
fat_buffer[i] = 0;
count++;
i=j;
}
fat_buffer[i] = 0;
FsSupBlk.fs_freesize=FsSupBlk.fs_freesize+count+1;//增加可分配物理块
return;
}
/*********************** 读磁盘块***********************************/
void Read_Block(unsigned long int addr,char *buf)
{
if (addr>FsSupBlk.fs_totalsize)
{ printf(" ==== 超出磁盘容量,不能读!==== \n"); return; }
fseek(fsPtr,FsSupBlk.fs_blocksize*addr,SEEK_SET);
fread(buf,512,1,fsPtr);
return;
}
/*********************** 写磁盘块***********************************/
void Write_Block(unsigned long int addr,char *buf)
{
if (addr>FsSupBlk.fs_totalsize)
{ printf(" ==== 超出磁盘容量,不能写!==== \n"); return; }
fseek(fsPtr,FsSupBlk.fs_blocksize*addr,SEEK_SET);
fwrite(buf,512,1,fsPtr);
return;
}
/*********************** 格式化磁盘***********************************/
void Real_Format()
{
unsigned long int bcount;
long int fatval,i;
char *c;
//更改系统超级块信息
FsSupBlk.fs_totalsize=total_disk_size;
FsSupBlk.fs_blocksize=block_size;
FsSupBlk.fs_dir_start=1;
FsSupBlk.fs_dir_size=total_dir_size;
FsSupBlk.fs_fat_start=total_dir_size+1;
FsSupBlk.fs_fat_size=total_fat_size;
FsSupBlk.fs_data_start=FsSupBlk.fs_fat_start+FsSupBlk.fs_fat_size;
FsSupBlk.fs_data_size = FsSupBlk.fs_totalsize - FsSupBlk.fs_dir_size - FsSupBlk.fs_fat_size-1;
FsSupBlk.fs_freesize= FsSupBlk.fs_data_size;
//初始化目录
for(i=0;i<128;i++) filefcb[i].flag=-1; //为-1表示FCB未使用
fseek(fsPtr,512L,SEEK_SET);
fwrite(&filefcb[0],sizeof(struct FCB_Block),128,fsPtr);
//初始化FAT
fatval=FsSupBlk.fs_fat_start*512;
fseek(fsPtr,fatval,SEEK_SET); //定位文件指针
bcount=FsSupBlk.fs_fat_size+FsSupBlk.fs_dir_size+1;
for(i=0;i<bcount;i++) fat_buffer[i]=-1; //标记已经使用的磁盘数据块,即FAT区、目录区和启动区
for(;i<FsSupBlk.fs_totalsize;i++) fat_buffer[i]=0; //为0表示为空的物理快
fwrite(&fat_buffer[0],sizeof(long int),FsSupBlk.fs_totalsize,fsPtr);
//初始化数据区
for(i=0;i<512;i++) rw_buffer[i]=' ';//缓冲区清空
for(i=FsSupBlk.fs_data_start;i<FsSupBlk.fs_totalsize;i++)
Write_Block(i,rw_buffer); //缓冲区写入第i块
}
/***********************新建系统磁盘文件***********************************/
void Create_Disk()
{
long int i;
unsigned long int total;
fsPtr=fopen(DiskName,"wb+");
if(fsPtr==NULL)
{
printf(" 不能建立磁盘所需的文件 !\n");
exit(0);
}
// 建立磁盘文件
total=total_disk_size;
for(i=0;i<total;i++) //建立大小为total的磁盘文件
fwrite(rw_buffer,512,1,fsPtr);
fclose(fsPtr);
fsPtr=fopen(DiskName,"rb+");
Real_Format();
return;
}
/***********************读写系统超级块信息***********************************/
void Read_Boot() //读取磁盘超级块数据信息
{
rewind(fsPtr);
fread(&FsSupBlk,sizeof(struct Super_Block),1,fsPtr);
return;
}
void FileBoot() //超级块数据信息存盘
{
rewind(fsPtr);
fwrite(&FsSupBlk,sizeof(struct Super_Block),1,fsPtr);
return;
}
/***********************FAT操作***********************************/
void LoadFat() //装载全部FAT到内存
{
fseek(fsPtr,FsSupBlk.fs_fat_start*512,SEEK_SET);
fread(fat_buffer,sizeof(long int),FsSupBlk.fs_totalsize ,fsPtr);
return;
}
void SaveFat() //FAT到文件FAT区
{
fseek(fsPtr,FsSupBlk.fs_fat_start*512,SEEK_SET);
fwrite(fat_buffer,sizeof(long int),FsSupBlk.fs_totalsize,fsPtr);
return;
}
/***********************显示超级块信息***********************************/
void boot_dis()
{
printf("FsSupBlk.fs_totalsize=%ld\n",FsSupBlk.fs_totalsize);
printf("FsSupBlk.fs_blocksize=%d\n",FsSupBlk.fs_blocksize);
printf("FsSupBlk.fs_dir_start=%d\n",FsSupBlk.fs_dir_start);
printf("FsSupBlk.fs_dir_size=%d\n",FsSupBlk.fs_dir_size);
printf("FsSupBlk.fs_fat_start=%d\n",FsSupBlk.fs_fat_start);
printf("FsSupBlk.fs_fat_size=%d\n",FsSupBlk.fs_fat_size);
printf("FsSupBlk.fs_data_start=%d\n",FsSupBlk.fs_data_start);
printf("FsSupBlk.fs_data_size=%ld\n",FsSupBlk.fs_data_size);
printf("FsSupBlk.fs_freesize=%ld\n",FsSupBlk.fs_freesize);
}
/***********************系统初始化***********************************/
void Sys_Init() //初始化
{
fsPtr=fopen(DiskName,"rb+");
if(fsPtr == NULL) Create_Disk();
Read_Boot();
//boot_dis();
LoadFat();
return;
}
/***********************显示操作***********************************/
void dir() //显示目录下的文件
{
int i,countFile=0;
char str[16];
long int n,pos_dir,pos_fat;
cout<<endl;
pos_dir=FsSupBlk.fs_dir_start*512;
pos_fat=FsSupBlk.fs_fat_start*512;
fseek(fsPtr,pos_dir,SEEK_SET);
while(ftell(fsPtr)<pos_fat)
{
fread(&filefcb[0],sizeof(struct FCB_Block),16 ,fsPtr);
for(i=0;i<16;i++)
if(filefcb[i].flag == 1) //文件占有
{
countFile++;
n = filefcb[i].file_length;
printf(" %-15s<%s>%15d bytes\n", filefcb[i].file_name,"file",n);
}
}
cout<<endl;
printf(" 总共有 %d 个文件\n",countFile);
printf(" 系统总共有 %ld 个物理块可用\n\n",FsSupBlk.fs_freesize);
}
/*************************查找文件*******************************/
//查找文件,文件存在返回当前FCB数组下标,否则返回-1
int Find_File(char *filename)
{
int i;
long int pos_dir,pos_fat;
pos_dir=FsSupBlk.fs_dir_start*512;
pos_fat=FsSupBlk.fs_fat_start*512;
find_fcb=0;
fseek(fsPtr,pos_dir,SEEK_SET);
while(ftell(fsPtr)<pos_fat)
{ find_fcb++;
fread(&filefcb[0],sizeof(struct FCB_Block),16 ,fsPtr);
for(i=0;i<16;i++)
if(filefcb[i].flag!=-1)
{ if(strcmp(filename,filefcb[i].file_name) == 0) return i;} //文件存在
}
return -1;
}
/*************************创建文件*******************************/
void create(char *fname,long int num) //在当前目录下创建一个名字为str的文件,长度为num
{
int i,j; //true表示没有与该名字重名的文件
int tempnode;
long int pos_dir,getnum=0;
unsigned long int blkcount;
blkcount= num/512+1; //计算需要的物理块
if(FsSupBlk.fs_freesize < blkcount) //磁盘没有足够空间
{
printf("\n 磁盘没有足够空间,不能建立 !\n\n");
return;
}
tempnode=Find_File(fname);
if (tempnode!=-1) //表示文件存在
{ printf("\n 文件已经存在,不需要建立 !\n\n"); return;}
// 建立文件的处理
pos_dir=FsSupBlk.fs_dir_start*FsSupBlk.fs_blocksize;
fseek(fsPtr,pos_dir,SEEK_SET); //定位到目录区
for(i=0; i<FsSupBlk.fs_dir_size; i++)
{
//Read_Block(i+FsSupBlk.fs_dir_start,(char *)filefcb);
fread(&filefcb[0],sizeof(struct FCB_Block),16 ,fsPtr);
for(j=0;j<16;j++)
if(filefcb[j].flag == -1) //找到空目录项
{
// 分配空间, 标记FCB数据项,并将FCB写磁盘
getnum=Get_Block(blkcount);
if(getnum==-1){ printf("不能分配存储空间 \n");return;}
filefcb[j].file_start=getnum;
filefcb[j].flag = 1;
filefcb[j].file_length=num;
strcpy(filefcb[j].file_name,fname);
//filefcb[].file_time=
//filefcb[].file_date=
// 改变磁盘FCB值
pos_dir=pos_dir+sizeof(struct FCB_Block)*(i*16+j);
fseek(fsPtr,pos_dir,SEEK_SET); //定位到目录区的FCB项
fwrite(&filefcb[j],sizeof(struct FCB_Block),1 ,fsPtr);
//Write_Block(i+FsSupBlk.fs_dir_start,(char *)filefcb);
printf(" 文件占用了 %d 个物理块\n",blkcount);
printf(" 系统还有 %ld 个物理块可用\n\n",FsSupBlk.fs_freesize);
return;
}
}
//没有FCB项,不能建立文件
cout<<"当前没有足够的目录区,不能建立文件 ! "<<endl;
return;
}
/*************************格式化*******************************/
void format()
{
char ch;
cout<<"\n 真希望格式化磁盘吗?(y/n) ";
cin>>ch;
if(ch=='y'||ch=='Y')
{
Real_Format();
printf("\n Format Successful!\n\n");
}
}
/****************************删除文件操作**************************************/
void del(char *fname)
{
long int tempnode;
long int pos_dir;
tempnode=Find_File(fname);
if (tempnode==-1) //表示文件不存在
{ printf("\n 文件不存在,不能删除 !\n\n"); return;}
// 删除文件的处理
Put_Block(filefcb[tempnode].file_start); // 释放文件存储空间
filefcb[tempnode].flag =-1; // 标记FCB项可用
// 修改的FCB项写回磁盘
pos_dir=FsSupBlk.fs_dir_start*FsSupBlk.fs_blocksize+((find_fcb-1)*16+tempnode)*sizeof(struct FCB_Block);
fseek(fsPtr,pos_dir,SEEK_SET); //定位到目录区
fwrite(&filefcb[tempnode],sizeof(struct FCB_Block),1 ,fsPtr);
return;
}
/*************************写文件*******************************/
void write(char *fname ,int num)//写文件,仅实现在文件尾部填加内容,并限制在512个字节内
{
int tempnode;
int i,j,x;
int rescount;
long int pos_dir;
if (num > 512)
{
printf("\n 不能写大于512字节的数据 !\n\n");
return;
}
tempnode=Find_File(fname);
if (tempnode==-1) //表示文件不存在
{
printf("\n 文件不存在,不能写 !\n\n");
return;
}
// 写文件的处理
char *string=new char [num]; //申请空间
for(i=0;i<num;i++)
{
cin>>string[i];
}
rescount=filefcb[tempnode].file_length % FsSupBlk.fs_blocksize;
if (num> FsSupBlk.fs_blocksize-rescount)
{
if (FsSupBlk.fs_freesize<1)
{
printf("\n 文件系统没有足够空间,不能写 !\n\n");
return;
}
}
for(j=filefcb[tempnode].file_start;fat_buffer[j]!=-1;j=fat_buffer[j]);
Read_Block(j,rw_buffer);
if (num<= FsSupBlk.fs_blocksize-rescount)
{
for(i=0;i<num;i++) rw_buffer[rescount+i]=string[i];
Write_Block(j,rw_buffer);
}
else
{
for(i=0;i<FsSupBlk.fs_blocksize-rescount;i++)
rw_buffer[rescount+i]=string[i];
Write_Block(j,rw_buffer);
fat_buffer[j]=Get_Block(1);
j=fat_buffer[j];
for(x=0;x<FsSupBlk.fs_blocksize;x++) rw_buffer[x]=' ';
for(x=0;x<num-(FsSupBlk.fs_blocksize-rescount);x++)
rw_buffer[x]=string[i+x];
Write_Block(j,rw_buffer);
}
//delete []string;
// 修改FCB项并写回磁盘
filefcb[tempnode].file_length+=num; // 增加文件的长度
pos_dir=FsSupBlk.fs_dir_start*FsSupBlk.fs_blocksize+((find_fcb-1)*16+tempnode)*sizeof(struct FCB_Block);
fseek(fsPtr,pos_dir,SEEK_SET); //定位到目录区
fwrite(&filefcb[tempnode],sizeof(struct FCB_Block),1 ,fsPtr);
cin.ignore(10000,'\n'); //清除输入流缓冲区
cout<<endl<<"=================== 写文件完成!=============="<<endl;
return;
}
void copyFcbtoOft(int fcbpos)
{
OFT[0].offset =0;
strcpy(OFT[0].file_name,filefcb[fcbpos].file_name);
OFT[0].file_start = filefcb[fcbpos].file_start ;
OFT[0].file_length= filefcb[fcbpos].file_length ;
}
/*************************读文件*******************************/
void read(char *fname,long start,int count)//读文件 ,限制在512个字节内
{
int tempnode;
int stblknum,offset;
int dspnum;
long i,j,x;
tempnode=Find_File(fname);
if (tempnode==-1) //表示文件不存在
{ printf("\n 文件不存在,不能读 !\n\n");
return; }
if (start > filefcb[tempnode].file_length) //读的数据超出文件范围
{ printf("\n 超出文件范围,不能读 !\n\n");
return; }
// 读文件的处理
printf("\n========================================\n");
stblknum=start/FsSupBlk.fs_blocksize+1; // 起始物理块
offset=start%FsSupBlk.fs_blocksize; // 起始物理块的偏移量
if(start+count>filefcb[tempnode].file_length )
count= filefcb[tempnode].file_length-start;
for(i=filefcb[tempnode].file_start,j=1;j<stblknum;j++)
i=fat_buffer[i];
Read_Block(i,rw_buffer);
if(start+count>filefcb[tempnode].file_length )
count= filefcb[tempnode].file_length-start;
if(count<=FsSupBlk.fs_blocksize-offset)
{
for(j=0;j<count;j++) cout<<rw_buffer[j+offset];
}
else
{
for(j=0;j<FsSupBlk.fs_blocksize-offset;j++) cout<<rw_buffer[j+offset];
dspnum=(count-(FsSupBlk.fs_blocksize-offset))/FsSupBlk.fs_blocksize+1;
for(j=0;j<dspnum-1;j++)
{
i=fat_buffer[i];
Read_Block(i,rw_buffer);
for(x=0;x<FsSupBlk.fs_blocksize;x++)
cout<<rw_buffer[x];
}
i=fat_buffer[i];
Read_Block(i,rw_buffer);
x=(count-(FsSupBlk.fs_blocksize-offset))%FsSupBlk.fs_blocksize;
for(j=0;j<x;j++) cout<<rw_buffer[x];
}
cout<<endl<<"============= 读文件完成!================"<<endl;
return;
}
/*************************显示帮助*******************************/
void display_help()
{
printf(" 写文件 wf filename size \n");
printf(" 读文件 rf filename start size \n");
printf(" 删除文件 df filename \n");
printf(" 建立文件 cf filename size \n");
printf(" 显示文件 dir \n");
printf(" 磁盘格式化 format \n");
printf(" 退出文件系统 quit \n\n");
}
/*************************命令解释*******************************/
void CmdShell() //全部命令的解释执行过程
{
int j;
int parameter; // 记录命令行参数个数
int len; // 输入的命令行长度
char string[50]; // 输入的命令行字符串
char *str[4]; // 分解出的命令行参数
bool flag; // 标志
cout<<"\n 输入help查看所有命令"<<endl<<endl;
while(1)
{
printf("c:\>"); //输出系统提示符
gets(string);
len = strlen(string);
//命令行信息的解析过程
for(parameter = 0, flag = true, j = 0; j<len ; j++)
{
if(string[j] == ' ') //读到输入字符为空格时,输出回车
{ flag = true; string[j] = '\0'; }
else
if(flag)
{ flag = false;
str[parameter++] = &string[j]; //分解出的命令,以空格为各参数的分割符
} //第一个为操作命令,其他为参数
}
strlwr(str[0]);
// 根据操作命令执行不同的过程
if(!strcmp("cf",str[0])) // 创建文件
{
if(parameter != 3)
printf(" 格式不正确,请重新输入 ! \n ");
else
create(str[1],atol(str[2])); } // 创建一个名字为str[0]大小为str[2]的文件
else if(!strcmp("df",str[0])) // 删除文件
{ if(parameter != 2)
printf(" 格式不正确,请重新输入 ! \n ");
else
//disfat();}
del(str[1]); } //删除文件str[1]
else if(!strcmp("dir",str[0])) //显示文件命令
dir();
else if(!strcmp("help",str[0]))
display_help();
else if(!strcmp("format",str[0]))
format();
else if(!strcmp("wf",str[0])) //写文件
{ if(parameter != 3)
printf(" 格式不正确,请重新输入 ! \n ");
else
write(str[1],atol(str[2])); }
else if(!strcmp("rf",str[0])) //读文件内容
{ if(parameter != 4)
printf(" 格式不正确,请重新输入 !\n");
else
read(str[1],atol(str[2]),atol(str[3])); }
else if(!strcmp("quit",str[0])) //退出
{ SaveFat(); fclose(fsPtr); exit(0);}
else
if (parameter >0 ) cout<<"命令错误!"<<endl;
}
}
/************************* 主过程 *******************************/
void main()
{
block_size=512; // 物理块大小512字节
total_disk_size=4096; // 磁盘总容量为4096块
total_dir_size=16; // 目录占有的物理块数为16块
total_fat_size=32; // FAT占有的物理块数为32块
Sys_Init(); //初始化磁盘系统
CmdShell(); //命令解释过程
}
五、实验结果截图:
六、实验结果、分析和结论(误差分析与数据处理、成果总结等。其中,绘制曲线图时必须用计算纸)
本实验是用C++程序语言编写的,由于对C++的学习不够深,导致在调试与编译时出现了很大的困难。在设计此系统时,发觉自己对文件系统理解得不够透彻。不知道如何来具体设计这个实验,后来耐心回到本学期学习的课本内容上,才觉悟对文件系统的理解,总而言之,操作系统的设计,不仅提升了自己的程序设计及编写技巧,更大大地加深了对操作系统的文件管理的了解。