说明:blog 中写到的这几个实验,不全面而且也不是上交实验报告的最终版本(是自己实验过程中用typora简单记录的笔记),完整内容(含代码+实验报告)可以通过(山东大学软件学院操作系统课设)下载,或者关注“陌兮blog”免费获取
查看 /filesys/filesys.cc 中文件系统的构造函数,可以发现以下信息
#define FreeMapSector 0
#define DirectorySector 1
#define FreeMapFileSize (NumSectors / BitsInByte)
#define NumDirEntries 10
#define DirectoryFileSize (sizeof(DirectoryEntry) * NumDirEntries)
FileSystem::FileSystem(bool format)
{
if (format) { // 是否应该初始化磁盘
BitMap *freeMap = new BitMap(NumSectors); // 创建文件位图
Directory *directory = new Directory(NumDirEntries); // 创建包含10个文件目录项的文件目录表
FileHeader *mapHdr = new FileHeader; // 创建文件位图的文件头
FileHeader *dirHdr = new FileHeader; // 创建文件目录表的文件头
// 第一步:文件位图中标记0、1号扇区被占用
freeMap->Mark(FreeMapSector); // 0号扇区为文件位图文件头
freeMap->Mark(DirectorySector); // 1号扇区为文件目录表文件头
// 第二步:在文件系统中分配位图文件与文件目录表的空间(传入文件位图与空间大小)
ASSERT(mapHdr->Allocate(freeMap, FreeMapFileSize)); // 位图文件大小为128字节,1个扇区
ASSERT(dirHdr->Allocate(freeMap, DirectoryFileSize)); // 十个文件目录项大小
// 第三步:将更新后的位图文件头、文件目录表文件头写入磁盘
mapHdr->WriteBack(FreeMapSector); // 传入对应文件头所在扇区号
dirHdr->WriteBack(DirectorySector);
// 第四步:创建位图文件、文件目录表的Openfile, Openfile中存储文件头与文件读写位置
freeMapFile = new OpenFile(FreeMapSector); // 传入文件头所在扇区, 用于创建Openfile文件头
directoryFile = new OpenFile(DirectorySector); // 文件读写位置初始为0
// 第五步:将位图文件信息、文件目录项信息传入对应Openfile中
freeMap->WriteBack(freeMapFile); // 确定Openfile的起始扇区与结束扇区,开辟文件缓冲区
directory->WriteBack(directoryFile); // 初始化整个文件
} else {
// 非初始化操作,则根据原有位图文件头、文件目录表文件头信息初始化Openfile
freeMapFile = new OpenFile(FreeMapSector);
directoryFile = new OpenFile(DirectorySector);
}
}
其中主要包含了位图文件、文件目录表的文件头、Openfile的创建,并将初始化信息写入磁盘。其中位图文件用每位的0、1来表示磁盘该位置是否空闲,分配时从前开始找,一旦有空位则直接分配。另外,从上面文件系统创建的部分可以发现,当文件创建后其大小则无法改变。
我们接下来需要自行实现的两个文件命令,-ap、-hap
nachos [-d f] -ap UNIX_filename nachos_filename:表示将一个 UNIX 文件内容添加到 nachos 文件的末尾。
nachos [-d f] -hap UNIX_filename nachos_filename:表示将一个 UNIX 文件内容从 nachos 文件的中间部分开始向后添加并覆盖 nachos 文件的后半部分。
nachos -ap 与 -hap 命令的实现
首先观察一下 main.cc 中是如何实现 nachos -ap 命令的
else if (!strcmp(*argv, "-ap")) { // append from UNIX to Nachos
ASSERT(argc > 2);
Append(*(argv + 1), *(argv + 2), 0);
argCount = 3;
}
可以发现 “-ap” 的命令调用了函数 Append(),而该函数主要调用的是 OpenFile::Write()
OpenFile::Write(char *into, int numBytes)
{
int result = WriteAt(into, numBytes, seekPosition);
seekPosition += result;
return result;
}
OpenFile::Write() 调用的是 OpenFile::WriteAt(),因此我们接下来考虑如何修改 OpenFile::WriteAt() 函数来实现该功能。
对原有 WriteAt 函数的分析可以看到,原有函数并不支持写入的数据超过其文件原有大小,因此我们现在需要修改该函数。
// 检查输入是否合法以及避免输入数据超过文件大小
if ((numBytes <= 0) || (position >= fileLength)) // For original Nachos file system
//if ((numBytes <= 0) || (position > fileLength)) // For lab4 ...
return 0;
// check request
if ((position + numBytes) > fileLength)
numBytes = fileLength - position;
接下来实现这两个命令
大概的思路就是要修改 WriteAt 方法,如果说写入的数据超出了文件的大小,那么进行扩展,再申请新的内存空间。然后调用 OpenFile 的 WriteBack 函数写回磁盘,这个函数通过调用 filehdr 的 WriteBack 方法实现数据的写回操作,我们需要将修改后的文件头写回硬盘时,需要获取该文件头所在扇区号,因此我们在 OpenFile 类中增加一个 sector 变量用于记录该文件头所在扇区号。
首先来实现 ExtendSpace 函数,修改文件大小的方法,完成对文件的扩展,并能够根据文件大小为其分配足够大的扇区。
首先在文件 fileheader.h 中类定义中添加了方法 bool ExtendSpace(int newSize); 然后在文件 fileheader.cc 中实现 FileHeader::ExtendSpace 方法
实现思路:首先根据数据大小判断是否需要扩展数据扇区,这里有两种情况,若不需要,就简单地修改文件大小,若需要,就分配扇区,然后再修改大小。
bool
FileHeader::Extend(int newSize)
{
if(newSize<numBytes)return FALSE; //if not a extend operation
if(newSize==numBytes)return TRUE; //if size not change
//计算所需的空间
int newNumSectors = divRoundUp(newSize, SectorSize); //the number of sectors the new size need to be allocated.
//如果所需空间等于numSectors(原有的空间)
if(newNumSectors == numSectors){
//设置一下大小
numBytes = newSize;
return TRUE;
}
//否则计算一下还需要申请的空间
int diffSector = newNumSectors - numSectors;
OpenFile *bitmapfile = new OpenFile(0);
BitMap *freeMap;//新建一个位图
freeMap = new BitMap(NumSectors);
freeMap->FetchFrom(bitmapfile);//从磁盘中取出位图
printf("debug in fhdr extend where new Sector=%d \n",freeMap->NumClear());
//if disk is full or file size is too big.
if(newNumSectors>NumDirect||freeMap->NumClear()< diffSector)return FALSE;
//申请新的空间,并且存储到文件头
int i;
//i从numSectors到newNumSectors,即循环diffSector次
for(i = numSectors; i<newNumSectors; i++)
{
dataSectors[i] = freeMap->Find();
}
numBytes = newSize;
numSectors = newNumSectors;
return TRUE;
}
接下来实现 WriteBack() 方法:
在文件 openfile.h 类定义中添加方法声明 void WriteBack(); ,然后添加属性 int sector; ,用于记录该文件头所在扇区号
class OpenFile {
public:
//2021.11.21add+++++++++++++++++++++++++++++++++++
void WriteBack();
//2021.11.21add+++++++++++++++++++++++++++++++++++
private:
//2021.11.21add++++++++++++++++++++++++++++++++
int sector;
//2021.11.21add++++++++++++++++++++++++++++++++
在文件 openfile.cc 构造方法中添加 this->ector = sector; 。
OpenFile::OpenFile(int sector)
{
//2021.11.21add++++++++++++++++++++++++++++++++++++++++
this->sector = sector;
//2021.11.21add++++++++++++++++++++++++++++++++++++++++
WriteAt 方法中修改对于 (position + numBytes) > fileLength 的处理
//2021.11.21add++++++++++++++++++++++++++++++++++++++++
// if ((numBytes <= 0) || (position >= fileLength)) // For original Nachos file system
if ((numBytes <= 0) || (position > fileLength)) // For lab4 ...
return 0;
// check request
if ((position + numBytes) > fileLength)
//numBytes = fileLength - position;
hdr->ExtendSpace(position+numBytes);
//2021.11.21add++++++++++++++++++++++++++++++++++++++++
实现方法 OpenFile::WriteBack ,通过调用 filehdr 的 WriteBack 方法实现数据的写回操作
void
OpenFile::WriteBack()
{
hdr->WriteBack(sector);
}
最后一步就是按照实验指导书上所提及的,去除掉fstest.cc 中 Append 方法的 openfile->WriteBack() 前面的注释。
//2021.11.21add++++++++++++++++++++++++++++++++++++++++
// Write the inode back to the disk, because we have changed it
openFile->WriteBack();
printf("inodes have been written back\n");
//2021.11.21add++++++++++++++++++++++++++++++++++++++++
首先需要实现openFile类的WriteBack方法,添加成员变量hdrSector用于记录该文件头所在扇区号,并按照实验指导书上所提及的,去除掉fstest.cc 中 Append 方法的 openfile->WriteBack() 前面的注释。
在openfile.h文件中的openFile类里面添加WriteBack方法
//lab4 add++++++++++++++++++++++++++++
void WriteBack();
添加私有成员变量hdrSector
private:
//lab4 add+++++++++++++++++++++++++++++
int hdrSector;
然后在openfile.cc 中实现WriteBack方法
//lab4 add++++++++++++++++++++++++++++
void
OpenFile::WriteBack()
{
hdr->WriteBack(hdrSector);
}
在openfile.cc文件中的openFile类构造方法中初始化成员变量hdrSector
去除掉fstest.cc 中 Append 方法的 openfile->WriteBack() 前面的注释。
//lab4 add++++++++++++++++++++++++++++
// Write the inode back to the disk, because we have changed it
openFile->WriteBack();
printf("inodes have been written back\n");
接下来实现文件扩展方法ExtendSpace
在filehdr.h文件中的FileHeader类里面添加ExtendSpace方法
//lab4 add++++++++++++++++++++++++++++
bool ExtendSpace(OpenFile *bitmapfile,int newSize);
然后在filehdr.cc文件实现ExtendSpace方法
//lab4 add++++++++++++++++++++++++++++
bool
FileHeader::ExtendSpace(OpenFile *bitmapfile,int newSize)
{
//计算所需扇区
int newNumSectors = divRoundUp(newSize, SectorSize);
//不需扩展,直接更新文件头信息
if(newNumSectors == numSectors){
numBytes = newSize;
return true;
}
//需要扩充的扇区数量
int diffSector = newNumSectors - numSectors;
BitMap *freeMap = new BitMap(NumSectors);
freeMap->FetchFrom(bitmapfile);
//超出一级索引||空间不足
if(newNumSectors>NumDirect||freeMap->NumClear()< diffSector){
return false;
}
//分配新扇区,并更改文件头信息
for(int i = numSectors; i<newNumSectors; i++)
{
dataSectors[i] = freeMap->Find();
}
numBytes = newSize;
numSectors = newNumSectors;
return true;
}
//lab4 add+++++++++++++++++++++++++++++++++++++++++
最后修改openfile.cc文件中的openFile类的WriteAt函数,判断文件是否需要扩展,如果需要扩展则调用ExtendSpace方法进行文件扩展