1. 程式人生 > >作業系統課設實驗五---Nachos檔案系統擴充套件

作業系統課設實驗五---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類中進行,因此我們為其增加一個ExtendSpace方法。最後,在檔案大小變化的時候,應該修改FileHeader類中的numBytes的值,因此我們需要新增一個set方法SetLength。這一切就緒之後,就可以依次實現這些方法了。

ps:原來我在AllocateSpace方法中是直接先釋放所有磁碟空間再申請新空間來實現擴容的,經過小夥伴指正我也發現這樣確實不行,如果內容儲存扇區之前的扇區空間都被別的內容佔用的話的沒有問題,可是如果有空扇區的話,申請之後檔案頭資訊中指向的內容扇區就會和實際儲存的扇區有偏差。
至少之前的設計是有問題的,可以用以下命令實驗下試試

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

之前的結果:
之前
現在的結果
在這裡插入圖片描述

三、實現

首先來說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);
}

然後,我們就只剩下OpenFile類中的AllocateSpace與WriteAt函式和FileHeader類中的ExtendSpace函式需要完成了。

由於OpenFile類裡面的兩個函式的實現都需要使用到ExtendSpace函式的功能,因此這裡首先來實現這個函式,這個函式模仿Allocate函式實現,需要做的就是根據要申請的空間大小和位元圖來申請新的磁碟扇區空間。如下:

bool
FileHeader::ExtendSpace(BitMap *freeMap,int appendSize)
{
    int oriSectors=numSectors; //記錄原空間大小
    numSectors  = divRoundUp(appendSize, SectorSize)+oriSectors;	//計算新扇區大小
//    printf("newSectorsNum is %d\n", numSectors);
    if (freeMap->NumClear() < numSectors-oriSectors)
	{
		numSectors=oriSectors;//將空間大小復原
		return FALSE;		// not enough space
	}

    for (int i = oriSectors; i < numSectors; i++)	//申請新的扇區
	dataSectors[i] = freeMap->Find();
    return TRUE;
}

在WriteAt函式中需要呼叫AllocateSpace函式,所以首先應該來實現AllocateSpace函式:
這個函式的功能應該是給它一個需要擴充套件的檔案長度,它就能申請到這個長度對應的扇區來進行以後的儲存,而具體的申請操作在OpenFile類的FileHeader型別的hdr成員已經實現了,這裡可以直接呼叫它來做:

void
OpenFile::AllocateSpace(int size)
{
    BitMap *freeMap;
    freeMap=new BitMap(NumSectors);	//新建一個BitMap物件
    OpenFile *freeMapFile;
    freeMapFile=new OpenFile(0);	//新建一個位元圖對應的OpenFile物件
    freeMap->FetchFrom(freeMapFile);	//從磁碟中取出位元圖的資訊
    
    hdr->ExtendSpace(freeMap,size);	//實際的擴充套件操作
    freeMap->WriteBack(freeMapFile);	//寫回位元圖的資訊
    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();
    int i, firstSector, lastSector, numSectors;
    bool firstAligned, lastAligned;
    char *buf;
//    printf("seekPosition is %d,fileLength is %d\n",seekPosition,fileLength);
    if (numBytes < 0) 
	return 0;				// check request
    if(seekPosition>=fileLength)	//如果檔案指標已經超過了檔案大小
    {
        numSectors=divRoundUp(fileLength,SectorSize);	//計算當前需要的扇區
        int numPos=seekPosition+numBytes;	//計算新增新位元組之後的指標位置
        if(fileLength==0||numPos>numSectors*SectorSize)	//如果檔案為空或者超過了已有扇區空間
        {
            AllocateSpace(fileLength==0?numBytes:numPos-numSectors*SectorSize);//申請新空間
            fileLength+=numBytes;	//增大檔案空間
        }
        hdr->SetLength(numPos);	//根據新指標位置設定新的檔案大小
    }
    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;
//    printf("firstSector is %d,lastSector is %d\n",firstSector,lastSector);
    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;
    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

這裡有一個原始碼中的坑,就是在測試要用的main.cc中,如果不改的話編譯會報如下錯誤:
報錯
只需要進入lab5下的main. cc,把NAppend函式的宣告修改一下即可:

extern void NAppend(char *from, char *to);

四、測試

測試可能需要若干命令,這裡我們可以寫一個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引數的執行情況,執行結果如下:
ap引數執行結果

可見,與實驗指導書預計的結果一致,即ap功能得以實現。

接下來再來測試一下hap命令。

這裡測試了兩種情況,即從大檔案的中間寫小檔案和從小檔案的中間寫大檔案。結果如下:執行結果如下:
hap命令執行結果

從結果來看,hap的功能也得到了實現。

五、結論分析與體會

從輸出結果來看,拓展之後的檔案系統在保證了基本的cp命令執行不出錯的情況下,增加了ap和hap兩個命令,讓原來固定大小的檔案系統變成了大小可變的檔案系統。

本次實驗在掌握了nachos基本檔案命令用法的情況下,對其功能進行了拓展,增加了檔案可變的功能,而實現這個功能的前提也是足夠了解nachos磁碟系統相關的原始碼,因此通過擴充套件這個功能,我對nachos檔案系統相關的部分有了更深的認識,也對作業系統中所講的檔案系統相關的內容進行了很好的回顧。