Lab2 PART1 Physical Page Management

引言

在本实验中,将为操作系统编写内存管理代码。

内存管理有两个组件。 第一个组件是内核的物理内存分配器,这样内核就可以分配内存并在以后释放它。分配器将以4096字节为单位运行,称为页面。任务是维护数据结构,记录哪些物理页面是空闲的,哪些是已分配的,以及有多少进程共享每个已分配的页面。还将编写分配和释放内存页面的例程。

内存管理的第二个组成部分是虚拟内存,它将内核和用户软件使用的虚拟地址映射到物理内存中的地址。当指令使用内存时,x86硬件的内存管理单元(MMU)会参考一组页表来执行映射。在这一part种将根据提供的规范修改JOS来设置MMU的页表。

Exercise 1

在文件kern/pmap.c中,需要实现以下几个函数:

boot_alloc()
mem_init()
page_init()
page_alloc()
page_free()

check_page_free_list()和check_page_alloc()两个函数将会检测你写的页分配器代码是否正确。

首先打开kern/init.c文件,观察启动内核的函数i386_init函数,可以看到mem_init函数是在内核启动时进行初始化内存的函数。因此,在这个练习中我们从men_init函数入手。

阅读mem_init()函数,首先执行i386_detect_memory()函数,这个函数的作用是检查机器有多少内存(注释有)(有一个疑惑:其中的不可用内存空间是怎么处理的?)执行完这个函数之后,定义了一个变量kern_pgdir,这是一个指向OS页目录表的指针。对这个指针指向的地方分配内存并清零后,通过boot_alloc()函数赋值。

因此我们要开始实现boot_alloc函数。

根据注释可知,boot_alloc(n)只是一个暂时的分配器,需要实现的功能是:分配n个字节连续的内存空间。如果超出内存了就panic. 这个函数主要是维护一个nextfree的指针,指向分配内存的起始地址。

在这段代码中,end指的是.bss段的尾部,定义可见kern/kernel.ld中

 if (!nextfree) {
                extern char end[];
                nextfree = ROUNDUP((char *) end, PGSIZE);
        }

 操作系统的物理内存分配如图

 因此,编写代码如下:

// LAB 2: Your code here.
        result = nextfree;
        nextfree = ROUNDUP(nextfree+n,PGSIZE);
        if((uint32_t)nextfree-KERNBASE > (npages*PGSIZE))
                panic("Out of memory!\n");
        return result;

回到mem-init函数,继续往下阅读代码。下一行代码是:

kern_pgdir[PDX(UVPT)] = PADDR(kern_pgdir) | PTE_U | PTE_P;

这一行代码是为页目录添加第一个页目录表项。通过查看inc/memlayout.h文件,可以看到UVPT是一段虚拟地址的起始地址。也就是说从0xef400000这个虚拟地址开始,存放操作系统的页表kern_pgdir. 我们必须把它和页表kern_pgdir的物理地址映射起来,PADDR(kern_pgdir)就是在计算kern_pgdir的物理地址。

然后下面的代码需要自己编写,根据注释了解到需要完成的功能是:分配一块内存,用来存放一个struct PageInfo的数组,数组中的每一个PageInfo代表内存当中的一页。操作系统内核就是通过这个数组来追踪所有内存页的使用情况的。

代码如下:

// Your code goes here:
        pages =(struct PageInfo *) boot_alloc(npages * sizeof(struct PageInfo));
        memset(pages,0, npages * sizeof(struct PageInfo);

接下来就开始执行page_init()函数。阅读example code发现两个新变量pp_link, pp_ref,于是打开头文件inc/memlayout.h,找到了PageInfo的定义:pp_ref表示指向这个页面的指针计数(即物理地址映射向虚拟地址的映射次数)。如果是空闲的就加入空闲链表中。,*pp_link是指向空闲页面链表上下一空闲页面的指针。

根据page_init()函数中的注释可知,第0页,IO Hole和部分扩展内存都已经被占用,不能再分配。

结合上面的物理内存图和注释编写代码如下,就是把已分配的地方标记,空闲的地方用链表串起来。

size_t i;

        page_free_list = NULL;

        size_t first_free_extend_addr = PADDR (boot_alloc(0));
        for (i = 0; i < npages; i++) {
        //0 page
                if(i==0){
                  pages[i].pp_ref = 1;
        //IO hole && Extend Memory being used
                }else if(i*PGSIZE>=IOPHYSMEM && i*PGSIZE<=first_free_extend_addr){
                        pages[i].pp_ref = 1;
                }else{
                 //free memory
                 pages[i].pp_ref = 0;
                 pages[i].pp_link = page_free_list;
                 page_free_list = &pages[i];
                }
        }

page_alloc,把空闲块从空闲链表上取下来,然后进行分配。

struct PageInfo *
page_alloc(int alloc_flags)
{
        // Fill this function in
        struct PageInfo *alloc_space;
        if(page_free_list ==NULL)
                return NULL;
        alloc_space = page_free_list;
        page_free_list = alloc_space->pp_link;
        alloc_space->pp_link = NULL;

        if(alloc_flags && ALLOC_ZERO){
           memset(page2kva(alloc_space),0,PGSIZE);
        }


        return alloc_space;
}

 page_free,同样也是链表操作,在这里值得注意的是,并不需要把pp_ref设为1。

void
page_free(struct PageInfo *pp)
{
        // Fill this function in
        // 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){
          pp->pp_link = page_free_list;
          page_free_list = pp;
        }else{
         panic("This page is being used!\n");
        }
}

 到这里,除mem_init()函数外其他函数都完成啦,mem_init()函数到后面的练习继续补充。

make qemu一下,实验成功。

疑问

KERNBASE和end的地址之间的关系?

我的理解是KERNBASE是kernel的堆栈顶指针(事实上代码注释里也是这样解释的?)那end-KERNBASE是啥?搞了好久没搞懂踩个坑,看看后面的练习会不会碰到。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值