实验完善代码 LAB2-4下载链接 提取码:79t8
1、页表管理
Exercise 1 中,我们分配了一个页表使得内核代码可以在其链接地址(0xf0100000)运行,代码被加载在物理地址(0x00100000)。一旦进入保护模式,不能直接用线性地址,所有的指针所指的地址都是虚拟地址。
代码中的 uintptr_t 表示的是虚拟地址;physaddr_t 表示的是物理地址。
T* Virtual address
uintptr_t Virtual address
physaddr_t Physical address
在了解页表管理的细节之前,需要了解三类地址:
- 逻辑地址(逻辑地址):指程序在编译连接后变量名字等符号地址,在JOS内核部分, 该地址是以KERNBASE(0xf0000000)对应 物理地址 0.
- 线性地址:指经过x86 保护模式的段地址变换后的地址,变换的过程是 逻辑地址 + 段首地址 。
- 物理地址:指内存存贮单元的编址。
未开启页式内存管理之前,线性地址就是物理地址;开启页式内存管理之后,线性地址需要经过页式转化得到物理地址。
- 线性地址的转换
当启用了x86 页式内存管理后,当处理器碰到一个线性地址后,它会将32 位的线性地址 分为3部分,分别是 页目录索引(10位)、页表索引(10位) 和 页内偏移(12位,因为一个页面4KB)。
页目录索引、页表索引的大小为4KB,实际上每个物理页面分成了1024个单元,每个单元占4个字节(32位),其中每个单元的格式如下所示
高20位存储的是一个地址,但是因为只有高20位(使用的时候地位会被清零),所以只能寻址4KB 对齐的地址空间(这就是为什么我们在为页目录分配空间时需要寻找内核代码后第一个4KB对齐的地址的原因),由于x86把所有的物理内存分为4KB大小的页,每个页的首地址必然是4KB对齐的,所以这个表项中的高20位可以定位到内存中的任何一个物理页面的首地址。如果这个表项在页目录表中,那么这个地址寻址的就是页目录的下级(页表),也就是页表所在物理页面的首地址;如果这个表项在页表中,这个地址寻址的就是真正的数据页的首地址。
0 位到12 位 是对应物理页面的标志位,这些位系统在访问一个页面的时候会自动判断权限是否满足其操作。
页目录所在的物理页面的首地址在启动x86的页式地址管理前,需要放在CR3中,这样x86 在进行页式转换时会自动从CR3 中取得页目录地址,从而找到当前的页目录。
通过线性地址的高10位,可以得到该线性地址在页目录中对应的表项(1KB)??? 通过该页目录项中的地址(页目录表项的高20位)可以得到页表所在物理页的首地址,从而找到页表;然后再通过线性地址的中间10位取得该地址在页表中的位置,从而得到页表项;最后从页表项中存储的地址(高20位)定位数据页,并将该数据页首地址加上线性地址中的页内偏移(后12位),从而得到物理地址。
- 页目录中的项 : CR0(页目录的首地址) + 线性地址的前10位
- 页表中的项 : (页目录)的高20位 + 线性地址的中间10位
- 物理地址 : (页表)的高20位 + 线性地址的低12位
逻辑地址 + 段首地址(GDTR) = 线性地址
上面所述的地址转换机制,可以对0~4GB 空间内的任何线性地址进行转换,页目录的每个页目录项都将指向一个包含1024(1KB) 个页表项的页面(4KB),整个页式转换机制将占用额外的1024(页面) + 1 (页表)= 1025 个 4KB 的物理页面。
在现代操作系统中,经常可以听到“用户进程的虚拟地址空间为 4 GB ”,原理是:为每个用户进程创建一个页目录,并将这个页目录保存到用户进程的上下文中,当用户进程被切换过来执行的时候,就将该用户进程的页目录地址写入 CR3 ,并重新启动一次页式内存管理。
对于页式地址管理,由于页目录以及页表都存放在物理内的页面中,要进行地址变换先要到内存中访问页目录和页表。由于CPU和内存速度的不匹配,为了提高地址翻译速度,提高系统的效率,x86系统中设计了用于地址翻译的缓存,称为TLB,即旁路转换缓冲(或页表缓冲),在该缓冲中存放了用于最近几次地址翻译的页表项,由于程序执行的局部性原理。
在启动分页前,由于系统仍然采用的是段式地址变换,线性地址就是物理地址,这是访问内存应该延续之前的做法,采用逻辑地址(从KERNBASE 0XF0000000) 来进行对内存的读写访问(段式地址变化: 物理地址 = 逻辑地址 + base ,会自动将0xF0000000 开始的地址转换成0x00000000 开始的实际物理地址);但是不要将逻辑地址写到页目录项和页表项,因为页式地址变换式地址变换的最后一步,通常是将逻辑地址通过段式地址变换成线性地址,然后再通过页式地址变换成物理地址,写入页目录和页表项的应该是实际的物理地址。
2、Exercise 4![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/a2925ee95a18a6449a454dfc89161e0a.png)
(1)完善代码前先分析下Exercise 4 所要求完成的几个函数的作用。
- pgdir_walk()
这个函数是检查虚拟地址va 是否能够用页表(页目录+ 页表)进行翻译,如果可以,则返回该地址所对应的页表项的地址;如果不能,则如果create= 0,则返回空;如果create = 1 , 则为该地址创建对应的页表(但是没有实际物理页面相对应,即使创建了,返回的页表项中的地址部分也是空的),并返回va 所对应的页表项的地址。 注意该函数返回的是 内核地址; - boot_map_region()
在也表中,将线性地址[la , la+size] 映射到物理地址 [pa, pa+size]。
注意: size 一定是PGSIZE(4KB) 的整数倍,这里有一个问题,是否有可能有一个物理页面,他已经被分配作为存储页表的页面了,但是由于在[pa, pa+size] 范围内,所以又被用来作为某线性地址对应的物理页面??? ***分析间连接【】*** - page_lookup()
在页式地址翻译机制中查找线性地址va 所对应的物理页面,如果找到了返回该物理页面,并将地址放入参数;如果找不到那么返回NULL; - page_remove()
删除线性地址va 所对应的物理页面;注意,在删除页面的时候调用的是page_decref(), 仅减低该页面的引用度,而不一定删除该页面。同时,由于页表发生了修改,删除操作完成后,应该调用tlb_invalidate() 更新页表; - page_insert()
这个函数是JOS 实现页面支持中最重要的一个函数,该函数的功能是将页面管理结构PP 所对应的物理页面分配给线性地址 va。 同时,将对应的页表项的permission 设置成 PTE_P & perm。
注意:一定要考虑到线性地址va 已经指向了另外一个物理页面或者干脆就是这个函数要指向的物理页面的情况。如果已经指向了另外一个页面,则需要先调用page_remove() 将该物理页面从线性地址 va 处删除,再将va 对应的页表项的地址赋值给PP 对应的物理页面。如果va 指向的本来就是参数PP 所对应的物理页面,则将va 对应的页表项中的物理地址重新赋值为PP 所对应的物理页面的首地址即可。==???why ==这里不明白,仔细看哦
(2