操作系统课设实验五---nachos文件系统

这次的实验让我想起了上学期被操作系统的九个实验支配的恐惧,因为这次可能也是前五次试验中最难的一次了,读源码加实现花了可能一两天的时间,所以也有必要记录一下,有些地方做的不是很好,比如Makefile自己写的话可能不需要把所有文件都从filesys复制到lab5,可是我太菜做不到,所以先这样凑合着吧emmm,毕竟还有一堆其他实验。

一、实验要求重述

目标

在lab5目录下,通过对nachos源代码的修改,实现可扩展的文件系统,即满足以下需求

  • 当一个文件创建的时候,它的初始大小为0
  • 文件的大小可以在文件内容增加时改变

如:100 bytes大小的文件在50 bytes的位置往后再写100个byte,文件大小变为150 bytes.

设计与实现的提示

  1. 在lab5目录下进行工作,其下的test文件夹中存放着测试文件。
  2. 需要创建arch子目录以及自己的Makefile文件到lab5。
  3. 考虑filesys中的哪些文件需要拷贝到lab5下并进行修改。
  4. 不需要修改lab5目录下的main. cc,但是需要将fstest.cc中的Append与NAppend函数中几行注释取消掉:
// Write the inode back to the disk, because we have changed it
// openFile->WriteBack();
// printf("inodes have been written back\n");

取消最后两行的注释即可。此外,在取消注释之后还需要在OpenFile的类中实现Writeback方法。

二、分析

首先我们需要分析nachos文件部分的源代码,一方面是要了解那七个文件的用法,另一方面要阅读测试使用的main. cc和fstest. cc,通过阅读源码,我们才能达到尽可能少的修改源码来实现功能的目的。

通过对这些源码的阅读以及实验指导书的提示,首先我们需要修改OpenFile这个类,要实现它的WriteBack方法,这个方法的作用是将大小改变的文件头信息写回磁盘。其次在写的方法上我们也要进行修改,那就是该类的WriteAt方法,因为这里的逻辑是假设文件大小固定,因此我们需要为它添加一个申请磁盘空间的方法,这里我在OpenFile类新建了一个AllocateSpace方法。而在申请新的磁盘空间的时候,我们可以知道文件大小必然会发生变化,而这个值是保存在FileHeader这个类的私有成员numBytes中,我们在申请了新的磁盘空间之后必然要改这个值,所以我们需要为这个私有成员提供一个set方法,这里我在FileHeader中新建了一个SetLength方法用于设置文件的大小。这一切就绪之后,就可以依次实现这些方法了。

三、实现

首先来说FileHeader类中的SetLength方法,我们只需要简单的设置即可,因此它的代码也非常简单,如下:

void 
FileHeader::SetLength(int length)
{
    this->numBytes=length;
}

下一步来实现相对简单一点的OpenFile类中的WriteBack方法,这里我们要把其内部的FileHeader类型的成员hdr写回到磁盘中,FileHeader内部实现了写回磁盘的方法,但是它需要扇区参数,而在这个OpenFile类中这个值并不好取得,因此我们再给OpenFile类增加一个私有成员sector,并在其构造函数中利用传入的头信息扇区号来初始化。如下:

OpenFile::OpenFile(int sector)
{ 
    hdr = new FileHeader;
    hdr->FetchFrom(sector);
    seekPosition = 0;
    this->sector=sector;
}

这样,我们就能在WriteBack方法中拿到扇区号了。如下:

void 
OpenFile::WriteBack()
{
    hdr->WriteBack(sector);
}

然后,我们就只剩下AllocateSpace与WriteAt函数需要完成了,而在WriteAt函数中需要调用AllocateSpace函数,所以首先应该来实现AllocateSpace函数:

