![]() | ![]() | ... | ![]() | ![]() | ... | ![]() | |
^ 0x00000 | ^ start of ucore (0x100000) | ^ end of ucore
| 首地址为pages的npage个struc Page用于管理page | ^ maxpa
|
extern char end[]: 虚拟地址,指向ucore在内存的末尾,定义在ld链接文件中
pages:由end[]计算得出,也是虚拟地址,用于管理page的struct Page数组的头指针
alloc_page:申请一个page,返回的struct Page* 是虚拟地址
page2pa:根据struct Page* 相对头指针pages的偏移计算对应的page的起始物理地址
memset:需要使用虚拟地址进行操作
页目录表项和页表项保存的是物理地址,注意物理地址和虚拟地址的转换
static inline ppn_t
page2ppn(struct Page *page) {
return page - pages;
}
static inline uintptr_t
page2pa(struct Page *page) {
return page2ppn(page) << PGSHIFT;
}
pte_t *
get_pte(pde_t *pgdir, uintptr_t la, bool create) {
/* LAB2 EXERCISE 2: YOUR CODE
*
* If you need to visit a physical address, please use KADDR()
* please read pmm.h for useful macros
*
* Maybe you want help comment, BELOW comments can help you finish the code
*
* Some Useful MACROs and DEFINEs, you can use them in below implementation.
* MACROs or Functions:
* PDX(la) = the index of page directory entry of VIRTUAL ADDRESS la.
* KADDR(pa) : takes a physical address and returns the corresponding kernel virtual address.
* set_page_ref(page,1) : means the page be referenced by one time
* page2pa(page): get the physical address of memory which this (struct Page *) page manages
* struct Page * alloc_page() : allocation a page
* memset(void *s, char c, size_t n) : sets the first n bytes of the memory area pointed by s
* to the specified value c.
* DEFINEs:
* PTE_P 0x001 // page table/directory entry flags bit : Present
* PTE_W 0x002 // page table/directory entry flags bit : Writeable
* PTE_U 0x004 // page table/directory entry flags bit : User can access
*/
pde_t *pdep = NULL; // (1) find page directory entry
pdep = pgdir + PDX(la);
struct Page* pt = NULL;
pte_t *ptep = NULL; // virtual
if (((*pdep) & PTE_P) == 0) { // (2) check if entry is not present
if (create && (pt = alloc_page())) //allocate a page as page table, return pt that manages the mem
{
set_page_ref(pt, 1);
ptep = page2pa(pt); // ptep points to the mem of page table(physical addr)
memset(KADDR(ptep), 0, PGSIZE); // using va in program
*pdep = (uintptr_t)ptep | PTE_P | PTE_W | PTE_U; // using physical addr in pd/pt
}
else
return NULL;
}
else
{
ptep = PDE_ADDR(*pdep);
}
return (pte_t *)KADDR(ptep) + PTX(la);
}
static inline void
page_remove_pte(pde_t *pgdir, uintptr_t la, pte_t *ptep) {
pde_t *pdep = pgdir + PDX(la); //获得页目录项
if ((*pdep) & PTE_P == 0) //判断是否在内存
return;
ptep = (pte_t*)PDE_ADDR(*pdep) + PTX(la); //获得对应页表项物理地址(页目录保存物理地址)
ptep = KADDR(ptep); //转虚拟地址 程序中只能使用虚拟地址
if ((*ptep) & PTE_P == 0) //判断页是否在内存
return;
struct Page* page = pa2page(PTE_ADDR(*ptep)); //获得页的物理地址 通过pa2page获得struct Page的虚拟地址
page_ref_dec(page); //减引用数
if (page->ref == 0)
{
free_page(page); //释放页
*ptep = 0;
tlb_invalidate(pgdir, la);
}
}