mit6.828 - lab2笔记

news/2024/10/5 19:09:43

目标:重点学习内存管理的相关知识,包括内存布局、页表结构、页映射
任务:完成内存管理的相关代码
lab2中,完全可以跟着实验手册的节奏走,逐步完善内存管理的代码。

环境准备:

image.png

实验 2 包含以下新的源文件:

  • inc/memlayout.h
  • kern/pmap.c
  • kern/pmap.h
  • kern/kclock.h
  • kern/kclock.c

memlayout.h pmap.h 定义了 PageInfo 结构,用于跟踪哪些物理内存页是空闲的。
kclock.ckclock.h 操作 PC 的电池时钟和 CMOS RAM 硬件,其中 BIOS 记录了 PC 所含的物理内存量等信息。
pmap.c 中的代码需要读取这些设备硬件,以计算出物理内存的容量,但这部分代码已经为你完成:你不需要了解 CMOS 硬件工作的细节。
请特别注意 memlayout.h pmap.h,因为本实验要求您使用并理解其中包含的许多定义。您可能还需要查看 inc/mmu.h,因为其中包含的许多定义对本实验也很有用。


Part1:物存管理(练习1)

操作系统必须跟踪物理内存中哪些是空闲内存,哪些是当前正在使用的内存。JOS 以页面粒度管理 PC 的物理内存,这样它就可以使用 MMU 来映射和保护每一块已分配的内存。

现在你将编写物理页面分配器。它通过 struct PageInfo 对象的链表来跟踪哪些页面是空闲的,每个对象对应一个物理页面。我们要做的就是通过 PageInfo链表,实现对物理内存的申请、释放。

练习 1. 在 kern/pmap.c 文件中,您必须实现以下函数的代码(可能按照给出的顺序)。
boot_alloc()
mem_init()(只调用到 check_page_free_list(1))。
page_init()
page_alloc()
page_free()
check_page_free_list() 和 check_page_alloc() 对物理页面分配器进行测试。你应该启动 JOS 并查看 check_page_alloc() 是否报告成功。修改代码,使其通过测试。你可能会发现添加自己的 assert()s 来验证你的假设是否正确很有帮助。

在这里我们可以先浏览一遍 mem_init

mem_init

mem_init 是用来初始化内存管理的函数。物存管理的部分在前面被处理,大致工作流程为:

  1. 获取硬件信息,内存有多大,i386_detect_memory();
  2. 初始化页目录 kern_pgdir
  3. 初始化pages数组

// Set up a two-level page table:
//    kern_pgdir is its linear (virtual) address of the root
//
// This function only sets up the kernel part of the address space
// (ie. addresses >= UTOP).  The user part of the address space
// will be set up later.
//
// From UTOP to ULIM, the user is allowed to read but not write.
// Above ULIM the user cannot read or write.
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 Rkern_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(sizeof(struct PageInfo)*npages);memset(pages, 0, sizeof(struct PageInfo) * npages);//////////////////////////////////////////////////////////////////////// Now that we've allocated the initial kernel data structures, we set// up the list of free physical pages. Once we've done so, all further// memory management will go through the page_* functions. In// particular, we can now map memory using boot_map_region// or page_insertpage_init();check_page_free_list(1);check_page_alloc();check_page();//....
}

boot_alloc

只在初始化时使用,确定申请n子节内存后后,空闲内存的首地址(虚拟内存空间)是多少

static void *
boot_alloc(uint32_t n)
{static char *nextfree;	// virtual address of next byte of free memorychar *result;if (!nextfree) {extern char end[];nextfree = ROUNDUP((char *) end, PGSIZE);}// Allocate a chunk large enough to hold 'n' bytes, then update// nextfree.  Make sure nextfree is kept aligned// to a multiple of PGSIZE.result = nextfree;nextfree = ROUNDUP((char *)result + n, PGSIZE);return result;
}

struct PageInfo

先来看看 PageInfo 这个结构体,这个注释真棒。

