Lab2实验分享

一.TLB访存机制

当一个虚拟地址被送到MMU中进行翻译的时候,硬件首先在TLB中寻找包含这个地址的页面,如果它的虚页号在TLB中,并且没有违反保护位,那么就可以直接从TLB中得到相应的物理页号,而不去访问页表;如果发现虚页号在TLB中不存在,那么MMU将进行常规的页表查找,同时通过一定的策略来将这一页的页表项替换到TLB中,之后再次访问这一页的时候就可以直接在TLB中找到。

拿到了物理地址后,可以直接访问内存拿数据,不过会慢,所以,Cache就是部分物理地址到数据的映射。是内存的一部分copy。
在这里插入图片描述

二.二级页表

首先,c语言可以操作任何空间的地址,这个就是虚拟地址。

我们要做的就是模拟二级页表机制,往虚拟地址里面填写物理地址,再通过f(va)−>pa和f(pa)→va转换取到地址里面的东西。

简单来说,把放着很多很多页的虚拟地址,按照虚拟地址的索引,把它对应的物理地址放到另一个虚拟地址pgidr里。

这样就“假装”建立起了二级页表,物理地址从未使用过,要访问时永远要加上ULIM。

Page结构体其实是物理地址的一个“象征”。因为它减去pages拿到ppn以后,shift12位再加上ULIM,就是虚拟地址,减去ULM又变回物理地址。建立起来虚拟页和物理页之间的桥梁。尽管自己不是真正的4KB页,但是是桥梁。

三.Page存储结构

在这里插入图片描述

四.自映射机制图示

在这里插入图片描述

五.页面与地址的转化

在本次实验中涉及到许多的页面与地址的转化,其中用到许多已经定义的函数,现整理如下:

page2pa:得到某个page结构体的物理地址

/* Get the physical address of Page 'pp'.
 */
static inline u_long
page2pa(struct Page *pp)
{
	return page2ppn(pp) << PGSHIFT;
}

pa2page:得到某个物理地址所对应的Page结构体

/* Get the Page struct whose physical address is 'pa'.
 */
static inline struct Page *
pa2page(u_long pa)
{
	if (PPN(pa) >= npage) {
		panic("pa2page called with invalid pa: %x", pa);
	}

	return &pages[PPN(pa)];
}

page2kva:得到某个Page结构体的内核虚拟地址

/* Get the kernel virtual address of Page 'pp'.
 */
static inline u_long
page2kva(struct Page *pp)
{
	return KADDR(page2pa(pp));
}

PPN:得到某个虚拟地址的页号

#define PPN(va)		(((u_long)(va))>>12)

PADDR:将某个内核虚拟地址转化为物理地址

// translates from kernel virtual address to physical address.
#define PADDR(kva)						\
	({								\
		u_long a = (u_long) (kva);				\
		if (a < ULIM)					\
			panic("PADDR called with invalid kva %08lx", a);\
		a - ULIM;						\
	})

KADDR:将某个物理地址转化为内核虚拟地址

// translates from physical address to kernel virtual address.
#define KADDR(pa)						\
	({								\
		u_long ppn = PPN(pa);					\
		if (ppn >= npage)					\
			panic("KADDR called with invalid pa %08lx", (u_long)pa);\
		(pa) + ULIM;					\
	})

本次实验的前半部分涉及了许多对这类函数的应用,熟练掌握这类函数实现各类地址查询是本次实验的一大难点。

六.部分实验代码详解

部分代码含义解释如下:

 1 #define BY2PG        4096             // 页面大小的Byte数,一个页面大小为4kb
 2 #define PDMAP        (4*1024*1024)    // 一个页表管理1024个页面,大小总共4*1024*1024Byte
 3 #define PGSHIFT        12             // 页面的偏移位数,4kb对应12位
 4 #define PDSHIFT        22        // 页表的偏移位数,同上,即log2(PDMAP)
 5 #define PDX(va)        ((((u_long)(va))>>22) & 0x03FF) // 取虚拟地址高10位,为页目录号
 6 #define PTX(va)        ((((u_long)(va))>>12) & 0x03FF) // 取虚拟地址高11~20位,为页表号
 7 #define PTE_ADDR(pte)    ((u_long)(pte)&~0xFFF) // 页表项取低12位,为页内偏移
 8 
 9 // page number field of address
