文件系統(tǒng)實(shí)習(xí)報(bào)告_第1頁(yè)
文件系統(tǒng)實(shí)習(xí)報(bào)告_第2頁(yè)
文件系統(tǒng)實(shí)習(xí)報(bào)告_第3頁(yè)
文件系統(tǒng)實(shí)習(xí)報(bào)告_第4頁(yè)
文件系統(tǒng)實(shí)習(xí)報(bào)告_第5頁(yè)
已閱讀5頁(yè),還剩6頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

付費(fèi)下載

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

文件系統(tǒng)實(shí)習(xí)報(bào)告姓名李煒學(xué)號(hào)1100012810日期5月6日

目錄內(nèi)容一:總體概述 3內(nèi)容二:任務(wù)完成情況 3任務(wù)完成列表(Y/N) 3具體Exercise的完成情況 3內(nèi)容三:遇到的困難以及解決方法 4內(nèi)容四:收獲及感想 5內(nèi)容五:對(duì)課程的意見(jiàn)和建議 5內(nèi)容六:參考文獻(xiàn) 5內(nèi)容一:總體概述這次實(shí)習(xí)都過(guò)擴(kuò)展文件系統(tǒng)以及添加相應(yīng)的系統(tǒng)調(diào)用,幫助我們理解文件在磁盤中實(shí)際的存儲(chǔ)方式,目錄、文件頭和文件之間的關(guān)系,以及控制臺(tái)和文件讀寫(xiě)之間的關(guān)系。概念主要涉及到了inode的實(shí)現(xiàn)方式,多級(jí)索引,多級(jí)目錄等。【用簡(jiǎn)潔的語(yǔ)言描述本次lab的主要內(nèi)容;闡述本次lab中涉及到的重要的概念,技術(shù),原理等,以及其他你認(rèn)為的最重要的知識(shí)點(diǎn)。這一部分主要是看大家對(duì)lab的總體的理解。 要求:簡(jiǎn)潔,不需要面面俱到,把重要的知識(shí)點(diǎn)闡述清楚即可?!?jī)?nèi)容二:任務(wù)完成情況任務(wù)完成列表(Y/N)Exercise1Exercise2Exercise3Exercise4Exercise5Exercise6第一部分YYYYY第二部分YYY第三部分YYchallengeYNNY具體Exercise的完成情況第一部分:文件系統(tǒng)的基本操作Exercise1:Filesys.hfilesys.cc從filesys.h的注釋中,我們可以看到,nachos使用了兩套文件系統(tǒng),一個(gè)是基于unix(linux)本身的文件系統(tǒng)的類似于包裝函數(shù),另一個(gè)是需要我們實(shí)現(xiàn)的真正的文件系統(tǒng),現(xiàn)在的nachos文件系統(tǒng)有很多限制,比如說(shuō),這個(gè)文件系統(tǒng)不支持多層的文件目錄,而是只有root目錄,而且由于系統(tǒng)啟動(dòng)的限制,標(biāo)記磁盤使用情況的bitmap和目錄分別在sector0,sector1。這樣就限制了可以使用的磁盤的大小和可以創(chuàng)建的文件的數(shù)量,就像其它大多數(shù)文件系統(tǒng)一樣,nachos需要支持初始化文件系統(tǒng),創(chuàng)建文件,打開(kāi)文件,刪除文件,打印目錄中的文件,打印文件信息。而這個(gè)文件系統(tǒng)中關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)就是前面所說(shuō)的空閑區(qū)表,和根目錄。下面具體來(lái)看需要實(shí)現(xiàn)的這些函數(shù)的初步實(shí)現(xiàn)。創(chuàng)建文件系統(tǒng)的時(shí)候首先查看是不是已經(jīng)被“格式化”了,也就是是不是已經(jīng)存在了文件系統(tǒng),如果不存在的話,那么先要?jiǎng)?chuàng)建并初始化前面經(jīng)常提到的bitmap和目錄,而因?yàn)檫@兩個(gè)數(shù)據(jù)結(jié)構(gòu)本身也需要存儲(chǔ)在文件中,所以首先標(biāo)記宏定義的0號(hào)和1號(hào)sector是已經(jīng)被占用了的,但是在nachos中實(shí)際上bitmap和根目錄也是文件,同樣需要文件頭來(lái)管理,于是0號(hào)和1號(hào)實(shí)際上存儲(chǔ)的分別是bitmap和根目錄的文件頭,而真正的bitmap和根目錄需要文件頭接著使用allocate函數(shù)來(lái)分配,并且writeback,向分配出的兩個(gè)塊寫(xiě)回到文件中,完成之后,打開(kāi)這兩個(gè)文件并將初始化后的bitmap和目錄寫(xiě)入到這兩個(gè)文件。如果文件系統(tǒng)已經(jīng)存在的話,那么直接new出兩個(gè)結(jié)構(gòu)即可,之后的操作可以直接使用文件中的信息了。對(duì)于創(chuàng)建文件來(lái)說(shuō),步驟如下,首先確保同目錄下不存在同名文件,之后為文件頭分配磁盤塊(只有一個(gè)塊),為文件實(shí)際的數(shù)據(jù)分配磁盤空間,向目錄中加入這個(gè)文件名,將文件頭寫(xiě)入到磁盤中,刷新bitmap和目錄信息到磁盤中的這兩個(gè)文件中。對(duì)于打開(kāi)文件來(lái)說(shuō),只需要打開(kāi)目錄文件,找到具有給出的名字的文件,然后打開(kāi)這個(gè)文件即可。對(duì)于刪除文件來(lái)說(shuō),需要從目錄中去掉這一項(xiàng),刪掉文件頭在磁盤上占用的塊,刪掉文件數(shù)據(jù)占據(jù)的塊,將bitmap和目錄的變化寫(xiě)回磁盤。Filehdr.hfilehdr.cc這兩個(gè)文件和前面提到的文件頭有關(guān),也就是原理課上講到過(guò)的i-node,從頭文件中可以看出,文件的頭存儲(chǔ)的全部是直接索引(占用的文件塊號(hào)),這就限定了文件的大小目前不能超過(guò)4K,而且還有一點(diǎn)很重要的是,每個(gè)文件頭都只能占用一個(gè)塊,這就限制了文件頭中能夠存儲(chǔ)的文件的信息。除此之外,文件頭鎖存儲(chǔ)的信息十分有限,只有文件的大小(bytes)和占用的sector多少。對(duì)于其中的功能來(lái)說(shuō),allocate通過(guò)向bitmap不斷申請(qǐng)磁盤塊(直到足夠?yàn)橹梗?,?lái)獲得文件所需要的空間,而deallocate則正好相反,通過(guò)將datasectors中的所有磁盤塊還原為未使用來(lái)釋放空間。其它函數(shù)都很簡(jiǎn)單,基本上是對(duì)synchdisk的包裝函數(shù),這里就不介紹了。Directory.hdirectory.cc對(duì)于目錄來(lái)說(shuō),其實(shí)就是許多文件名和文件頭所在塊的組合,這個(gè)組合被定義為一個(gè)叫做DirectoryEntry的類,這個(gè)類中除了包含這兩個(gè)信息之外,還包括目錄項(xiàng)是否被使用。而對(duì)于這個(gè)目錄來(lái)說(shuō),最主要的數(shù)據(jù)結(jié)構(gòu)就是table(DirectorEntry數(shù)組)。下面來(lái)看具體的函數(shù),對(duì)于構(gòu)造函數(shù)來(lái)說(shuō),就是創(chuàng)建出上面所說(shuō)的table,并且初始化。FetchFrom的作用是從已經(jīng)有了目錄結(jié)構(gòu)的文件中,把這個(gè)結(jié)構(gòu)整體讀到table中。writeBack則相反,是將table的內(nèi)容寫(xiě)入到一個(gè)文件中。FindIndex是通過(guò)文件名來(lái)查找這個(gè)文件在table中的索引號(hào)。Find則是通過(guò)先找到索引號(hào),之后返回文件所對(duì)應(yīng)的文件頭的塊號(hào)。Add是通過(guò)檢查目錄中是否有重名文件或者目錄是否仍有空間,如果是的話就把這個(gè)文件名-文件頭塊號(hào)對(duì)寫(xiě)入到table當(dāng)中。Remove十分簡(jiǎn)單,把table中對(duì)應(yīng)的這一項(xiàng)的inUse設(shè)置成False(原來(lái)inUse是標(biāo)記是不是有效的,現(xiàn)在才看出來(lái))。OpenFile.hopenFile.cc和filesys一樣,openfile也有兩種實(shí)現(xiàn),一種是unix的包裝,令一種是需要我們自己去改進(jìn)的簡(jiǎn)單的打開(kāi)文件的方式??梢钥闯?,對(duì)于一個(gè)打開(kāi)文件操作來(lái)說(shuō),最重要的是文件頭和當(dāng)前讀到的位置,對(duì)應(yīng)的就是hdr和seekPosition,下面來(lái)看具體的函數(shù)。構(gòu)造函數(shù)的作用是從一個(gè)sector中讀入文件頭,并且把讀取位置設(shè)置成0。Read和write差不多,都是從當(dāng)前位置開(kāi)始讀制定長(zhǎng)度的內(nèi)容到into指向的位置,使讀取位置這個(gè)值增加到讀到的位置,并且返回真正讀取到的字節(jié)數(shù)。Readat函數(shù)將文件從指定的位置向后讀指定長(zhǎng)度(如果夠長(zhǎng)的話), 但是不同的是讀取文件位置的指不變,先是讀文件到一個(gè)buf緩沖數(shù)組中,之后從buf中再?gòu)?fù)制到into指定的位置。Exercise2:擴(kuò)展文件信息,我選擇在文件頭中添加文件信息(也可以在目錄中添加),對(duì)于文件類型來(lái)說(shuō),我在filehdr中新定義了一個(gè)類叫Filedtype,為了節(jié)省空間,我只用了一個(gè)char來(lái)存儲(chǔ)文件類型,按照從低位到高位的順序,使用位操作來(lái)設(shè)置和獲取信息,我定義第一位代表可讀,第二位代表可寫(xiě),第三位代表可執(zhí)行,通過(guò)與&操作來(lái)獲取信息,通過(guò)或|操作來(lái)設(shè)置信息。對(duì)于創(chuàng)建時(shí)間來(lái)說(shuō),我認(rèn)為給文件頭allocate的時(shí)候算創(chuàng)建,于是我在allocate開(kāi)始的地方加上createTime=stats->totalTicks,而對(duì)于lastModifyTime來(lái)說(shuō),我選擇在writeback的里面寫(xiě)入類似的賦值語(yǔ)句,而lastReferTime則是在FetchFrom里面,后來(lái)在調(diào)試的時(shí)候發(fā)現(xiàn)如果創(chuàng)建之后就沒(méi)有再讀過(guò),那么lastReferTime就會(huì)是0,這樣不符合實(shí)際,所以我在allocate里面給三個(gè)時(shí)間都賦了值。但是定義了這些屬性之后還需要調(diào)整文件頭中的dataSectors的大小,因?yàn)槊總€(gè)文件頭都只占了一個(gè)sector,增加了額外的信息之后就會(huì)減少一個(gè)文件所能使用的塊數(shù)。對(duì)于路徑來(lái)說(shuō),這個(gè)比較麻煩,實(shí)際上是我在完成了exercise3以后才回來(lái)完成的,我在文件頭里面加入一個(gè)信息是父目錄的文件頭的塊號(hào),但是因?yàn)槲募^里面并沒(méi)有存儲(chǔ)改文件的名字,名字在上級(jí)目錄中,所以我向上找兩級(jí)父目錄,在這個(gè)祖父目錄中通過(guò)父目錄的塊號(hào)來(lái)找到父目錄的文件名(如果中間遇到了parentDire是-1的話,說(shuō)明遇到了root,停止向上查找),為了實(shí)現(xiàn)這個(gè)功能,我在directory中加入了一個(gè)findName函數(shù),這個(gè)函數(shù)通過(guò)匹配sector號(hào)來(lái)找到文件名。在實(shí)現(xiàn)了多級(jí)目錄之后,只要調(diào)用-l就可以打印出所有我定義在文件頭中的信息,以及文件的父目錄。Exercise3:這個(gè)練習(xí)要求通過(guò)使用間接索引來(lái)使得文件的大小突破4K,主要思路并不難想象,定義一個(gè)一級(jí)索引作為例子,在文件頭中使用一個(gè)int型屬性來(lái)指示一級(jí)索引所在的文件塊,然后如果文件小于直接索引所能提供的大小,那么把這個(gè)間接索引值賦成無(wú)效的-1,否則在bitmap中找一個(gè)空閑塊,賦給這個(gè)間接索引值,然后通過(guò)synchdisk的writesector把寫(xiě)回這個(gè)塊即可。想明白以后,最大的困難是不知道文件是怎么寫(xiě)的,因?yàn)橹翱吹竭^(guò)許多各種各樣的writeback,有的是用塊號(hào),有的是用openfile。參考文件頭自己的writeback方法,它用的是synchdisk的writesector,這個(gè)函數(shù)接受一個(gè)塊號(hào)作為地址,但是開(kāi)始的時(shí)候我不明白的是為什么第二個(gè)參數(shù),也就是寫(xiě)的內(nèi)容是(char*)this,比較奇怪,后來(lái)想了很久明白了這里就是把這個(gè)對(duì)象的內(nèi)容整體寫(xiě)到塊中,而這個(gè)對(duì)象的內(nèi)容就是類中的屬性。也就是說(shuō)我可以把任何我想寫(xiě)的東西寫(xiě)入磁盤塊中,只要大小不超過(guò)一個(gè)塊的大小,而且必須包裝成char*數(shù)組的形式,于是我定義一個(gè)int型數(shù)組來(lái)存放文件頭中需要的磁盤塊號(hào),在分配或者修改的時(shí)候把這個(gè)數(shù)組寫(xiě)回磁盤即可。在分配函數(shù)allocate中,我首先判斷需要的大小是不是超過(guò)直接索引能夠存儲(chǔ)的范圍,如果沒(méi)有的話,皆大歡喜,直接使用原來(lái)的代碼,否則,先給直接索引分配需要的磁盤塊,之后給間接索引分配一個(gè)塊,并且存儲(chǔ)到間接索引這個(gè)屬性中,然后創(chuàng)建一個(gè)前面所說(shuō)的數(shù)組,大小就是Sectorsize/sizeof(int),這樣,給需要的塊找到相應(yīng)的位置,其余的賦成-1,最后把這個(gè)數(shù)組寫(xiě)到找到的間接索引指向的塊,大功告成。測(cè)試的時(shí)候,我先用python制造了一個(gè)超過(guò)4K大小的文件叫huge,之后使用nachos-cp把這個(gè)文件復(fù)制到nachos中再-D顯示內(nèi)容,結(jié)果成功了,說(shuō)明間接索引能夠很好的工作。Exercise4:多級(jí)目錄主要和directory有關(guān),但是在實(shí)際實(shí)現(xiàn)的時(shí)候發(fā)現(xiàn)directory的功能實(shí)在是太弱了,僅靠directory無(wú)法實(shí)現(xiàn)多級(jí)目錄,directory里面實(shí)際上與多級(jí)目錄相關(guān)的是add這個(gè)函數(shù),但是這個(gè)函數(shù)實(shí)際上也只是將文件名和sector號(hào)的這樣一個(gè)對(duì)加入到directory對(duì)象的table屬性中(如果沒(méi)有重名的時(shí)候),而真正的創(chuàng)建目錄等操作這里無(wú)法完成,也就是說(shuō)得在其它地方創(chuàng)建好目錄,甚至寫(xiě)到相應(yīng)的磁盤塊之后,這時(shí)向directory中加入這一項(xiàng)才有意義。而且向directory中加入的只應(yīng)該是對(duì)應(yīng)文件頭的塊號(hào)(這一點(diǎn)我開(kāi)始的時(shí)候沒(méi)有按照這樣實(shí)現(xiàn),結(jié)果到了后來(lái)出現(xiàn)了很多矛盾)。因?yàn)槟夸浀膄etchfrom和writeback針對(duì)的都是openfile,這個(gè)openfile需要的是文件頭,也就是說(shuō)directory里的fetchfrom需要通過(guò)目錄文件頭的塊號(hào)來(lái)打開(kāi)目錄。所以如果自己需要建目錄的話,不僅需要寫(xiě)目錄所在的塊,還要?jiǎng)?chuàng)建并寫(xiě)入目錄對(duì)應(yīng)的文件頭所在的塊。再看每個(gè)目錄項(xiàng)中存儲(chǔ)的內(nèi)容,最開(kāi)始的時(shí)候是inUse和文件名-塊號(hào)對(duì),為了區(qū)別目錄中的目錄項(xiàng)是文件還是目錄,還需要加入是否是目錄這個(gè)屬性,為了節(jié)省空間,我同樣使用位運(yùn)算來(lái)標(biāo)記inUse和isDire兩個(gè)狀態(tài),即提供了get和set方法。之后我想仿照創(chuàng)建文件并加入目錄的方法,來(lái)創(chuàng)建目錄并加入目錄,首先來(lái)實(shí)驗(yàn)只加到根目錄中,我的做法是修改filesys中的create,要求create的參數(shù)中包括要?jiǎng)?chuàng)建的是不是目錄。做法基本上照抄創(chuàng)建文件的方法,不同的是allocate的時(shí)候需要的大小是DirectoryFileSize(有宏定義好的),并且不僅要寫(xiě)回根目錄、bitmap和文件頭,還需要寫(xiě)回新創(chuàng)建的目錄。實(shí)驗(yàn)的時(shí)候創(chuàng)建一個(gè)目錄dire,并且list出來(lái),能夠顯示,但是當(dāng)然是空的。接下來(lái)就是考慮如何真正實(shí)現(xiàn)多級(jí)目錄,也就是說(shuō)怎么在我新創(chuàng)建的目錄下創(chuàng)建目錄或者添加文件呢,我想到了正在使用的linux在文件頭中有.和..兩個(gè)文件,..代表的是上一級(jí)目錄(前面已經(jīng)介紹了實(shí)現(xiàn)),那么就是說(shuō)還需要支持.文件,也就是當(dāng)前工作目錄。怎么辦呢?我在filesys中添加了workingDireFile這個(gè)openfile*類型的屬性(因?yàn)閎itmap和根目錄也是通過(guò)這個(gè)屬性存儲(chǔ)的)。在filesys初始化的時(shí)候把創(chuàng)建出來(lái)之后的根目錄賦給工作目錄(符合常理),在create的時(shí)候首先判斷工作目錄是不是Null,如果是的話,那么把根目錄賦給工作目錄,之后接下來(lái)的一切之前和根目錄有關(guān)的操作,都改成使用工作目錄。在open和remove中也同樣如此。有了這些還不夠,我們還需要支持改變工作目錄,否則就不可能在新的目錄下創(chuàng)建文件或者目錄,于是我定義了一個(gè)gotoDirectory的函數(shù),接受名字(目錄或文件)作為參數(shù),這個(gè)函數(shù)首先打開(kāi)工作目錄,在工作目錄下找是否有具有這個(gè)名字的目錄,如果有這個(gè)目錄的話,那么把這個(gè)目錄作為工作目錄。這樣工作就基本完成了??墒菧y(cè)試的時(shí)候要看到結(jié)果,于是我修改了print函數(shù)(主要是directory中的),這里主要就是判斷是否是目錄,如果不是目錄,那么依舊使用原來(lái)的print,如果是目錄的話,那么先打開(kāi)這個(gè)目錄,之后執(zhí)行目錄的print,這樣即使有多層的目錄也可以遞歸地打印出結(jié)果了,list的修改方式類似,也需要遞歸,不過(guò)在文件頭層面上需要打印出文件中我自己定義的信息和父目錄名,方法上面已經(jīng)說(shuō)過(guò)了。Exercise5:動(dòng)態(tài)調(diào)整文件大小在這個(gè)任務(wù)中,我主要做了將文件擴(kuò)大的操作,也就是說(shuō)如果當(dāng)前文件的大小不夠需要寫(xiě)入的內(nèi)容的大小,那么我就根據(jù)另外需要的磁盤塊的數(shù)量,給這個(gè)文件分配更多的磁盤塊。為了實(shí)現(xiàn)這個(gè)功能,我在filehdr中定義了一個(gè)函數(shù)叫做AllocateMore,這個(gè)函數(shù)接受兩個(gè)參數(shù),一個(gè)是另外需要的文件塊數(shù),另一個(gè)是實(shí)際需要的字節(jié)數(shù)。在這個(gè)函數(shù)的實(shí)現(xiàn)過(guò)程中,我主要參考了allocate和create兩個(gè)函數(shù),要得到更多的空間,首先需要先得到標(biāo)記磁盤空閑位置的bitmap,因?yàn)閒ilesys里面有這個(gè)屬性,所以直接讀出freeMapFile這個(gè)屬性并且通過(guò)它取出bitmap,然后判斷直接索引中是不是還有剩余空間,之后判斷直接索引是不是足夠提供需要的磁盤塊,如果是的話,那么把需要的磁盤塊分配給這個(gè)文件,增加文件頭中的磁盤塊數(shù)到實(shí)際塊數(shù),寫(xiě)回bitmap,并且返回True。之后如果直接索引不足以提供需要的磁盤塊數(shù),那么先把直接索引中剩余的塊分配出去,之后再將間接索引中的塊分配出去,直到滿足需求為止。其中,如果本身沒(méi)有間接索引的話,還需要首先給這個(gè)間接索引分配一個(gè)塊,最后的時(shí)候還要把這些都寫(xiě)回到磁盤中。第二部分:系統(tǒng)調(diào)用Exercise6:Syscall.hStart.sException.cc這兩個(gè)文件需要一起結(jié)合著看,syscall這個(gè)頭文件可以說(shuō)沒(méi)有沒(méi)有相對(duì)應(yīng)的.cc文件,它對(duì)應(yīng)的實(shí)際上是start.s文件,就我們關(guān)心的幾個(gè)函數(shù)來(lái)說(shuō),create,open,close,read,write這幾個(gè)函數(shù)都是在syscall里面定義了函數(shù)的signature,而在start.s中把這些函數(shù)對(duì)應(yīng)的系統(tǒng)調(diào)用號(hào)SC_...放入二號(hào)寄存器中,使得exception里面的exceptionhandler可以識(shí)別出對(duì)應(yīng)的系統(tǒng)調(diào)用類型,來(lái)進(jìn)行相應(yīng)的處理。由于系統(tǒng)調(diào)用不能通過(guò)傳統(tǒng)的方式傳遞參數(shù),在start里面的注釋中可以看出,4~7號(hào)寄存器可以用來(lái)傳遞參數(shù),而2號(hào)寄存器在exception里面應(yīng)該作為傳遞返回值的寄存器。由于exception在用戶線程部分就已經(jīng)用過(guò)多次,這里就不介紹了。Exercise7,8:系統(tǒng)調(diào)用和調(diào)試就像之前實(shí)現(xiàn)tlbmiss和pagefault的時(shí)候一樣,這次也是主要修改exception中的exceptionhandler,其實(shí)有了前面的基礎(chǔ),這個(gè)練習(xí)相對(duì)簡(jiǎn)單,但是最麻煩的是傳遞參數(shù)的問(wèn)題,雖然知道是通過(guò)4個(gè)寄存器來(lái)傳遞參數(shù),但是實(shí)際編寫(xiě)的時(shí)候發(fā)現(xiàn)并不是這個(gè)簡(jiǎn)單,比如create需要的參數(shù)是一個(gè)char*指針,指向的是一個(gè)字符串,這個(gè)字符串是要?jiǎng)?chuàng)建的文件的名字,開(kāi)始的時(shí)候我直接把從4號(hào)寄存器中讀出的int值強(qiáng)制轉(zhuǎn)化為char*直接輸出,發(fā)現(xiàn)出現(xiàn)了段錯(cuò)誤,其它任何提示都沒(méi)有,直接就頭大了,后來(lái)經(jīng)過(guò)同學(xué)提醒,想起了這里的內(nèi)存空間應(yīng)該是一致的內(nèi)存空間而不是宿主機(jī)的內(nèi)存空間,如果直接使用char*轉(zhuǎn)化過(guò)的那個(gè)int型地址,得到的其實(shí)是宿主機(jī)上的相應(yīng)地址而不是nachos的內(nèi)存地址。這樣就是說(shuō)我們應(yīng)該去machine->mainMemory里面找這個(gè)字符串,有用的還是得到的那個(gè)地址,但是麻煩的是readMemory這個(gè)函數(shù)只接受int*作為寫(xiě)入的地址,也就是說(shuō)如果想直接向我自己定義的char數(shù)組中一個(gè)一個(gè)地寫(xiě)入char,是不可以的,因?yàn)檫@樣會(huì)覆蓋掉相鄰位置的字節(jié)。于是只能模擬讀int的方式,一次讀入4個(gè)字節(jié),每次把地址加4(而不是1)。這樣操作之后終于可以正確地輸出這個(gè)名字參數(shù)了。接著把名字和文件大?。ㄎ夷J(rèn)為一個(gè)sector的大小)作為參數(shù)傳遞給filesys的create,就可以創(chuàng)建文件了。但是開(kāi)始寫(xiě)的時(shí)候改正完這個(gè)錯(cuò)誤還不算完,程序運(yùn)行的時(shí)候就像死循環(huán)一樣,不停地在create里面轉(zhuǎn),后來(lái)想到之前寫(xiě)的tlbmiss的部分,明白了這里也應(yīng)該改pc指針的值,把nextpc的值賦給pc寄存器,這樣之后create終于可以很好地運(yùn)行了。完成了一個(gè)系統(tǒng)調(diào)用,其它的其實(shí)就簡(jiǎn)單多了,大多數(shù)是函數(shù)的包裝。對(duì)于open來(lái)說(shuō),還需要在當(dāng)前線程的地址空間中增加把這個(gè)打開(kāi)文件號(hào)的功能,我在地址空間中定義了一個(gè)存放打開(kāi)文件的數(shù)組,每次找空的位置放入新的打開(kāi)文件。這樣open就基本完成了,close就是delete數(shù)組中這個(gè)文件號(hào)對(duì)應(yīng)的文件,然后把數(shù)組中這個(gè)位置置為null即可。對(duì)于write和read來(lái)說(shuō),需要有讀文件寫(xiě)入buffer的操作或者從buffer里面讀然后寫(xiě)入到文件,因?yàn)閛penfile的read和write都是根據(jù)seekposition的位置讀寫(xiě)的,具有不確定性,于是我選擇了使用readat和writeat,和讀文件名的時(shí)候不同的是,這里buffer和mainMemory都是char類型的,所以在寫(xiě)buffer的時(shí)候可以一個(gè)字節(jié)一個(gè)字節(jié)地寫(xiě)。而在寫(xiě)文件的時(shí)候還需要判斷當(dāng)前文件的大小是不是足夠盛放這些要寫(xiě)的內(nèi)容,如果不夠的話,那么還需要先allocatemore。測(cè)試程序的話,很簡(jiǎn)單,就是仿照halt.c的寫(xiě)法,先create,然后open,write,read,close最后halt就可以了。這里我碰到的一個(gè)問(wèn)題是不能在main里面定義變量(所謂的局部變量),必須在main外面定義全局變量,否則編譯不會(huì)通過(guò)。Exercise9:閱讀代碼:Synchdisk.hsynchdisk.ccSynchdick的主要作用是確保磁盤訪問(wèn)的互斥性,為了做到這一點(diǎn),它是用了信號(hào)量Semaphore和互斥鎖lock,lock的作用是起到比較“霸道”的讀寫(xiě)鎖的作用,也就是說(shuō),在同一時(shí)刻,只能有一個(gè)線程在讀或者寫(xiě),而不是想真正的讀寫(xiě)鎖那樣只排斥讀的時(shí)候的寫(xiě)操作,或者寫(xiě)的時(shí)候的讀操作。而Semaphore是用來(lái)起到類似于中斷的作用,當(dāng)發(fā)起讀或者寫(xiě)操作以后,就使信號(hào)量進(jìn)行P操作,而當(dāng)磁盤的讀或者寫(xiě)操作結(jié)束后,就會(huì)進(jìn)行V操作,喚醒synchdisk,繼續(xù)執(zhí)行剛才的操作。而這個(gè)V操作其實(shí)是通過(guò)synchdisk的包裝函數(shù)RequestDone函數(shù)來(lái)實(shí)現(xiàn)的,這個(gè)包裝函數(shù)的作用在于disk操作結(jié)束之后就會(huì)自動(dòng)調(diào)用作為參數(shù)傳入disk的構(gòu)造函數(shù),這樣disk在進(jìn)行完讀寫(xiě)操作后就可以自動(dòng)調(diào)用這個(gè)函數(shù),從而間接進(jìn)行V操作。而V操作執(zhí)行完畢之后,就會(huì)解鎖。實(shí)現(xiàn)SynchConsole其實(shí)在通過(guò)看progtest里面有關(guān)console的實(shí)現(xiàn)就可以看出,nachos的console也是可以互斥訪問(wèn)的,但是當(dāng)前是通過(guò)在外部包上PV操作來(lái)實(shí)現(xiàn)的,在ConsoleTest中,創(chuàng)建了兩個(gè)信號(hào)量,一個(gè)是readAvail,一個(gè)是WriteDone,在取字操作之前,首先readAvail進(jìn)行P操作,把這個(gè)字符反過(guò)來(lái)輸出到屏幕上之后,WriteDone進(jìn)行P操作,這樣,就可以仿照這樣的方式,創(chuàng)建一個(gè)新的類SynchConsole,像SynchDisk一樣對(duì)Console進(jìn)行包裝,從而實(shí)現(xiàn)目標(biāo)。為了避免編譯的麻煩,我選擇在console中直接定義SynchConsole類,而不另外使用其它文件。仿照SynchDisk,我在SynchConsole中使用了讀鎖readLock,寫(xiě)鎖writeLock,因?yàn)閏onsole中使用了readAvail和writeDone兩個(gè)信號(hào)量,而Console會(huì)在讀寫(xiě)控制臺(tái)之后調(diào)用兩個(gè)信號(hào)量的V操作,所以我在SynchConsole中,也定義了兩個(gè)包裝函數(shù)也叫readAvail和writeDone,這兩個(gè)函數(shù)也像progtest里面的同名函數(shù)一樣,直接調(diào)用synchConsole中的對(duì)應(yīng)的V操作。于是,在構(gòu)造函數(shù)中,new出讀寫(xiě)鎖,讀寫(xiě)信號(hào)量,以及真正進(jìn)行控制臺(tái)操作的console成員變量,在putchar中首先取得寫(xiě)鎖,然后調(diào)用console的putchar函數(shù),之后調(diào)用寫(xiě)信號(hào)量的P操作,最后釋放寫(xiě)鎖,getchar的操作與此類似,只是要先調(diào)用讀的信號(hào)量的P操作,然后調(diào)用console的getchar函數(shù)。這樣之后,synchConsole就完成了。Exercise10:為了保證第一個(gè)要求,每一個(gè)線程擁有獨(dú)自的文件訪問(wèn)位置,我在Addrspace中定義了一個(gè)OpenFile指針數(shù)組,為了簡(jiǎn)便,我直接通過(guò)宏指定這個(gè)數(shù)組的大小為10,因?yàn)檫@里只是限制用戶打開(kāi)的文件之間互不干擾,于是我在處理系統(tǒng)調(diào)用Open的時(shí)候(exceptionhandler里面),把要打開(kāi)的OpenFile指針?lè)诺絚urrentThread的space里面,為了保持ExceptionHandler代碼的簡(jiǎn)潔,我在addrspace里面定義了一個(gè)AddToOpenFile函數(shù),用來(lái)通過(guò)遍歷數(shù)組找到空位,把OpenFile的指針?lè)湃氲絪pace的openFiles數(shù)組(我自己定義的)中。這個(gè)函數(shù)會(huì)返回在數(shù)組中的下標(biāo),這樣就可以把這個(gè)文件號(hào)作為返回值了。相應(yīng)的,還需要在Close里面加上從數(shù)組中去除要關(guān)閉的OpenFile,于是我把數(shù)組中的指針delete掉,并且置為NULL。之后在Read和Write中也就可以通過(guò)文件下標(biāo)直接得到space中的OpenFile了。對(duì)于第二個(gè)要求,我認(rèn)為既然已經(jīng)使用了SynchDisk,那么就是原子化的了,因?yàn)樵谧x寫(xiě)之前都必須首先申請(qǐng)鎖。對(duì)于第三個(gè)要求,為了保證在要?jiǎng)h除一個(gè)文件時(shí)必須保證其它線程已經(jīng)關(guān)閉這個(gè)文件,我在system.cc中定義了一個(gè)全局的打開(kāi)文件鏈表,鏈表中存放文件打開(kāi)的次數(shù)和文件頭所在的sector號(hào),并且實(shí)現(xiàn)了AddOpenFile和RemoveOpenFile兩個(gè)函數(shù),AddOpenFile在鏈表中查找是否已經(jīng)打開(kāi)了這個(gè)文件(通過(guò)打開(kāi)文件所在的sector號(hào)是否相同來(lái)判斷),如果已經(jīng)打開(kāi)了這個(gè)文件,那么打開(kāi)次數(shù)加一,否則把這一項(xiàng)加入到鏈表中,打開(kāi)次數(shù)設(shè)置為1。RemoveOpenFile也是先找這個(gè)文件,如果找到則給引用次數(shù)減一,如果沒(méi)找到,返回-1。為了在刪除判斷的時(shí)候方便,我還定義了GetOpenFileNum這個(gè)函數(shù),這個(gè)函數(shù)通過(guò)和上面一樣的方法,遍歷找到這個(gè)文件的打開(kāi)次數(shù),在FileSys得到Remove里面,如果查詢到要?jiǎng)h除的打開(kāi)文件次數(shù)是0,那么刪除這個(gè)文件,否則返回False。這樣就完成了這個(gè)Exercise。Challenge1:為了實(shí)現(xiàn)Exec系統(tǒng)調(diào)用,可以仿照progtest里面的StartProcess,于是我基本上按照StartProcess的實(shí)現(xiàn)方法在Exception.cc里面加入一個(gè)同名函數(shù)StartProcess,不同的只是在于這里我需要從寄存器和machine的mainMemory里面讀取出可執(zhí)行文件名,因?yàn)樾枰С侄嗑€程執(zhí)行不同的程序,所以我在ExceptionHandler里面的SC_Exec里面還需要使用新創(chuàng)建的thread,并且用這個(gè)新的thread去Fork出StartProcess這個(gè)函數(shù),這樣,即使多次使用系統(tǒng)調(diào)用Exec,也可以并行地執(zhí)行了。對(duì)于Join來(lái)說(shuō),主要是需要被Join的線程等待join的線程,可以通過(guò)在調(diào)度到父線程時(shí)不斷地yield來(lái)實(shí)現(xiàn)。為了明確什么要等待的子線程是不是已經(jīng)結(jié)束運(yùn)行了,需要在父線程中維護(hù)一個(gè)數(shù)組來(lái)記錄該線程都有哪些子線程,這樣也就給了join目標(biāo)參數(shù),我在thread類中定義了這樣一個(gè)數(shù)組,并且定義了相應(yīng)的將子線程加入到數(shù)組中的函數(shù)AddChild,這個(gè)函數(shù)遍歷數(shù)組,找到空位后將子線程指針加入到空位中,返回剛才加入的數(shù)組下標(biāo)。并且需要在thread類中加入join函數(shù),這個(gè)函數(shù)接收子線程號(hào)作為參數(shù),如果數(shù)組中這個(gè)下標(biāo)的線程還不是NULL(通過(guò)while循環(huán)來(lái)判斷),那么就一直Yield。這樣的機(jī)制必須允許子線程到父線程中修改子線程數(shù)組,于是我在AddChild函數(shù)里面,把currentThread賦給子線程的parentThread指針,并且在thread的finish函數(shù)里面把父線程的具有子線程spaceId(在exec系統(tǒng)調(diào)用中賦給子線程)的數(shù)組元素賦成NULL,這樣當(dāng)父線程再次檢查的時(shí)候就可以跳出while循環(huán)了。對(duì)于Exit,比較簡(jiǎn)單直接調(diào)用currentThread的finish函數(shù)并且使得pc指針前進(jìn)即可。Challenge4:實(shí)現(xiàn)Fork和Yield從syscall.h中可以看出,在Nachos中Fork和Exec的區(qū)別在于Exec會(huì)創(chuàng)建不同的Addrspace而Fork和父線程使用相同的Addrspace,Nachos和Linux的Fork的不同點(diǎn)在于Linux只需要復(fù)制父線程的空間即可,而Nachos直接要求Fork出來(lái)的線程運(yùn)行一個(gè)函數(shù)。于是在ExceptionHandler中我加入對(duì)SC_Fork的支持,首先我new出一個(gè)thread,并把它加入到currentThread的子線程中,接下來(lái)問(wèn)題來(lái)了,怎么使得子線程和父

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論