/** 页面描述符结构,映射到 UPAGES。* 内核可读写,用户程序只读。** 每个结构 PageInfo 保存一个物理页面的元数据。*  它不是物理页面本身,但物理页面和结构 PageInfo 之间有一一对应的关系。* 您可以使用 kern/pmap.h 中的 page2pa() 将结构 PageInfo * 映射到相应的物理地址。*/
struct PageInfo {//空闲列表中的下一页。struct PageInfo *pp_link;// pp_ref 是指向此页的指针(通常是页表条目)的计数。// 对于使用 page_alloc 分配的页面,pp_ref 是指向该页面的指针计数(通常在页表项中)。// 在启动时使用 pmap.c 的boot_alloc 分配的页面没有有效的引用计数字段。uint16_t pp_ref;
}

如注释所述, PageInfo 和物理内存是一一对应的,一个PageInfo 对应一页物理内存(4KB),可以从 page2pa 这个映射函数中看出来

image.png

对于一个物理地址 pa ,将其右移 12 位,然后就可以作为 pages 数组的下标了。
也就是说 :
pages[0] 对应 pa 0x0000_0000 到 0x0000_1000
pages[1] 对应 pa 0x0000_1000 到 0x0000_2000

pmap.h 中还有很多好用的函数和宏,除了这个 pa2page 还有 PADDR、KADDR等,可以先看一看,理解下。

理解了 struct PageInfo 的结构和映射方法,可以来看 page_init 了。

page_init

page_init 初始化了 pages 数组,注释给的相当详尽了。这里直接给出答案,可以参照 lab1笔记 中的内存布局和 memlayout.h 中关于 IOPHYSMEM、EXTPHYSMEM 的定义写

//  初始化页面结构和内存空闲列表。
// 完成后,永远不要再使用 boot_alloc。 只使用下面的页面分配器函数来分配和取消分配物理内存。
// 通过 page_free_list 分配和删除物理内存。
//
void
page_init(void)
{// 这里的示例代码将所有物理页面标记为空闲。// 但实际情况并非如此。 哪些内存是空闲的?//  1) 将物理页 0 标记为使用中。//     这样,我们就可以保留实际模式 IDT 和 BIOS 结构,以备不时之需。 (目前还不需要,但是......)。//     //  2) 其余的基本内存 [PGSIZE, npages_basemem * PGSIZE)是空闲的。//     //  3) 然后是 IO 孔 [IOPHYSMEM, EXTPHYSMEM),它必须永远不会被分配。//     //  4) 然后是扩展内存 [EXTPHYSMEM, ...)  其中一些在使用中,一些是空闲的。//     内核在物理内存的哪里?哪些物理页已经用于页表和其他数据结构?//     //     //// 修改代码以反映这一点。// 注意:切勿实际触及与空闲页面对应的物理内存!// size_t i;//物理页 0 标记为使用中pages[0].pp_ref = 1;for(int i = 1; i<PGNUM(IOPHYSMEM); i++){pages[i].pp_ref = 0;pages[i].pp_link = page_free_list;page_free_list = &pages[i];}//然后是 IO 孔 [IOPHYSMEM, EXTPHYSMEM),它必须永远不会被分配。for(int i = PGNUM(IOPHYSMEM); i<PGNUM(EXTPHYSMEM); i++){pages[i].pp_ref = 1;}//获取当前空闲的内存首地址 cur_free_paddr(物理内存)physaddr_t cur_free_paddr = PADDR(boot_alloc(0));//[EXTPHYSMEM, cur_free_paddr) 中的扩展内存在使用中for(int i = PGNUM(EXTPHYSMEM); i<PGNUM(cur_free_paddr); i++){pages[i].pp_ref = 1;}//[cur_free_paddr, ...] 之后的物理内存目前是空闲的for(int i = PGNUM(cur_free_paddr); i<npages; i++){pages[i].pp_ref = 0;pages[i].pp_link = page_free_list;page_free_list = &pages[i];}
}

如图,深蓝色部分应当被标记为已经占用

image.png

page_alloc

从pageinfo 空闲链表中摘下一个,并返回,细节见注释:

