操做系統lab2實驗總結——Part1

 

這是MIPS虛擬映射佈局圖,在接下來的實驗中,咱們須要特別注意的地址分別是kuseg和kseg0區,首先列出這兩個區域的意義。緩存

MIPS虛存映射佈局

32位的MIPS CPU最大尋址空間爲4GB(2^32字節),這4GB虛存空間被劃分爲四個部分:app

  1. kuseg (TLB-mapped cacheable user space, 0x00000000 - 0x7fffffff): 這一段是用戶模式下可用的地址,大小爲2G,也就是MIPS約定的用戶內存空間。須要經過MMU進行虛擬地址到物理 地址的轉換。ide

  2. kseg0 (direct-mapped cached kernel space, 0x80000000 - 0x9fffffff): 這一段是內核地址,其內存虛存地址到物理內存地址的映射轉換不經過MMU,使用時只須要將地址的最高位清零 (& 0x7fffffff), 這些地址就被轉換爲物理地址。也就是說,這段邏輯地址被連續地映射到物理內存的低端512M空間。對這段地址 的存取都會經過高速緩存(cached)。一般在沒有MMU的系統中,這段空間用於存放大多數程序和數據。對於有 MMU 的系統,操做系統的內核會存放在這個區域。函數

kuseg中有三個大小爲PDMAP(4MB)的區域,分別從0x7f400000開始是ENVS,PAGES和User VPT。須要注意的是PAGES和User VPT的關係:User VPT中存放了1024個頁表,因爲自映射的關係,這個頁表裏又包括了頁目錄的頁表,每個頁表中又有1024個頁表項。PAGES存放了48個頁表,這48個頁表映射的內容是16*1024個物理頁框的結構體(struct Page)的物理內存。在lab2實驗中,咱們只用了boot_map_segment函數將PAGES的內容進行了映射,並無操做User VPT的空間。佈局

第一幅圖將是咱們理解整個mm/pmap.c的核心,接下來將按照init/init.c中的順序進行逐一解釋每一個函數的細節和總體的啓動佈局。spa

1 //這是init/init.c裏的函數調用順序
2 mips_detect_memory(); 3 mips_vm_init(); 4 page_init();
init/init.c

1、mips_detect_memory()

1.代碼塊

 1 void mips_detect_memory()  2 {  3     //in bytes
 4     basemem = 64 * 1024 * 1024;//物理內存的大小爲64MB-->26位
 5     npage = 16 * 1024;//物理頁框的個數
 6     maxpa = basemem;//最大的物理地址
 7     extmem = 0;//延申的空間
 8     printf("Physical memory: %dK available, ", (int)(maxpa / 1024));  9     printf("base = %dK, extended = %dK\n", (int)(basemem / 1024), 10            (int)(extmem / 1024)); 11 }
mips_detect_memory

2.初始化一些內存相關的參數,意義寫在註釋裏,不贅述。操作系統

2、mips_vm_init()

1.代碼塊

 1 void mips_vm_init()  2 {  3     extern char end[];  4     extern int mCONTEXT;  5     extern struct Env *envs;  6 
 7     Pde *pgdir;  8  u_int n;  9 
10     /* Step 1 */
11     pgdir = alloc(BY2PG, BY2PG, 1); 12     printf("to memory %x for struct page directory.\n", freemem); 13     mCONTEXT = (int)pgdir;//mCONTEXT是虛擬地址
14 
15     boot_pgdir = pgdir; 16 
17     /* Step 2*/
18     pages = (struct Page *)alloc(npage * sizeof(struct Page), BY2PG, 1); 19     printf("to memory %x for struct Pages.\n", freemem); 20 
21     n = ROUND(npage * sizeof(struct Page), BY2PG); 22 
23  boot_map_segment(pgdir, UPAGES, n, PADDR(pages), PTE_R); 24 
25     /* Step 3*/
26     envs = (struct Env *)alloc(NENV * sizeof(struct Env), BY2PG, 1); 27     n = ROUND(NENV * sizeof(struct Env), BY2PG); 28  boot_map_segment(pgdir, UENVS, n, PADDR(envs), PTE_R); 29 
30     printf("pmap.c:\t mips vm init success\n"); 31 }
View Code

2.目的:

  step1:申請了4KB的頁目錄空間。(pgdir)線程

  step2:申請了192KB的結構體空間,將這一塊物理內存進行二級映射。指針

  step3:申請了線程的空間。(這一塊不在這裏討論)code

3.佈局圖

在函數的起始位置聲明瞭

extern char end[];

 end[]的地址就是0x8040 0000,在tools/scse0_3.lds處進行了定義。

理解這個函數的關鍵是理解alloc()函數
 1 static void *alloc(u_int n, u_int align, int clear)  2 {  3 
 4     extern char end[];  5  u_long alloced_mem;  6 
 7     if (freemem == 0)  8  {  9         freemem = (u_long)end; 10  } 11 
12     freemem = ROUND(freemem, align); 13     alloced_mem = freemem; 14     freemem = freemem + n; 15 
16     if (clear) 17  { 18         bzero((void *)alloced_mem, n); 19  } 20 
21     if (PADDR(freemem) >= maxpa) 22  { 23         panic("out of memorty\n"); 24         return (void *)-E_NO_MEM; 25  } 26 
27     return (void *)alloced_mem; 28 }
alloc

