Lab 2:内存管理实验报告
一、实验目的
本实验的主要目的是实现操作系统存储管理的细节。包括建立内存模型,页表,物理地址映射等,理 解页表的分配机制,熟练的使用gdb进行程序的调试,可以灵活区分虚拟地址和物理地址之间的转换。
二、实验内容
练习1
boot_alloc
static void *
boot_alloc(uint32_t n)
{
static char *nextfree; // 下一个空闲内存字节的虚拟地址
char *result;
// 如果这是第一次调用,初始化 nextfree。
// 'end' 是链接器自动生成的一个魔术符号,
// 指向内核 bss 段的末尾:
// 链接器未分配给任何内核代码或全局变量的第一个虚拟地址。
if (!nextfree)
{
extern char end[];
nextfree = ROUNDUP((char *)end, PGSIZE);
}
// 分配足够容纳 'n' 字节的内存块,然后更新
// nextfree。确保 nextfree 保持对齐
// 到 PGSIZE 的倍数。
//
// LAB 2: 你的代码在这里。
if (n > 0)
{
uint32_t alloc_size = ROUNDUP(n, PGSIZE);
result = nextfree;
nextfree += alloc_size;
return result;
}
else
{
return nextfree;
}
}
mem_init
void mem_init(void)
{
uint32_t cr0;
size_t n;
// Find out how much memory the machine has (npages & npages_basemem).
i386_detect_memory();
// Remove this line when you're ready to test this function.
// panic("mem_init: This function is not finished\n");
//////////////////////////////////////////////////////////////////////
// create initial page directory.
kern_pgdir = (pde_t *)boot_alloc(PGSIZE);
memset(kern_pgdir, 0, PGSIZE);
//////////////////////////////////////////////////////////////////////
// Recursively insert PD in itself as a page table, to form
// a virtual page table at virtual address UVPT.
// (For now, you don't have understand the greater purpose of the
// following line.)
// Permissions: kernel R, user R
kern_pgdir[PDX(UVPT)] = PADDR(kern_pgdir) | PTE_U | PTE_P;
//////////////////////////////////////////////////////////////////////
// Allocate an array of npages 'struct PageInfo's and store it in 'pages'.
// The kernel uses this array to keep track of physical pages: for
// each physical page, there is a corresponding struct PageInfo in this
// array. 'npages' is the number of physical pages in memory. Use memset
// to initialize all fields of each struct PageInfo to 0.
// Your code goes here:
pages = (struct PageInfo *)boot_alloc(npages * sizeof(struct PageInfo));
memset(pages, 0, npages * sizeof(struct PageInfo));
page_init
void page_init(void)
{
uintptr_t first_free_page = PADDR(boot_alloc(0)) / PGSIZE;
size_t i;
// 1) 物理页 0
pages[0].pp_ref = 1;
pages[0].pp_link = NULL;
// 2) 基础内存 [PGSIZE, npages_basemem * PGSIZE)
for (i = 1; i < npages_basemem; i++)
{
pages[i].pp_ref = 0;
pages[i].pp_link = page_free_list;
page_free_list = &pages[i];
}
// 3) IO 空洞 [IOPHYSMEM, EXTPHYSMEM)
for (i = IOPHYSMEM / PGSIZE; i < EXTPHYSMEM / PGSIZE; i++)
{
pages[i].pp_ref = 1;
pages[i].pp_link = NULL;
}
// 4) 扩展内存 [EXTPHYSMEM, ...)
for (i = EXTPHYSMEM / PGSIZE; i < npages; i++)
{
if (i >= first_free_page)
{
pages[i].pp_ref = 0;
pages[i].pp_link = page_free_list;
page_free_list = &pages[i];
}
else
{
pages[i].pp_ref = 1;
pages[i].pp_link = NULL;
}
}
}
page_alloc
struct PageInfo *
page_alloc(int alloc_flags)
{
if (!page_free_list)
{
return NULL;
}
struct PageInfo *page = page_free_list;
page_free_list = page_free_list->pp_link;
if (alloc_flags & ALLOC_ZERO)
{
memset(page2kva(page), 0, PGSIZE);
}
page->pp_link = NULL; // 确保已分配页面的 pp_link 字段设置为 NULL
return page;
}
page_free
void page_free(struct PageInfo *pp)
{
// 填写这个函数
// 提示:如果 pp->pp_ref 不为零或 pp->pp_link 不为 NULL,你可能需要触发 panic。
if (pp->pp_ref == 0)
{
panic("ref is not NULL");
return;
}
if (pp->pp_link == NULL)
{
panic("link is not NULL");
return;
}
pp->pp_link = page_free_list;
page_free_list = pp;
}
练习2
其实这张图以及写的很明显了,x86和arm的区别之一就是段这个概念,这里与老师在书上讲的概念的区别就是这里还需要讲地址转化成线性地址,再转化成物理地址。
逻辑地址就是段选择器加上偏移量segment_selector:offset
,段选择器包含段的索引、特权级别和表指示符(GDT 或 LDT)。
线性地址 = 段基址(base address) + 段内偏移量(offset)
假设有一个逻辑地址 0x2B:0x1234
,其中 0x2B
是段选择器,0x1234
是段内偏移量。我们如何去计算它的线性地址呢?
首先使用段选择器 0x2B
在 GDT 或 LDT 中查找段描述符,假设找到的段描述符基址为 0x40000000
。
然后就可以计算出线性地址 = 段基址 0x40000000
+ 段内偏移量 0x1234
= 0x40001234
然后的物理地址的转化老师都讲过了,就不在赘述了。
练习3
我们随便找一个区域,如内存为0的区域做示范,使用命令看看
可以看到,左右两边的数值是完全一样的。
info pg
info mem
问题1
c语言的指针都是虚拟地址。
练习4
pgdir_walk
pte_t *
pgdir_walk(pde_t *pgdir, const void *va, int create)
{
pde_t *pde = &pgdir[PDX(va)];
if (!(*pde & PTE_P))
{
if (!create)
return NULL;
struct PageInfo *page = page_alloc(1);
if (!page)
return NULL;
page->pp_ref++;
*pde = page2pa(page) | PTE_P | PTE_W | PTE_U;
}
pte_t *pt = (pte_t *)KADDR(PTE_ADDR(*pde));
return &pt[PTX(va)];
}
关于权限位,暂时不知道为何是这三个。
boot_map_region
static void
boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm)
{
size_t pieces = ROUNDUP(size, PGSIZE) / PGSIZE;
while (pieces > 0)
{
pte_t *pte = pgdir_walk(pgdir, (void *)va, 1);
if (pte == NULL)
panic("boot_map_region: out of memory!\n");
*pte = pa | perm | PTE_P;
pieces -= 1;
va += PGSIZE, pa += PGSIZE;
}
}
page_insert
int page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm)
{
pte_t *pte = pgdir_walk(pgdir, va, 1);
if (pte == NULL)
return -E_NO_MEM; // 分配失败,返回错误码
physaddr_t pa = page2pa(pp);
pp->pp_ref++; // 更新引用次数
if (*pte & PTE_P)
{
page_remove(pgdir, va);
tlb_invalidate(pgdir, va);
}
// 如果有映射,删除映射
*pte = pa | perm | PTE_P; // 设置权限,更新页表项
return 0;
}
page_lookup
struct PageInfo *
page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
{
pte_t *pte = pgdir_walk(pgdir, va, 0);
if (pte == NULL)
return NULL;
physaddr_t pa = PTE_ADDR(*pte);
if (pte_store)
*pte_store = pte;
return pa2page(pa);
}
page_remove
void page_remove(pde_t *pgdir, void *va)
{
pte_t *pte_store;
struct PageInfo *pp = page_lookup(pgdir, va, &pte_store);
if (pp == NULL)
return;
*pte_store = 0;
page_decref(pp);
tlb_invalidate(pgdir, va);
}
发表评论