这个函数的功能应该是给它一个文件长度,它就能申请到这个长度对应的扇区来进行以后的存储,而具体的申请和释放操作OpenFile类的FileHeader类型的hdr成员已经实现了,这里可以直接调用它来做。但是有个问题在于这个方法需要一个BitMap类型的指针,而我们可以知道相关的指针存在于FileSystem类中,且作为一个私有成员,不好获取到,而且底层调用上层的API更是不太合理,因此我们可以直接在这个类中对照FileSystem类的从磁盘中读取BitMap的代码来实现功能。由于FileHeader类的释放与申请都是对整个文件的所有扇区来做的,为了尽可能少的修改代码,这里我先释放所有空间,然后再申请新的大小的空间,同时改变文件头信息的文件尺寸的大小。代码如下:

void 
OpenFile::AllocateSpace(int size)
{
    hdr->SetLength(size);	//设置文件头信息的文件长度
//    printf("allocate new space %d\n",size);
    BitMap *freeMap ;	//声明一个BitMap指针
    freeMap = new BitMap(NumSectors);	//为BitMap指针分配空间
    OpenFile* freeMapFile;	//新建一个OpenFile指针,用于打开磁盘中实际的BitMap内容存在的位置
    freeMapFile = new OpenFile(0);	//已知BitMap存在于磁盘的0扇区,用此新建一个OpenFile对象
    freeMap->FetchFrom(freeMapFile);	//进行实际的读取操作,将磁盘中的BitMap读取出
    hdr->Deallocate(freeMap);	//调用释放内存的方法
    hdr->Allocate(freeMap,size);	//调用申请内存的方法
//   printf("write back bitmap\n");
//    hdr->Print();
    freeMap->WriteBack(freeMapFile);	//写会BitMap文件
    delete freeMap;	//释放空间
}

完成这些之后,可以来实现WriteAt方法了,这个方法在实现的时候已经考虑到了不从头开始写的情况,因此我们也不需要太担心ap和hap命令的实现,在实现这个函数之前,我们应该先进行一些思考:

如果是cp模式,即直接复制UNIX文件的内容到nachos文件系统中去,则当前的代码已经不需要修改了。如果我们要实现ap或者hap模式,则需要申请新的空间,而剩余的部分也不需要进行修改,因此,我们首先需要判断一下是不是追加模式,如果是,则去申请新的空间。而判断是否为追加模式,我们可以用当前文件指针的位置是否大于文件大小来确定。如果是追加模式,我们则分两种情况讨论:

1.加上新的字符后的文件大小还没有占满整个扇区,这时只需要设置文件大小即可。

2.加上新的字符后文件大小已经超过了整个扇区,这时候就需要申请新的磁盘空间来对文件进行存储了。

此外再排除一些WriteAt本来的关于文件长度的一些限制之后,修改的WriteAt方法如下:

int
OpenFile::WriteAt(char *from, int numBytes, int position)
{
    int fileLength = hdr->FileLength();
    printf("fileSize is %d\n",hdr->FileLength());
    int i, firstSector, lastSector, numSectors;
    bool firstAligned, lastAligned;
    char *buf;

    if (numBytes <= 0)
	return 0;				// check request	//排除一些长度限制
    //if ((position + numBytes) > fileLength)
	//numBytes = fileLength - position;
    printf("seekPosition is %d,numBytes is %d\n",seekPosition,numBytes);
    if(seekPosition>=fileLength)	//如果此时文件指针已经超过了文件末尾
    {
        numSectors=fileLength/SectorSize+1;	//获得所占扇区数目
		int newPos=seekPosition+numBytes;	//算出新的位置
		   if(fileLength==0||seekPosition+numBytes>numSectors*SectorSize)	//如果文件本来为空或者扇区放不下新加的字符,即第二种情况
		{
			AllocateSpace(seekPosition+numBytes);	//申请新的内存
            fileLength+=numBytes;//增加文件长度
		}else{	//否则为第一种情况
			hdr->SetLength(seekPosition+numBytes);	//直接修改文件大小
		}
    }
    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++)
    {
        printf("will write sector %d\n",hdr->ByteToSector(i * SectorSize));	
        synchDisk->WriteSector(hdr->ByteToSector(i * SectorSize), 
					&buf[(i - firstSector) * SectorSize]);
    }
    delete [] buf;
    return numBytes;
}

