1. 程式人生 > 其它 >BUAA_OS lab2實驗報告

BUAA_OS lab2實驗報告

一、思考題

思考2.1

請思考cache用虛擬地址來查詢的可能性,並且給出這種方式對訪存帶來的好處和壞處。另外,你能否能根據前一個問題的解答來得出用實體地址來查詢的優勢?

我認為cache使用虛擬地址查詢在理論上是可行的,但實際上要有很多顧慮。使用虛擬地址和好處:若cache能命中,則不需要經過實體地址就能得到資料,提高效能。壞處:對於不同程序來說,相同的虛擬地址可能對映到不同的實體地址,為獲得自己程序對應的此虛擬地址上的資料,防止程序間相互干擾,可能需要額外思考區分方式,比如增加表示程序的標誌位(這導致佔用空間變大)、或者為每個程序分配一個cache(這不可能);cache未命中時,需要訪問頁表載入資料,而頁表一般較大,這會需要更多的時間。

使用實體地址的優勢:cache未命中時載入速度相對較快;每個地址只對應一個數據,更加安全,且使得程序間的資料可以共享。

思考2.2

在我們的實驗中,有許多對虛擬地址或者實體地址操作的巨集函式(詳見include/mmu.h ),那麼我們在呼叫這些巨集的時候需要弄清楚需要操作的地址是實體地址還是虛擬地址,閱讀下面的程式碼,指出x是一個實體地址還是虛擬地址。

int x;
char *value = return_a_pointer();
*value = 10;
x = (int) value;

x是虛擬地址(c語言中指標指向的地址都是虛擬地址)。

思考2.3

我們在 include/queue.h 中定義了一系列的巨集函式來簡化對連結串列的操作。實際上,我們在 include/queue.h 檔案中定義的連結串列和 glibc 相關原始碼較為相似,這一連結串列設計也應用於 Linux 系統中 (sys/queue.h 檔案)。請閱讀這些巨集函式的程式碼,說說它們的原理和巧妙之處。

  • LIST_HEAD建立連結串列頭,名稱、資料型別都可以根據需求設定

  • LIST_HEAD_INITIALIZE初始化連結串列頭,可用於直接重置連結串列

  • LIST_ENTRY連結串列第一個節點,其中*le_next指向下一個節點,**le_prev指向前一個節點的*le_next

  • LIST_EMPTY判斷連結串列是否為空

  • LIST_FIRST返回指向連結串列第一個節點的指標

  • LIST_FOREACH用於遍歷連結串列

  • LIST_INIT建立新連結串列

  • LIST_INSERT_AFTER在某結點後插入新節點

  • LIST_INSERT_BEFORE在某結點前插入新節點,需要操作需要操作新節點兩個指標,原前面結點的le_next

    指標,原後面結點的le_prev指標

  • LIST_INSERT_HEAD在連結串列頭插入新節點,需要首先判斷原頭結點是否為空,若為空,直接將新節點作為頭結點,不空則需要設定原頭結點的le_prev指標

  • LIST_INSERT_TAIL在連結串列尾部插入新節點

  • LIST_REMOVE移除節點,若被移除的節點位於連結串列尾部,則只需設定其前面節點的指標,否則還需要設定其後面節點的le_prev指標

巧妙之處:節點中連結前後的指標與平時所用連結串列的指標方式不同,這使得在清空連結串列、節點插入和刪除時更方便。巨集定義使得連結串列的幾乎所有操作都可以呼叫相同的巨集函式實現,便於使用。將指向連結串列的頭結點的指標包括在一個結構體中,而沒有直接傳遞和使用連結串列頭結點,可能也具有保護作用。

思考2.4

我們注意到我們把巨集函式的函式體寫成了 do { /* ... */ } while(0)的形式,而不是僅僅寫成形如 { /* ... */ } 的語句塊,這樣的寫法好處是什麼?

保證巨集替換時其內容仍是一個完整的語句塊,其中定義的臨時變數只在這個程式碼塊中有效,防止其與外部產生衝突。

不直接使用{}包圍可以防止語法錯誤,因為日常c語言書寫語句時需要在語句後寫分號,呼叫巨集函式也是如此,do while句式後面是需要分號的,但如果巨集定義中只是簡單的大括號括起來,其後面再加分號就會出現語法錯誤。

思考2.5

注意,我們定義的 Page 結構體只是一個資訊的載體,它只代表了相應實體記憶體頁的資訊,它本身並不是實體記憶體頁。 那我們的實體記憶體頁究竟在哪呢?Page 結構體又是通過怎樣的方式找到它代表的實體記憶體頁的地址呢? 請你閱讀 include/pmap.h 與 mm/pmap.c 中相關程式碼,並思考一下。

實體記憶體頁空間由pmap.c檔案中的mips_vm_init函式初始分配,是pages指標指向的位置。實體記憶體頁地址 = 物理頁號 * 頁面大小,在我們的作業系統中是物理頁號左移12位。對於Page結構體,可以呼叫pmap.h標頭檔案中的page2pa函式,以結構體的指標為引數獲得實體記憶體頁地址。