//
// 分配一个物理页面。 如果(alloc_flags & ALLOC_ZERO),则用“\0 ”字节填充返回的整个物理页
// 不要增加页的引用,page_alloc 的调用者负责增加页面的引用(显式地或通过 page_insert)。//
// 务必将已分配页面的 pp_link 字段设置为 NULL,以便page_free 可以检查是否存在双重引用。
// 
//
// 如果没有可用内存,则返回 NULL。
//
// Hint: use page2kva and memset
struct PageInfo *
page_alloc(int alloc_flags)
{// Fill this function inif(page_free_list == NULL){return NULL;}//取出一个空闲 pageinfostruct PageInfo * pp = page_free_list;page_free_list = page_free_list->pp_link;pp->pp_link = NULL;//置零if(alloc_flags & ALLOC_ZERO){void* pp_kv = page2kva(pp);memset(pp_kv, 0, PGSIZE);}return pp;
}

page_free

//
// 返回一个页面到空闲列表。
// (只有当 pp->pp_ref 达到 0 时才调用此函数)。
//
void
page_free(struct PageInfo *pp)
{//  填充此函数// Hint: You may want to panic if pp->pp_ref is nonzero or// pp->pp_link is not NULL.if(pp->pp_ref != 0 || pp->pp_link != NULL){panic("page_free: pp->pp_ref is nonzero or pp->pp_link is not NULL.");}pp->pp_link = page_free_list;page_free_list = pp;
}

到此为止,练习1就算完成了,然后测试一下

image.png

没毛病

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/28657.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

Linux系统中的“文件夹”为何称之为目录

什么是文件夹 文件夹是一种用来组织和管理磁盘文件的数据结构 文件存储也称为文件级存储或基于文件的存储,数据会以单条信息的形式存储在文件夹中。当需要访问该数据时,计算机需要知道相应的查找路径。存储在文件中的数据会根据元数据来进行整理和检索,这些元数据会告诉计算…

Testing Egineer note:2024_5_7-day06-part02

测试技术与测试设计 黑盒设计测试用例方法等价类,边界值,判定表,因果图,正交表,场景法,状态迁移法错误推测法,异常分析法,随机测试白盒测试设计用例方法语句覆盖判断覆盖条件覆盖判断条件覆盖路径覆盖(独立路径覆盖,z路径)一、设计测试用例方法之等价类 等价类:定义…

Homework6

1、Quora精选:为什么软件开发周期总是预估的2~3倍?https://www.sohu.com/a/132411358_355123 这篇文章通过一个徒步旅行的比喻,解释了为什么软件开发周期通常会比预估的长2到3倍。文章中提到,开发过程中经常会出现意想不到的挑战和困难,比如需求变更、技术问题、资源限制等…

Tomcat简介

也就是说,将来我们在服务器端要安装一个Web服务器的软件,然后我们把我们自己写的Web项目放到服务器软件里边。服务器软件一启动起来,我们写的Web页面就可以被浏览器访问到

13.网络编程

1.IP 地址 IP地址:InetAddress import java.net.InetAddress; import java.net.UnknownHostException;//测试IP public class TestInetAddress {public static void main(String[] args) {try {//获取本机地址InetAddress inetAddress1 = InetAddress.getByName("127.0.0…

NTFS、exFAT、FAT32、Ext4文件系统的区别

本文从最大单文件、最大管理空间、最大文件名长度、主要平台的角度介绍了NTFS、exFAT、FAT32、Ext4文件系统的区别。V1.0 2024年5月7日 发布于博客园NTFS、exFAT、FAT32、Ext4文件系统的区别 FAT(File Allocation Table) FAT(File Allocation Table,文件分配表)是1977年微…

Linux系统启动过程

Linux系统启动过程(电影内挂)

mysql练习 —— 关于一些函数的使用

学校作业,拿来记录一下。(老师出的习题貌似是从一本书上拿到的)Q1:使用RAND()函数来获得3个随机值。A1:   解析:rand()函数,获得随机值,产生0-1的随机值。(random ,随机)Q2:求3和4的平方根。A2:   解析:sqrt()用于求解平方根。Q3:求7.2和-7.2的绝对值。A3: …