Introduction
在本实验中,您将为操作系统编写内存管理代码。内存管理有两个组成部分。
第一个组件是内核的物理内存分配器, 以便内核可以分配内存并随后释放它。 你的分配器将以 4096 字节为单位进行操作,称为 页面。您的任务是维护数据结构,记录哪些物理页是空闲的、哪些是已分配的,以及有多少进程正在共享每个分配的页。您还将编写分配和释放内存页的例程。
内存管理的第二个组成部分是虚拟内存,它将内核和用户软件使用的虚拟地址映射到物理内存中的地址。 x86 硬件的内存管理单元 (MMU) 在指令使用内存时执行映射,并查阅一组页表。您将修改 JOS 以根据我们提供的规范设置 MMU 的页表。
Getting started
在本实验和未来的实验中,您将逐步构建您的内核。我们还将为您提供一些额外的来源。要获取该源,请使用
Git 提交自提交实验 1
以来所做的更改(如果有),获取课程存储库的最新版本,然后创建一个名为的本地分支
lab2
基于我们的 lab2 分支, origin/lab2
:
1 | athena% cd ~/6.828/lab |
这里我们因为是推送到自己仓库的,所以指令要修改一下
1 | git remote add jos https://pdos.csail.mit.edu/6.828/2018/jos.git |
这 git checkout -b
上面显示的命令实际上做了两件事:它首先创建一个本地分支 lab2
这是基于 origin/lab2
分支由课程工作人员提供,其次,它改变了你的内容 lab
目录来反映存储在的文件 lab2
分支。 Git
允许使用以下方式在现有分支之间切换 git checkout branch-name
,尽管您应该在切换到另一个分支之前在一个分支上提交所有未完成的更改。
在某些情况下,Git 可能无法弄清楚如何将您的更改与新的实验作业合并(例如,如果您修改了第二个实验作业中更改的某些代码)。在这种情况下, git merge 命令会告诉您哪些文件有冲突,您应该首先解决冲突(通过编辑相关文件),然后使用以下命令提交生成的文件 git commit -a 。
实验 2 包含以下新源文件,您应该浏览这些文件:
inc/memlayout.h
kern/pmap.c
kern/pmap.h
kern/kclock.h
kern/kclock.c
memlayout.h
描述了必须通过修改来实现的虚拟地址空间的布局
pmap.c
。 memlayout.h
和 pmap.h
定义PageInfo
您将使用它来跟踪哪些页面的结构
物理内存是空闲的。 kclock.c
和 kclock.h
操纵
PC 的电池供电时钟和 CMOS RAM 硬件, 其中 BIOS 记录 PC 包含的物理内存量,
除其他事项外。 代码在 pmap.c
需要读取该设备硬件才能弄清楚有多少物理内存,但这部分代码已经为您完成:您不需要了解
CMOS 硬件如何工作的详细信息。
特别注意 memlayout.h
和 pmap.h
,因为本实验要求您使用并理解它们包含的许多定义。您可能想查看
inc/mmu.h
,因为它还包含许多对本实验有用的定义。
在此实验和后续实验中,完成实验中描述的所有常规练习以及至少一个挑战问题。
(当然,有些挑战问题比其他问题更具挑战性!)此外,写下实验中提出的问题的简短答案,以及您为解决所选挑战问题所做的工作的简短描述(例如,一两段)。如果您实现了多个挑战问题,您只需在文章中描述其中一个问题,当然我们也欢迎您做更多的事情。将写入的内容放在名为的文件中
answers-lab2.txt
在你的顶层 lab
交作业前的目录。
Part 1: Physical Page Management
操作系统必须跟踪物理 RAM 的哪些部分是空闲的以及哪些部分当前正在使用。 JOS以页粒度管理PC的物理内存,以便可以使用MMU来映射和保护每块分配的内存。
您现在将编写物理页分配器。它通过struct PageInfo
对象的链接列表(与
xv6
不同,它们不嵌入空闲页面本身)来跟踪哪些页面是空闲的,每个对象对应于一个物理页面。您需要先编写物理页分配器,然后才能编写其余的虚拟内存实现,因为页表管理代码需要分配物理内存来存储页表。
Exercise 1. In the file
kern/pmap.c
, you must implement code for the following functions (probably in the order given).boot_alloc()
mem_init()
(only up to the call tocheck_page_free_list(1)
)page_init()
page_alloc()
page_free()
check_page_free_list()
andcheck_page_alloc()
test your physical page allocator. You should boot JOS and see whethercheck_page_alloc()
reports success. Fix your code so that it passes. You may find it helpful to add your ownassert()
s to verify that your assumptions are correct.
本实验以及所有 6.828 实验将要求您做一些侦探工作,以准确弄清楚您需要做什么。此作业并未描述您必须添加到 JOS 的代码的所有详细信息。在 JOS 源代码中查找您必须修改的部分的注释;这些评论通常包含规范和提示。您还需要查看 JOS 的相关部分、Intel 手册,或许还需要查看 6.004 或 6.033 注释。
Part 2: Virtual Memory
在做任何其他事情之前,先熟悉 x86 的保护模式内存管理架构:即分段和页面转换。
Exercise 2. Look at chapters 5 and 6 of the Intel 80386 Reference Manual, if you haven't done so already. Read the sections about page translation and page-based protection closely (5.2 and 6.4). We recommend that you also skim the sections about segmentation; while JOS uses the paging hardware for virtual memory and protection, segment translation and segment-based protection cannot be disabled on the x86, so you will need a basic understanding of it.
Virtual, Linear, and Physical Addresses
在 x86 术语中,虚拟地址由段选择器和段内的偏移量组成。线性地址是在段转换之后但在页转换之前获得的地址。物理地址是经过段和页转换后最终获得的地址,以及最终通过硬件总线发送到 RAM 的地址。
1 |
|
AC指针是虚拟地址的“偏移”部分。在 boot/boot.S
,我们安装了一个全局描述符表(GDT),它通过将所有段基地址设置为 0
并限制为0xffffffff
来有效禁用段转换。因此,“选择器”不起作用,线性地址始终等于虚拟地址的偏移量。在实验
3
中,我们必须与分段进行更多交互以设置权限级别,但对于内存翻译,我们可以在整个
JOS 实验室中忽略分段,而只关注页面翻译。
回想一下,在实验 1 的第 3 部分中,我们安装了一个简单的页表,以便内核可以在其链接地址 0xf0100000 处运行,即使它实际上加载到 ROM BIOS 上方的物理内存中的 0x00100000 处。这个页表只映射了4MB的内存。在本实验中您将为 JOS 设置的虚拟地址空间布局中,我们将对其进行扩展以映射从虚拟地址 0xf0000000 开始的前 256MB 物理内存,并映射虚拟地址空间的许多其他区域。
Exercise 3. While GDB can only access QEMU's memory by virtual address, it's often useful to be able to inspect physical memory while setting up virtual memory. Review the QEMU monitor commands from the lab tools guide, especially the
xp
command, which lets you inspect physical memory. To access the QEMU monitor, press Ctrl-a c in the terminal (the same binding returns to the serial console).Use the xp command in the QEMU monitor and the x command in GDB to inspect memory at corresponding physical and virtual addresses and make sure you see the same data.
Our patched version of QEMU provides an info pg command that may also prove useful: it shows a compact but detailed representation of the current page tables, including all mapped memory ranges, permissions, and flags. Stock QEMU also provides an info mem command that shows an overview of which ranges of virtual addresses are mapped and with what permissions.
从 CPU 上执行的代码开始,一旦我们处于保护模式(我们首先进入
boot/boot.S
),没有办法直接使用线性或物理地址。所有内存引用都被解释为虚拟地址并由
MMU 翻译,这意味着 C 中的所有指针都是虚拟地址。
JOS
内核通常需要将地址作为不透明值或整数进行操作,而不取消对它们的引用,例如在物理内存分配器中。有时这些是虚拟地址,有时它们是物理地址。为了帮助记录代码,JOS
源代码区分了两种情况:类型uintptr_t
表示不透明虚拟地址,
physaddr_t
表示物理地址。这两种类型实际上只是 32 位整数 (
uint32_t
)
的同义词,因此编译器不会阻止您将一种类型分配给另一种类型!由于它们是整数类型(而不是指针),因此如果您尝试取消引用它们,编译器会抱怨。
JOS
内核可以通过首先将uintptr_t
转换为指针类型来取消引用它。相反,内核无法明智地取消引用物理地址,因为
MMU
会转换所有内存引用。如果将physaddr_t
转换为指针并取消引用它,您可能能够加载并存储到结果地址(硬件会将其解释为虚拟地址),但您可能无法获得您想要的内存位置。