邏輯位址與實體位址的轉化
頁表是由頁表項(PTE)組成的數組。512個PTE構成一個頁表頁(Page-table page)。
PTE中包含了實體頁碼(PPN physical page number)以及一些标志,來控制實體空間塊的讀寫通路權限。
實體位址與虛拟位址的映射為三層樹形結構,每一層存儲下一層頁表頁的位址,最後一層存儲實體位址的PTE。
(個人了解:計算機中的實體位址被邏輯性了解成了頁面+偏移量,其本質沒發生變化,依舊是某一記憶體單元的編号)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZwpmL3V2dihDM1QGMzo2d5IWbyMXO3Z3Zxk3ZOt2czkGOwAzLcV2ZyFGbvwlbj5yZtlWYul2cuETY2R3Lc9CX6MHc0RHaiojIsJye.jpg)
圖 PTE的格式
代碼解析
walk函數用來獲得邏輯位址(va virtual address)的所在實體位址(pa physical address)的PTE (一般都是對整個page做操作,很少有對某個特定位址進行操作,是以是獲得該va所在page的PTE,主要操作包括對其标志位進行處理)
主要展示了邏輯位址是如何轉換成實體位址的,這是分頁機制最核心的一個部分
page和上篇exec提到的segment共同組成計算機中虛拟記憶體機制
// Return the address of the PTE in page table pagetable
// that corresponds to virtual address va. If alloc!=0,
// create any required page-table pages.
//
// The risc-v Sv39 scheme has three levels of page-table
// pages. A page-table page contains 512 64-bit PTEs.
// A 64-bit virtual address is split into five fields:
// 39..63 -- must be zero.
// 30..38 -- 9 bits of level-2 index.
// 21..29 -- 9 bits of level-1 index.
// 12..20 -- 9 bits of level-0 index.
// 0..11 -- 12 bits of byte offset within the page.
//傳回邏輯位址va所對應的PTE的指針
pte_t *
walk(pagetable_t pagetable, uint64 va, int alloc)
{
if(va >= MAXVA)
panic("walk");
for(int level = 2; level > 0; level--) {
//PX(level,va) 擷取level對應的PTE 如上圖的L2
//以L2為例,這裡獲得了L2在第三級頁表頁中位置編号
//相當于實體位址的偏移量
pte_t *pte = &pagetable[PX(level, va)];
// PTE_V valid 表示PTE是否合法
if(*pte & PTE_V) {
//PTE2PA 将PTE轉換成實際的實體位址
//這個實體位址即第二級頁表頁的起始位址
pagetable = (pagetable_t)PTE2PA(*pte);
} else {
//如果PTE_V=0 說明第二級頁表頁還未建立,則建立一個
if(!alloc || (pagetable = (pde_t*)kalloc()) == 0)
return 0;
//用0填充
memset(pagetable, 0, PGSIZE);
//然後将PTE_V置 1
*pte = PA2PTE(pagetable) | PTE_V;
}
}
return &pagetable[PX(0, va)];
}
附:
typedef uint64 pte_t;
typedef uint64 *pagetable_t; // 512 PTEs
// extract the three 9-bit page table indices from a virtual address.
#define PXMASK 0x1FF // 9 bits
#define PXSHIFT(level) (PGSHIFT+(9*(level)))
#define PX(level, va) ((((uint64) (va)) >> PXSHIFT(level)) & PXMASK)
#define PTE_V (1L << 0) // valid
#define PTE_R (1L << 1)
#define PTE_W (1L << 2)
#define PTE_X (1L << 3)
#define PTE_U (1L << 4) // 1 -> user can access
// shift a physical address to the right place for a PTE.
#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)
#define PTE2PA(pte) (((pte) >> 10) << 12)
可執行檔案以節section劃分部分,載入記憶體後,以段segment劃分部分,換了個名字(大概)