思考2.6

請閱讀 include/queue.h 以及 include/pmap.h, 將Page_list的結構梳理清楚,選擇正確的展開結構(請注意指標)。

C

思考2.7

在 mmu.h 中定義了 bzero(void *b, size_t) 這樣一個函式,請你思考,此處的b指標是一個實體地址, 還是一個虛擬地址呢?

b指標是虛擬地址。在pamp.c的*alloc函式中呼叫了這一函式,b對應的是變數alloced_mem,是虛擬地址。

思考2.8

瞭解了二級頁表頁目錄自對映的原理之後,我們知道,Win2k核心的虛存管理也是採用了二級頁表的形式,其頁表所佔的 4M 空間對應的虛存起始地址為 0xC0000000,那麼,它的頁目錄的起始地址是多少呢?

頁表大小為4M,因此起始位置對應的物理頁號為0xC0000000 / 4M = 0x300,頁目錄起始地址為0xC0000000 + 0x300 * 4K = 0xC0300000.

思考2.9

注意到頁表在程序地址空間中連續存放,併線性對映到整個地址空間,思考:是否可以由虛擬地址直接得到對應頁表項的虛擬地址?上一節末尾所述轉換過程中,第一步查頁目錄有必要嗎,為什麼?

由虛擬地址可以直接得到對應頁表項的虛擬地址。

第一步查頁目錄有必要,因為需要根據頁目錄中儲存的資訊判斷二級頁表是否有效(其餘11個標誌位同樣需要檢查)。

設定二級頁表也是有必要的。查詢資料後我瞭解到,儘管二級頁表甚至比一級頁表多了4KB儲存頁目錄的記憶體消耗,但二級頁表在程序上遠比一級頁表更節省空間。具體為:一級頁表需要連續儲存,這意味著只能使用基地址+偏移的方式查詢頁表。因此為防止空間不夠,一個程序開始時就需要把所有一級頁表都載入到記憶體中,即使並不是所有地址都對映到了頁表上。而二級頁表機制中只需要連續儲存4KB的頁目錄,之後頁表在使用到的時候再動態分配,因此節約了記憶體。

思考2.10

觀察給出的程式碼可以發現,page_insert 會預設為頁面設定PTE_V的許可權。請問,你認為是否應該將PTE_R 也作為預設許可權?並說明理由。

我認為不應該。是否賦予寫入許可權應該有執行插入的程式決定,如果確實有寫入的需要,可以通過perm變數傳參。

思考2.11

思考一下tlb_out 彙編函式,結合程式碼闡述一下跳轉到NOFOUND的流程?從MIPS手冊中查詢tlbp和tlbwi指令,明確其用途,並解釋為何第10行處指令後有4條nop指令。

#include <asm/regdef.h>
#include <asm/cp0regdef.h>
#include <asm/asm.h>

LEAF(tlb_out)
nop
mfc0 k1,CP0_ENTRYHI
mtc0 a0,CP0_ENTRYHI
nop
// insert tlbp or tlbwi
nop
nop
nop
nop
mfc0 k0,CP0_INDEX
bltz k0,NOFOUND
nop
mtc0 zero,CP0_ENTRYHI
mtc0 zero,CP0_ENTRYLO0
nop
// insert tlbp or tlbwi
NOFOUND:

mtc0 k1,CP0_ENTRYHI

j ra
nop
END(tlb_out)

grep查詢呼叫這一彙編函式的位置,發現在pmap.c檔案中,函式如下:

void
tlb_invalidate(Pde *pgdir, u_long va)
{
if (curenv) {
tlb_out(PTE_ADDR(va) | GET_ENV_ASID(curenv->env_id));
} else {
tlb_out(PTE_ADDR(va));
}
}

引數為va對應的實體地址,因此在調用匯編函式時,a0暫存器即儲存了這一實體地址。

函式首先將CP0_ENTRYHI中原有的值寫入k1暫存器,然後將a0暫存器值轉存到k1中,待mtc0指令退出流水線後執行tlbp指令查詢引數的地址是否存在於tlb中。如果不存在,將Index最高位置1;如果存在,將地址所在項index寫入到Index暫存器中。

由於前一步可能寫入Index暫存器,後一步需要獲取Index暫存器的資料,因此需要等寫入指令出流水線後再執行下一條指令,對於五級流水線,需要暫停4個nop指令。

之後將剛剛寫入的index讀出,執行bltz指令,若index < 0,即最高位置1了,說明tlb確實,跳轉到NOTFOUND函式,恢復CP0_ENTRYHI,函式執行結束;若index <= 0,說明tlb命中,將EntryHiEntryLo重置為0,之後呼叫tlbwi指令將EntryHiEntryLo暫存器的值寫入到Index暫存器儲存的index對應的快表位置,函式執行結束。

