Administrator
发布于 2022-02-09 / 9 阅读
0
0

OS_Lab2 内存开始 lab2 !

OS\_Lab2 内存

在 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. 完善函数 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

```

```hljs isbl

>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,所以我们自己实现一个):


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;
		}
}

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

先分配 页目录表


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

随后我们继续分配 页表

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


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

![](http://imt.rui.vin/202202172123327.png)

第二个函数page\_init()

初始化页表:

pages并没有内容

按要求来

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


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来管理


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 完善函数

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

```

1

2

3

4

5

```

```hljs c

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


评论