最后,我们则是按照实验指导书上的提示,在fstest.cc文件中取消掉调用WriteBack的部分的注释。


// Write the inode back to the disk, because we have changed it
  openFile->WriteBack();
  printf("inodes have been written back\n");

源码部分处理完成以后就要进行编译与测试了,这里实验要求自己编写Makefile文件,由于nachos的编译模块过于复杂,这里我先把filesys中的所有除fstest.cc之外的文件复制到lab5目录下,然后修改Makefile文件来实现的编译,两个相关的Makefile代码如下:

Makefile.local:

ifndef MAKEFILE_LAB5_LOCAL
define MAKEFILE_LAB5_LOCAL
yes
endef

# Add new sourcefiles here.

CCFILES +=bitmap.cc\
        directory.cc\
	filehdr.cc\
	filesys.cc\
	fstest.cc\
	openfile.cc\
	synchdisk.cc\
	disk.cc\
	main.cc

ifdef MAKEFILE_USERPROG_LOCAL
DEFINES := $(DEFINES:FILESYS_STUB=FILESYS)
else
INCPATH += -I../userprog -I../lab5
DEFINES += -DFILESYS_NEEDED -DFILESYS
endif

endif # MAKEFILE_FILESYS_LOCAL

Makefile:

ifndef MAKEFILE_LAB5
define MAKEFILE_LAB5
yes
endef

# You can re-order the assignments.  If filesys comes before userprog, 
# just re-order and comment the includes below as appropriate.

include ../threads/Makefile.local
include ../lab5/Makefile.local
#include ../userprog/Makefile.local
#include ../vm/Makefile.local
#include ../filesys/Makefile.local

include ../Makefile.dep
include ../Makefile.common

endif # MAKEFILE_FILESYS

四、测试

测试可能需要若干命令,这里我们可以写一个Shell脚本来保存连续的命令,需要的时候调用然后观察最后结果即可,这里我准备了两个测试脚本,修改完源码并且编译之后执行即可:

测试ap参数:

rm DISK
./nachos -f
./nachos -cp test/small small
./nachos -ap test/small small
./nachos -cp test/empty empty
./nachos -ap test/medium empty
./nachos -D

测试hap参数:

rm DISK
./nachos -f
./nachos -cp test/small small
./nachos -cp test/big big
./nachos -hap test/big small
./nachos -hap test/small big
./nachos -D

将上述两个脚本分别另存为ap. sh和hap. sh,然后在命令行中执行如下命令给予其执行权限:

$chmod a+x ap.sh
$chmod a+x hap.sh

然后首先我们测试ap参数的运行情况,运行结果如下:
操作系统课设实验五---nachos文件系统

可见,与实验指导书预计的结果一致,即ap功能得以实现。

接下来再来测试一下hap命令。

这里测试了两种情况,即从大文件的中间写小文件和从小文件的中间写大文件。结果如下:运行结果如下:
操作系统课设实验五---nachos文件系统

从结果来看,hap的功能也得到了实现。

五、结论分析与体会

从输出结果来看,拓展之后的文件系统在保证了基本的cp命令执行不出错的情况下,增加了ap和hap两个命令,让原来固定大小的文件系统变成了大小可变的文件系统。

本次实验在掌握了nachos基本文件命令用法的情况下,对其功能进行了拓展,增加了文件可变的功能,而实现这个功能的前提也是足够了解nachos磁盘系统相关的源代码,因此通过扩展这个功能,我对nachos文件系统相关的部分有了更深的认识,也对操作系统中所讲的文件系统相关的内容进行了很好的回顾。