思考2.12

顯然,執行後結果與我們預期的不符,va值為0x88888,相應的pa中的值為0。這說明我們的程式碼中存在問題,請你仔細思考我們的訪存模型,指出問題所在。

觀察va2pa函式:

static inline u_long
va2pa(Pde *pgdir, u_long va)
{
Pte *p;

pgdir = &pgdir[PDX(va)];

if (!(*pgdir & PTE_V)) {
return ~0;
}

p = (Pte *)KADDR(PTE_ADDR(*pgdir));

if (!(p[PTX(va)]&PTE_V)) {
return ~0;
}

return PTE_ADDR(p[PTX(va)]);
}

發現函式中只涉及相對頁目錄起始地址和相對頁表起始地址的偏移,沒有頁內偏移,因此返回的只是va所在頁表對應的實體地址,而不是真正的實體地址。因此之後0x88888的賦值也沒有對應到pa指向的位置。

思考2.13

在X86體系結構下的作業系統,有一個特殊的暫存器CR4,在其中有一個PSE位,當該位設為1時將開啟4MB大物理頁面模式,請查閱相關資料,說明當PSE開啟時的頁表組織形式與我們當前的頁表組織形式的區別。

若使用PSE技術,需要設定CR4中的第四位,此時也目錄中的表項第七位增添一個新標識(PS位),若此位為1,表示這個表項指向一個4MB的大物理頁面,否則指向普通的4KB頁面。PSE的頁目錄項只用到了原本20個地址位的高10位,後22位為0恰好表示這是一個4MB對齊的頁面。

二、實驗難點

1. Page儲存結構

2. task2.1

LIST_INSERT_TAIL是一個難點,它比已給出的LIST_INSERT_HEAD更復雜

其中遍歷查詢到最後一個節點可以使用LIST_FOREACH巨集函式,其本質上是一個for迴圈,但考慮到for迴圈中用於記錄迴圈的i這一變數,在當時我並沒有想到如何在巨集中定義出這一變數(事實上似乎可以直接定義,因為巨集的do{}while塊可以保證定義出來的變數不干擾到外部且不受外部干擾),因此最後我使用新節點的next部分作為了迴圈自增的變數。節點將作為尾節點插入,因此在使用next完成迴圈查詢後只需要最後將next指向NULL即可。

3. task2.5

虛擬地址部分邏輯並不是很難,主要難在對所給出的變數的理解和使用,不過我認為有時候拘泥於所給出的程式碼和提示,可能會擾亂自己的思路。在此列一下自己了理解的各個變數的意義和使用:

變數名意義用法
pgdir 頁目錄首地址(虛) pgdir + PDX(va)可以獲取一級頁表項的指標
pgdir_entryp 指向頁目錄中的頁表項 取指標指向的內容,呼叫PTE_ADDR獲取二級頁表的實體地址
pgtable 指向二級頁表首地址 pgtable + PTX(va)可以獲取二級頁表項的指標

其中需要注意的是,在呼叫KADDRpage2kva獲取虛擬地址後,需要使用(Pte *)進行強制型別轉換,否則在之後進行加法時會按照有符號數進行加減。

4. 頁表結構與自對映機制

我理解的對映機制是:

va的22-31位用於指示一級頁表項在頁目錄中的偏移,從而查詢到一級頁表項後,頁表項中的內容包括二級頁表的起始地址,再根據va的12-21位獲得在二級頁表內的偏移,從而查詢到二級頁表項,二級頁表項的內容包括了頁表在實體記憶體中的起始地址,最後運用va的0-11位頁內偏移,找到虛擬地址對應的頁表項。

三、體會與感想

本次lab用時大約15小時,期間經歷了無數次理論體系的崩塌與重構,甚至曾以為實體記憶體就是硬碟上的地址?!

從這次作業開始,需要閱讀的程式碼量陡增,也更感覺吃力了些。儲存結構比較抽象,光靠腦子想是不行的,還是畫出圖來更能幫助理解,這種方法也要運用到之後的學習中。

這次作業也讓我意識到“過了”不是真的“過了”,以後做完可能還需要找幾個同學一起對一對邏輯,學習的同時也儘量避免自己的作業系統成為bug的舞池。

本次居然又掛掉了一次課上,lab2-2,全程在算task0,就沒有想到像其他同學一樣列舉。課上的時候可能思維陷入了一個怪圈,甚至一直在想64-9-9-9-12之後剩下的位數怎麼辦,還列了好多方程,以至於如此接近答案卻又沒有做出來。可能有時候,取巧也是一種必要的技能。

四、殘留難點

由於課上task0沒有做出來,後面的task也不知道有沒有做對,也沒有時間仔細研究後面的問題,這可能成為了永遠殘留的難點。如果可能,還是希望課程組在之後能開放一下課上內容的測試,哪怕只開放很短的一段時間,畢竟考試的最終目的還是掌握這些知識吧。