0.写在前面:
为什么这次把两个实验放在一起写了,因为实验五的要求就是在实验四的基础上完成实现的。但是我得实现说明,我的实验四虽然完成了要求,但是无法在我自己的实验四的基础上完成实验五,这是一个很大的问题,所以在这里将直接将实验四和实验五统一进行讲解和处理。
由于后续时间不足,因此实验四开始,全部的东西将完全使用报告的形式来进行演示。在一些我遇到坑的地方,我会告诉大家怎么样处理,当然仅此而已了。。。。。
对不起啦:D
小三爷,你大胆的往前走,莫回头
其实说真的,我的本职工作是写小说的,或许未来的某天我会试着重新开始写我想写的东西。
1.实验要求(四五)
1.实验四
实验四的要求,按照2021级的要求,大概就是这些了
1. 扩展nachos的文件大小
2. 增加文件的修改时间
其实对于这个实验来说,这个实验就是一个分水岭了,接下来该怎么做,就算是抄也要对源码部分有一定的理解才可以了。
2.实验五
实验五的要求,总结一下大致如下:
0.在实验四的基础上实现
1.实现二级索引
2.增加-DI指令,可以输出磁盘的占用状态
3.尝试并解释如何为nachos加上权限实现(不要求代码实现)
其实可以看出来,核心就在于两个实验各自的第一条,但是这个实验难搞的点就在于,我们需要百分百保证实验四的可行性,而实验手册对于这个的讲解其实非常混乱,因此我们在这里的实验步骤,是一次性完成实验四五的!大家可以按需所需,这个过程中需要什么可以直接选择,如果是全篇,可以拿去当实验五的完整步骤:
ok,那么我们开始?
2.实验步骤
首先,将图中对应文件移动进入lab5文件夹
filehdr类
在h头文件中,加入以下声明:
并且在对应的类中,加入如下属性以及方法
(注意,这里给print加上了一个参数默认,这个参数我们会后续说明怎么修改,print下面的方法属于我们的添加)
在filehdr.cc文件中,实现对于这些功能的声明:
在这里直接写出对于对应方法的修改,自行取用
方法新增:
对于构造和解析方法:
FileHeader::FileHeader(){
memset(dataSectors, 0, sizeof(dataSectors));
bHdrChange=false;
lastModTime=0;
}
FileHeader::~FileHeader(){
if(bHdrChange){
WriteBack(sectorNo);
}
}
对于expendSize方法:
//申请新的扇区的方法
bool
FileHeader::ExtendSpace(int newFileSize){
int i;
if(newFileSize<=numBytes)
return true;
int numSectorsSet=divRoundUp(newFileSize,SectorSize); //计算新的扇区位置
if(numSectorsSet==numSectors){
numBytes = newFileSize;
bHdrChange = true;
return true;
}
BitMap * freeMap= new BitMap(NumSectors);
OpenFile * bitMapFile=new OpenFile(0);
freeMap->FetchFrom(bitMapFile);
if(numSectorsSet>MaxFileSectors||freeMap->NumClear()<numSectorsSet-numSectors){ //超过最大限度或者是不合适
//经过答案提示需要删除点东西==========维持系统的稳定性吧=============
delete bitMapFile;
delete freeMap;
return false;
}
if(numSectorsSet<=NumDirect){ //不需要二级索引的情况
for(int i=numSectors;i<=numSectorsSet;i++){
dataSectors[i] = freeMap->Find();
}
}else{ //需要二级索引的情况
if(numSectors<NumDirect){
for(int i=numSectors;i<=NumDirect;i++){
dataSectors[i] = freeMap->Find();
}
numSectors=NumDirect;
}
int dataSectors2[NumDirect2];
synchDisk->ReadSector(dataSectors[NumDirect],(char *)dataSectors2);
for(i=0;i< numSectorsSet-numSectors;i++){
dataSectors2[i+numSectors-NumDirect]=freeMap->Find();
}
synchDisk->WriteSector(dataSectors[NumDirect],(char *)dataSectors2);
}
freeMap->WriteBack(bitMapFile);
numBytes=newFileSize;
numSectors=numSectorsSet;
bHdrChange=true;
delete bitMapFile;
delete freeMap;
return true;
}
关于修改时间的参数
//关于时间新增的方法如下
//这两个方法为文件头这个对象加上了些许时间属性。
time_t FileHeader::GetModTime(){
return (time_t)lastModTime;
}
void FileHeader::SetModTime(time_t modTime){
lastModTime=(unsigned)modTime;
}
关于代码的重写
首先是空间分配的方法
bool
FileHeader::Allocate(BitMap *freeMap, int fileSize) //这个方法可能只是用于最开始分配空间而已了。。。。
{
printf("\n=========分配空间================\n");
int i;
numBytes = fileSize; //确定文件的大小
numSectors = divRoundUp(fileSize, SectorSize); //根据文件大小和每个盘的尺寸确定要分几个盘
if (freeMap->NumClear() < numSectors)
return FALSE; // not enough space 空间不足
else if(NumDirect2+NumDirect<numSectors)
return FALSE; // 超过了二级索引所能容纳的最大部
//然后接下来分成两种情况,也就是看看是否超过了最后一层的索引,根据这种情况来完成需要的分配 //应该就是这里设置了-1这个东西
if(numSectors<=NumDirect){
for (int i = 0; i < numSectors; i++)
dataSectors[i] = freeMap->Find();
//这个时候,按照要求,因为没有用上二级的索引,因此最后一个指向二级索引的块应该为-1
dataSectors[NumDirect]=-1;
//打印当前信息
printf("numSector:%d . numBytes:%d . lastIndex:%d\n",numSectors,numBytes,NumDirect);
}else{
for (int i = 0; i <=NumDirect; i++) //更改2,这个位置写错了
dataSectors[i] = freeMap->Find();
int dataSectors2[NumDirect2];
for (int i = 0; i < numSectors - NumDirect; i++)
dataSectors2[i] = freeMap->Find();
//将结果写回,就算成功了
synchDisk->WriteSector(dataSectors[NumDirect],(char *)dataSectors2);
}
//=================================================================================
return true;
}
撤销空间分配的方法
void
FileHeader::Deallocate(BitMap *freeMap) //在释放空间这一步需要回收二级列表
{
int lastIndex=NumDirect;
if(dataSectors[lastIndex]==-1){ //
for (int i = 0; i < numSectors; i++) {
ASSERT(freeMap->Test((int) dataSectors[i])); // ought to be marked!
freeMap->Clear((int) dataSectors[i]);
}
}else{
for (int i = 0; i < lastIndex; i++) {
ASSERT(freeMap->Test((int) dataSectors[i])); // ought to be marked!
freeMap->Clear((int) dataSectors[i]);
}
int dataSectors2[NumDirect2];
synchDisk->ReadSector(dataSectors[lastIndex],(char *)dataSectors2);
freeMap->Clear(dataSectors[lastIndex]);
for (int i = lastIndex; i < numSectors; i++) {
ASSERT(freeMap->Test((int) dataSectors2[i-lastIndex])); // ought to be marked!
freeMap->Clear((int) dataSectors2[i-lastIndex]);
}
}
}
根据扇区号码,拉取和写回的方法
void
FileHeader::FetchFrom(int sector)
{
synchDisk->ReadSector(sector, (char *)this);
numSectors=divRoundUp(numBytes,SectorSize);
sectorNo=sector;
bHdrChange=false;
}
//----------------------------------------------------------------------
// FileHeader::WriteBack
// Write the modified contents of the file header back to disk.
//
// "sector" is the disk sector to contain the file header
//----------------------------------------------------------------------
void
FileHeader::WriteBack(int sector)
{
//在这里修改时间即可
synchDisk->WriteSector(sector, (char *)this);
sectorNo=sector;
bHdrChange=false;
}
print方法
void //这个是打印具体文件信息的方法
FileHeader::Print(bool bPrintTime)
{
int i, j, k;
char *data = new char[SectorSize];
//临时测试
//lastTime=(unsigned)std::time(nullptr); //段错误就这段代码引起的
if(bPrintTime){
printf("FileHeader contents==========\n. File size: %d. lastModTime: %u . File blocks:\n",numBytes,lastModTime);
}else{
printf("FileHeader contents==========\n. File size: %d. File blocks:\n", numBytes);
}
//这里加上两种情况,来输出所占的块的数目=========================================================
int lastIndex=NumDirect;
if(dataSectors[lastIndex]==-1){ //仅仅占用了一个索引
for (i = 0; i < numSectors; i++)
printf("%d ", dataSectors[i]);
printf("\n没有使用二级索引\n");
}else{ //占用了所有的索引
printf("一级索引内容\n");
for (i = 0; i < lastIndex; i++)
printf("%d ", dataSectors[i]);
printf("\n二级索引内容\n");
int dataSectors2[NumDirect2];
synchDisk->ReadSector(dataSectors[lastIndex],(char*)dataSectors2);
for (; i < numSectors; i++)
printf("%d ", dataSectors2[i-lastIndex]); //为什么这里会一直认为是-1呢
printf("\n确定使用二级索引,索引地址为:%d\n",dataSectors[lastIndex]);
}
//==========================================================================================
printf("\nFile contents:\n");
for (i = k = 0; i < numSectors; i++) {
synchDisk->ReadSector(dataSectors[i], data);
for (j = 0; (j < SectorSize) && (k < numBytes); j++, k++) {
if ('\040' <= data[j] && data[j] <= '\176') // isprint(data[j])
printf("%c", data[j]);
else
printf("\\%x", (unsigned char)data[j]);
}
printf("\n");
}
delete [] data;
}
然后是对于openfile类的修改:
openfile:
首先是对于头文件中的声明
新增一个写回方法
属性增加这些:
接下来是对实现的一个修改
首先是对于构造方法的修改,如下
OpenFile::OpenFile(int sector) //文件读取器的创建
{
hdr = new FileHeader;
hdr->FetchFrom(sector);
seekPosition = 0;
// 修改时间=======================================================================================================
hdr->SetModTime((unsigned)std::time(nullptr));
hdrSector=sector;
}
读和写的方法,大概是这些,我在这里主要添加了关于时间修改的函数
int
OpenFile::Read(char *into, int numBytes) //读写文件
{
int result = ReadAt(into, numBytes, seekPosition);
seekPosition += result;
// 修改时间=======================================================================================================
hdr->SetModTime((unsigned)std::time(nullptr));
return result;
}
int
OpenFile::Write(char *into, int numBytes)
{
int result = WriteAt(into, numBytes, seekPosition);
seekPosition += result;
// 修改时间=======================================================================================================
hdr->SetModTime((unsigned)std::time(nullptr));
return result;
}
在writeAt方法上作出改进
int
OpenFile::WriteAt(char *from, int numBytes, int position) //最后是在这个方法上进行一些小小的改进
{
int fileLength = hdr->FileLength();
int i, firstSector, lastSector, numSectors;
bool firstAligned, lastAligned;
char *buf;
//if ((numBytes <= 0) || (position >= fileLength)) // For original Nachos file system
// For lab4 ...
if ((numBytes <= 0)|| position > fileLength) return 0;
if ((position+numBytes) >= fileLength)
if(!(hdr->ExtendSpace(position+numBytes)))
numBytes=fileLength-position;
if(fileLength==0) return 0;
DEBUG('f', "Writing %d bytes at %d, from file of length %d.\n",
numBytes, position, fileLength);
firstSector = divRoundDown(position, SectorSize);
lastSector = divRoundDown(position + numBytes - 1, SectorSize);
numSectors = 1 + lastSector - firstSector;
buf = new char[numSectors * SectorSize];
firstAligned = (bool)(position == (firstSector * SectorSize));
lastAligned = (bool)((position + numBytes) == ((lastSector + 1) * SectorSize));
// read in first and last sector, if they are to be partially modified
if (!firstAligned)
ReadAt(buf, SectorSize, firstSector * SectorSize);
if (!lastAligned && ((firstSector != lastSector) || firstAligned))
ReadAt(&buf[(lastSector - firstSector) * SectorSize],
SectorSize, lastSector * SectorSize);
// copy in the bytes we want to change
bcopy(from, &buf[position - (firstSector * SectorSize)], numBytes);
// write modified sectors back
for (i = firstSector; i <= lastSector; i++)
synchDisk->WriteSector(hdr->ByteToSector(i * SectorSize),
&buf[(i - firstSector) * SectorSize]);
delete [] buf;
hdr->SetModTime((unsigned)std::time(nullptr)); // 修改时间==============
return numBytes;
}
最后实现写回方法
void
OpenFile::WriteBack(void){ //给文件头部设置扇区号
hdr->WriteBack(hdrSector);
}
directory:
我好像是直接加入了这个方法
int Directory::GetHdrSecByIndex(int index){
if(table[index].inUse){
return table[index].sector;
}else{
return -1;
}
}
filesys:
在这个类中,我们只需要实现一个打印的方法,来帮助我们实现需要的-DI指令获取文件信息的操作
首先在头文件的声明中,加入
然后直接实现这个方法即可
void FileSystem::PrintInfo(){
BitMap *freeMap=new BitMap(NumSectors);
Directory *directory=new Directory(NumDirEntries);
FileHeader *fileHdr= new FileHeader();
int i, nBytes;
int fileHdrSector;
int nUsedSector, nFiles =0, nBytesInFiles=0,nSectorsOfFiles=0,nFragSectors=0;
printf("\n磁盘尺寸:%d个扇区,字节数目:%d\n",NumSectors,NumSectors*SectorSize);
freeMap->FetchFrom(freeMapFile);
nUsedSector = NumSectors - freeMap->NumClear();
printf("\n(已经使用):%d个扇区,字节数目:%d\n",nUsedSector,nUsedSector*SectorSize);
printf("\n(未使用):%d个扇区,字节数目:%d\n",freeMap->NumClear(),freeMap->NumClear()*SectorSize);
directory->FetchFrom(directoryFile);
for(i = 0; i < NumDirEntries; i++) {
fileHdrSector = directory->GetHdrSecByIndex(i);
if(fileHdrSector != -1) {
nFiles++;
fileHdr->FetchFrom(fileHdrSector);
nBytes = fileHdr->FileLength();
nBytesInFiles += nBytes;
nSectorsOfFiles += divRoundUp(nBytes, SectorSize);
if(nBytes % SectorSize)
nFragSectors++;
}
}
printf("%d bytes in %d files, occupy %d bytes(%d sectors).\n", \
nBytesInFiles, nFiles, nSectorsOfFiles * SectorSize, nSectorsOfFiles);
printf("%d bytes of internal fragmentation in %d sectors.\n", \
nSectorsOfFiles * SectorSize - nBytesInFiles, nFragSectors);
delete freeMap;
delete directory;
delete fileHdr;
}
main:
最后在这个入口文件中,新增这样一个分支
到此为止,所有的代码全部修改完毕:
3.实验汇总
关于代码的执行:
首先使用如下指令,初始化并且复制一个big文件
make
rm -f DISK
//初始化磁盘
./nachos -f
然后使用cp指令创建big文件
./nachos -cp test/big big
使用./nachos -D查询当前文件的状态
可以看到已经创建了big文件
此时使用指令
./nachos -ap test/big big
执行多次以后,可以检查到最后的修改时间被更新,并且已经实现了二级索引
最后使用指令
./nachos -D
查看占用情况:
最后是关于为nachos添加wxz等权限:
-
在Nachos的文件系统中,每个文件都有一个相应的文件描述符(File Descriptor),用于标识和操作该文件。
-
使用文件描述符打开文件,并获取文件的当前权限。在Nachos中,可以使用
OpenFile::Open
函数打开文件,并使用OpenFile::GetFileHeader
函数获取文件的文件头(File Header)信息。 -
在文件头中,找到与权限相关的位(比如读、写、执行位),并根据需要修改这些位。可以使用位操作来设置或清除相应的权限位。
-
将修改后的文件头写回到文件系统中,以保存更改后的权限。可以使用
OpenFile::WriteAt
函数将文件头写回磁盘中的相应位置。 -
关闭文件并释放相关资源。使用
OpenFile::Close
函数关闭文件
4.可能会踩坑的问题
我这里只说一个,我在实验四里面遇到的一个大问题,就是这个3到底含义是什么
答案就是,nachos的文件头需要存储在一个sector的内部,但是一个磁盘内的容量仅仅为128字节,而我们需要存入的是这些东西
这些东西是按照顺序存储的,如果这些东西超过了128字节,那就没救了
因此我们需要适当修改数组的大小,因此在定义NumDirect的时候,是根据我们需要存储的其他变量的大小,修改数组的长度
5.补充,我自己的实验四实现(等待补充)