freemem(全局變量)是end到physics Memory Max的空閒內存,每一次調用都將alloced_mem移到分配空間的開頭,freemem移到分配空間的結尾,並返回了alloced_mem的地址。

因此mips_vm_init對於0x8040 0000之後的空間操做以下:

 

首先給pgdir在0x80400000申請了一個4KB的空間,而後給全局變量struct Page *pages申請了npage * sizeof(struct Page)即16*1024*12=192KB的空間。

1 struct Page{ 2          struct { 3             struct Page *le_next; 4             struct Page **le_prev; 5  } pp_link; 6  u_short pp_ref; 7     };
struct Page// sizeof(struct Page)=0xC

接着在boot_map_segment()裏,每一次映射在一級頁表的索引是相同的,因此會創造一個二級頁表。

4.boot_map_segment(Pde *pgdir, u_long va, u_long size, u_long pa, int perm)

4.1代碼塊

 1 void boot_map_segment(Pde *pgdir, u_long va, u_long size, u_long pa, int perm)  2 {  3     int i, va_temp;  4     Pte *pgtable_entry;  5 
 6     assert(size % BY2PG == 0);  7 
 8     for (i = 0; i < size; i += BY2PG)  9  { 10         pgtable_entry = boot_pgdir_Walk(pgdir, va + i, 1);//返回的是頁表項的虛擬地址
11         *pgtable_entry = (pa + i) | perm | PTE_V; //物理地址映射上
12  } 13 }
View Code

4.2理解和畫了一個的圖(具體說明了end[]之後的虛擬空間是怎麼佈局的)

這個函數的目的是將虛擬地址空間[va, va+size) 和物理地址空間[pa, pa+size)創建映射關係。什麼叫創建映射關係呢?即在虛擬地址所對應的二級頁表裏存放(物理頁框的)物理地址。下面如下面這個調用爲例,說明二級頁表的映射過程。

boot_map_segment(pgdir, UPAGES, n, PADDR(pages), PTE_R);

這個函數的具體映射是經過boot_pgdit_walk實現的。

static Pte *boot_pgdir_walk(Pde *pgdir, u_long va, int create)

 

          

 下面說明一下Pde*的理解(Pde*和Pte*是同樣的)

Pde *pgdir_entryp;

在include/mmu.h中定義了

typedef u_long Pde;

Pde的類型就是unsigned long,佔4B。它的指針是要尋找虛擬空間,大小也是一個頁表項,4B。Pde和Pde*都是4B,使它們的轉換很是方便。指針實際上一塊以它爲地址的內存單元。因此Pde的指針(Pde*)所指的地址就是虛擬地址,而這塊地址的內容(Pde)是物理地址,因此實現了虛擬地址和物理地址的映射關係。

3、page_init()

1.代碼塊

 1 void page_init(void)  2 {  3     /* Step 1 */
 4     LIST_INIT(&page_free_list); //page free list is a struct
 5     /* Step 2*/
 6     freemem = ROUND(freemem, BY2PG);  7     /* Step 3 */
 8     int i;  9     int free_page_size = PADDR(freemem) / BY2PG; 10     for (i = 0; i < free_page_size; i++) 11  { 12         pages[i].pp_ref = 1; 13  } 14     /* Step 4 */
15     for (; i < npage; i++) 16  { 17         pages[i].pp_ref = 0; 18         LIST_INSERT_HEAD(&page_free_list, &pages[i], pp_link); 19  } 20 }
page_init()

2.目的

這個函數自己很簡單,其中有兩個問題須要注意:

  • 問題1:struct Page *pages; 爲何要申請這些結構體?它和咱們的頁表有什麼關係?
  • 問題2:Step3"Mark all memory below `freemem` as used"的意義是什麼?

在以前的操做中,咱們一直使用了兩個虛擬地址,一個是在kuseg0(end[](0x8040 0000) 之後),一個是在kuseg(裏面有UPAGES,UVPT,UENVS)。前者不須要mmu直接將高位清零獲得物理地址,後者須要mmu將虛擬地址映射到物理地址。因此其實咱們對於end[]之後的全部操做能夠理解成這是在對物理內存進行操做。物理內存的大小有多少呢?64MB。64MB的物理內存中有16*1024個物理頁框,則16*1024個struct Page和64MB的物理頁框是一一對應的。咱們知道對空閒物理內存進行管理有兩種基本的操做:1.位圖;2.鏈表。因此page_free_list能夠把空閒的struct Page串起來。

在step3中咱們就將end[]後面已經使用的內核虛擬地址所對應的物理地址佔用的內存,轉換爲 [PADDR(freemem) / BY2PG] 個頁表,並把它們標記過已經使用的。

struct Page和物理內存是怎麼一一對應的呢?

  (pp - pages)<< PGSHIFT

就是算出這是pages裏的第幾個Page,再乘上4KB,就是物理地址了。

上面的兩個問題就解決了。

相關文章
相關標籤/搜索