10 #define PPN(va)        (((u_long)(va))>>12) // 物理页号,为虚拟地址偏移12位
11 #define VPN(va)        PPN(va) // 虚页号
12 
13 #define VA2PFN(va)        (((u_long)(va)) & 0xFFFFF000 )
14 #define PTE2PT        1024
15 //$#define VA2PDE(va)        (((u_long)(va)) & 0xFFC00000 )
16 
17 /* Page Table/Directory Entry flags
18  *   these are defined by the hardware
19  */
20 #define PTE_G        0x0100    // 全局位
21 #define PTE_V        0x0200    // 有效位
22 #define PTE_R        0x0400    // 修改位,如果是0表示只对该页面进行过读操作,否则进行过写操作,要引发中断将内容写回内存
23 #define PTE_D        0x0002    // 文件缓存的修改位dirty
24 #define PTE_COW        0x0001    // 写时复制copy on write
25 #define PTE_UC        0x0800    // 未缓存uncached
26 #define PTE_LIBRARY        0x0004    // 共享内存

其余的一些定义(异常码的解释略去):

 1 #define KERNBASE 0x80010000 // 内核基地址
 2 
 3 #define VPT (ULIM + PDMAP ) //
 4 #define KSTACKTOP (VPT-0x100) // 内核栈顶
 5 #define KSTKSIZE (8*BY2PG) // 内核栈大小
 6 #define ULIM 0x80000000 // 用户态地址上限
 7 
 8 #define UVPT (ULIM - PDMAP) // 
 9 #define UPAGES (UVPT - PDMAP) // 用户页表
10 #define UENVS (UPAGES - PDMAP) // 用户进程控制块
11 
12 #define UTOP UENVS // 用户态高地址
13 #define UXSTACKTOP (UTOP) // 用户态异常栈
14 #define TIMESTACK 0x82000000 // 上下文保存栈
15 
16 #define USTACKTOP (UTOP - 2*BY2PG) // 用户栈
17 #define UTEXT 0x00400000 // 用户代码段

最后是一些函数与函数宏:

  • void bcopy(const void *, void *, size_t):内存拷贝
  • void bzero(void *, size_t):内存清空
  • assert(x):支持断言机制。
  • TRUP(_p):相当于min(_p, ULIM),似乎是为了防止用户读写内核段内存。

在进行内存初始化时,mips_detect_memory()mips_vm_init()page_init()被依次调用。mips_detect_memory()用来初始化一些全局变量(此处将物理内存大小设置为64MB,在实际中,内存大小是由硬件得到的,这里只是模拟了检测物理内存大小这个过程)。其余的函数的功能为:

  • static void *alloc(u_int n, u_int align, int clear):申请一块内存,返回首地址。
  • static Pte *boot_pgdir_walk(Pde *pgdir, u_long va, int create):从页目录项中找出虚拟地址va对应的页表项,若create置位,则不存在时创建。
  • void boot_map_segment(Pde *pgdir, u_long va, u_long size, u_long pa, int perm):将虚拟地址va映射到物理地址pa
  • void mips_vm_init():创建一个二级页表。
  • void page_init(void):将内存分页并初始化空闲页表。
  • int page_alloc(struct Page **pp):分配一页内存并把值赋给pp。
  • void page_free(struct Page *pp):释放一页内存。
  • int pgdir_walk(Pde *pgdir, u_long va, int create, Pte **ppte):建立起二级页表结构后从页目录中找到va对应页表项的函数。
  • int page_insert(Pde *pgdir, struct Page *pp, u_long va, u_int perm):将物理页pp映射到va。
  • struct Page * page_lookup(Pde *pgdir, u_long va, Pte **ppte):找到虚拟地址va对应的物理页面。
  • void page_decref(struct Page *pp):降低物理页面的引用次数,降到0后释放页面。
  • void page_remove(Pde *pgdir, u_long va):释放虚拟地址va对应的页面。
  • void tlb_invalidate(Pde *pgdir, u_long va):更新TLB。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值