OS_Lab2 内存

Last updated on 8 months ago

在 jos中 BIOS程序先写好了,qemu已启动直接跳到了 0x7c00处 开始执行程序,在设模式下内存布局如图

preview

注意: 在 lab1中 作者已经帮我们手动映射了 4 MB的内存

所以0XF0100000开始的内存映射到了0x0100000的位置上,物理地址加上0XF0000000就是虚拟地址

物理地址直接通过MMU模块转换成了虚拟地址!

到目前为止,内核已经写进了了内存,

​ 如图

image-20220217161035639

将一个已经映射好的页表的地址放在指定的地方,开业分页模式后,cpu会自动查找到这个并且通过MMU模块转换层虚拟地址


开始 lab2 !

本章主要内容 是内存管理 虚拟内存 -> 映射到物理内存

目前的内存布局如下

image-20220217203020565

在写入完内核后,我们知道j接下来的任务建立页表, 我们要从哪里开始建立页表呢?很明显是在内核之后,而我们不知道内核的具体到小,而内核的大小是不固定的,在那个地址结束呢?在 kernel文件生成时,BSS段我们设置一个变量 end,作为内核结束的标志,所以我们可以在其他文件extend 引用这个变量,就可以知道 内核结束的位置在哪里!

image-20220217195848698


Exercise 1. 完善函数

In the file kern/pmap.c, you must implement code for the following functions (probably in the order given).

1
2
3
4
5
>boot_alloc()
>mem_init() (only up to the call to check_page_free_list(1)`)
>page_init()
>page_alloc()
>page_free()

check_page_free_list() and check_page_alloc() test your physical page allocator. You should boot JOS and see whether check_page_alloc() reports success. Fix your code so that it passes. You may find it helpful to add your own assert()s to verify that your assumptions are correct.

第一个函数: boot_alloc()
最基本的分配内存方式(目前没有malloc,所以我们自己实现一个):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
boot_alloc(uint32_t n)
{
static char *nextfree; // virtual address of next byte of free memory
char *result;
if (!nextfree) {
extern char end[];
nextfree = ROUNDUP((char *)end, PGSIZE);
}
cprintf("boot_alloc memory at %x\n", nextfree);
cprintf("Next memory at %x\n", ROUNDUP((char *)(nextfree + n), PGSIZE));
if (n != 0) {
char *next = nextfree;
nextfree = ROUNDUP((char *)(nextfree + n), PGSIZE);
return next;
} else{
return nextfree;
}
}

有了基本的内存分配方式就可以随心分配内存了

先分配页目录表

1
2
kern_pgdir = (pde_t *)boot_alloc(PGSIZE);
memset(kern_pgdir, 0, PGSIZE);

随后我们继续分配页表

每个页表项都是个结构体,这个结构体用途是管理内存,这里的每个结构体都管理这个一个4k大小的内存!!

1
2
3

pages = (struct PageInfo*)boot_alloc(sizeof (struct PageInfo)*npages); //调用boot_alloc()函数找到空闲空间起点,并且大小n= PageInfo结构大小*npages
memset(pages,0,sizeof (struct PageInfo)*npages);

第二个函数page_init()

初始化页表:

pages并没有内容
按要求来

第一个物理页标记为被使用,有几个区间是不可以用的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void
page_init(void)
{
size_t i;
size_t io_hole_start_page = (size_t)IOPHYSMEM / PGSIZE;
size_t kernel_end_page = PADDR(boot_alloc(0)) / PGSIZE; //注意boot_alloc()返回虚拟地址
for (i = 0; i < npages; i++) {
if(i==0) //如上面要求1
{
pages[i].pp_ref = 1;
pages[i].pp_link = NULL;
}
else if(i >= io_hole_start_page && i <= kernel_end_page) //这区域内是已经被使用的了
{
pages[i].pp_ref = 1;
pages[i].pp_link = NULL;
}
else //而这段 才是真正 我么可以操控的内存
{
pages[i].pp_ref = 0;
pages[i].pp_link = page_free_list; // 头插法形成一个链表
page_free_list = &pages[i];
}
}
}

第三个函数 page_alloc()

page_init 生成了页表,但是页表项并没有管理空间, page_alloc 函数可以申请一个空间,并用一个PageInfo来管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct PageInfo *
page_alloc(int alloc_flags)
{
// Fill this function in /// page_free_list是指向页表项的结构体指针 (链表)
if (page_free_list == NULL) {
cprintf("out of free memory");
return NULL;
}
struct PageInfo *addr = page_free_list;
page_free_list = page_free_list->pp_link; //需要使用一个页表项 ,防止丢失,指向下一个页表项
addr->pp_link = NULL;
if (alloc_flags & ALLOC_ZERO) { //看 传入的参数, 如果为 1 ,则申请一个 4k的内存
memset(page2kva(addr), 0, PGSIZE); //
}
return addr;
}

这里说下 page2kva(addr) 这个函数;
memset扩充内存,操作的虚拟是虚拟内存 (实际物理地址加 0xf00000)
现在 的addr 只是这个页表项的地址,如果直接按这个地址扩展的话肯定是会覆盖其他页表项的,所以呢,我们的根据这个页表项设计出在那分配内存

page2pa : pages是页表项的初始地址 pp-pages 可以得出 是第几个页表项 ; 而 左移则是 乘以4096 ,那么这个结果是 物理页的偏移量,我们可以算出在哪里开始分配内存

KADDR : 刚才得出的是物理内存,我们还得把这个转换成虚拟地址 ,这个宏就可以将物理地址转换成虚拟地址

image-20220218222302556


Exercise 4 完善函数

Exercise 4. In the file kern/pmap.c, you must implement code for the following functions.

1
2
3
4
5
pgdir_walk()
boot_map_region()
page_lookup()
page_remove()
page_insert()

check_page(), called from mem_init(), tests your page table management routines. You should make sure it reports success before proceeding.

​ pgdir_walk(pde_t *pgdir, const void *va, int create)
​ 给一个虚拟地址,页目录表入口,返回该虚拟地址所在的页表项

image-20220220154819450

大致看完 lab2 ,先回答几个问题

在 lab1 完成后,此时 OS的布局是怎么的?

解释下 虚拟内存 ,物理内存, 两者是如何映射的?

kernel 存放在哪个位值? (分派内存是以防覆盖) (提示 : bss)

内存权限问题

开启分页模式后,物理地址 默认都会转换为 虚拟地址( cr3 寄存器 可以设置 )

image-20220217164716439

image-20220217164746773

img

image-20220209223329957

image-20220211105409966

image-20220211113225003

img