操作系统 ucore lab2

操作系统 ucore lab2 物理内存管理

实验目的

  • 理解基于段页式内存地址的转换机制
  • 理解页表的建立和使用方法
  • 理解物理内存的管理方法

实验步骤

练习0:填写已有实验

  • 利用meld比较lab1和lab2两个文件夹,发现
    在这里插入图片描述
  • 发现kdebug.c和trap.c有差异,点入进行修改,标识明显,简单方便。
  • kdebug.c:在这里插入图片描述
  • trap.c:
    在这里插入图片描述
    在这里插入图片描述

练习1:实现 first-fit 连续物理内存分配算法

算法基本思想:要求空闲区按地址递增的次序排列。当进行内存分配时,从空闲区表头开始顺序查找,直到找到第一个能滿足其大小要求的空闲区为止。

  • 在memlayout.h中的结构体Page:
struct Page {
    int ref;                        // 表示该页被页表的引用记数
    uint32_t flags;                 // 表示此物理页的状态标记,bit0表示是否被保留,bit1表示是否是空闲的
    unsigned int property;          // 用来记录某连续内存空闲块的大小
    list_entry_t page_link;         // 是便于把多个连续内存空闲块链接在一起的双向链表指针
};
  • 在memlayout.h中的结构体free_area_t:
typedef struct {
    list_entry_t free_list;         // 一个list_entry结构的双向链表指针
    unsigned int nr_free;           // 记录当前空闲页的个数
} free_area_t;
  • pmm.h中的物理内存管理类pmm_manager:
struct pmm_manager {
    const char *name;                                 // 名字
    void (*init)(void);                               // 初始化             
    void (*init_memmap)(struct Page *base, size_t n); // 根据初始物理内存空间设置描述和管理数据结构
    struct Page *(*alloc_pages)(size_t n);            // 分配页
    void (*free_pages)(struct Page *base, size_t n);  // 释放页
    size_t (*nr_free_pages)(void);                    // 返回空闲页数
    void (*check)(void);                              // 检验管理器的正确性
};
  • default_pmm.c中的default_init():
static void
default_init(void) {           // 插入free_list并将nr_free置为0
    list_init(&free_list);     // 记录freemem块
    nr_free = 0;               // free mem块总数.
}
  • default_pmm.c中的default_init_memmap():
    用来初始化空闲页链表的,初始化每一个空闲页,然后计算空闲页的总数。

注释:

* (3) default_init_memmap:  CALL GRAPH: kern_init --> pmm_init-->page_init-->init_memmap--> pmm_manager->init_memmap
 *              This fun is used to init a free block (with parameter: addr_base, page_number).
 *              First you should init each page (in memlayout.h) in this free block, include:
 *                  p->flags should be set bit PG_property (means this page is valid. In pmm_init fun (in pmm.c),
 *                  the bit PG_reserved is setted in p->flags)
 *                  if this page  is free and is not the first page of free block, p->property should be set to 0.
 *                  if this page  is free and is the first page of free block, p->property should be set to total num of block.
 *                  p->ref should be 0, because now p is free and no reference.
 *                  We can use p->page_link to link this page to free_list, (such as: list_add_before(&free_list, &(p->page_link)); )
 *              Finally, we should sum the number of free mem block: nr_free+=n

改写:

static void 
default_init_memmap(struct Page *base, size_t n) {   
    assert(n > 0);
    struct Page *p = base;
    for (; p != base + n; p ++)
    {
        assert(PageReserved(p));                        //确认本页是否为保留页
        p->flags = 0//设置标志位
        SetPageProperty(p);
        p->property = 0;
        set_page_ref(p, 0);//清空引用
        list_add_before(&free_list, &(p->page_link));   //插入空闲页的链表里面
    }
    nr_free += n;                                       //说明连续有n个空闲块,属于空闲链表
    base->property=n;                                   //连续内存空闲块的大小为n,属于物理页管理链表
}
  • default_pmm.c中的default_alloc_pages():
    遍历空闲页块的链表,找到第一块大小大于n的块,分配出来,从空闲页链表中移除,若有多余的,再加入空闲页链表中。

注释:

* (4) default_alloc_pages: search find a first free block (block size >=n) in free list and reszie the free block, return the addr
 *              of malloced block.
 *              (4.1) So you should search freelist like this:
 *                       list_entry_t le = &free_list;
 *                       while((le=list_next(le)) != &free_list) {
 *                       ....
 *                 (4.1.1) In while loop, get the struct page and check the p->property (record the num of free block) >=n?
 *                       struct Page *p = le2page(le, page_link);
 *                       if(p->property >= n){ ...
 *                 (4.1.2) If we find this p, then it' means we find a free block(block size >=n), and the first n pages can be malloced.
 *                     Some flag bits of this page should be setted: PG_reserved =1, PG_property =0
 *                     unlink the pages from free_list
 *                     (4.1.2.1) If (p->property >n), we should re-caluclate number of the the rest of this free block,
 *                           (such as: le2page(le,page_link))->property = p->property - n;)
 *                 (4.1.3)  re-caluclate nr_free (number of the the rest of all free block)
 *                 (4.1.4)  return p
 *               (4.2) If we can not find a free block (block size >=n), then return NULL

改写:

static struct Page *
default_alloc_pages(size_t n) {
    assert(n>0);
    if(n>nr_free) return NULL;//如果所有的空闲页的加起来的大小都不够,那直接返回NULL
    list_entry_t *le, *len;
    le=&free_list;          //从空闲块链表的头指针开始
    while((le=list_next(le))!=&free_list) //依次往下寻找直到回到头指针处,即已经遍历一次
    {
      struct Page *p = le2page(le, page_link);//将地址转换成页的结构
      if(p->property >= n)   //遇到的第一个大于N的块
      {
        int i;
        for(i=0;i<n;i++)     //把选中的空闲块链表中的每一个页结构初始化
        {
          len = list_next(le);
          struct Page *pp = le2page(le, page_link);
          SetPageReserved(pp);
          ClearPageProperty(pp);
          list_del(le); //删除双向链表指针
          le = len;
        }
        if(p->property>n)
            (le2page(le,page_link))->property = p->property - n; //只取其中的大小为n的块
        ClearPageProperty(p);
        SetPageReserved(p);
        nr_free-=n;   //空闲页的数目减n
        return p;
      }
    }
    return NULL;      //没找到
}

练习2:实现寻找虚拟地址对应的页表项

通过设置页表和对应的页表项,可建立虚拟内存地址和物理内存地址的对应关系。其中的get_pte函数是设置页表项环节中的一个重要步骤。
在这里插入图片描述

  • pmm.c中的get_pte():
    找到一个虚地址对应的二级页表项的内核虚地址,如果此二级页表项不存在,则分配一个包含此项的二级页表。
pte_t *
get_pte(pde_t *pgdir, uintptr_t la, bool create) {
    pde_t *pdep = &pgdir[PDX(la)];                              // (1) find page directory entry
    if (!(*pdep & PTE_P)) {                                     // (2) check if entry is not present
        struct Page *page;
        if (!create || (page = alloc_page()) == NULL) { // (3) check if creating is needed, then alloc page for page table
           return NULL;
        } 
        set_page_ref(page, 1);                                  // (4) set page reference
        uintptr_t pa = page2pa(page);                           // (5) get linear address of page
        //注释中给了提示,If you need to visit a physical address, please use KADDR()
        memset(KADDR(pa), 0, PGSIZE);                           // (6) clear page content using memset
        *pdep = pa | PTE_U | PTE_W | PTE_P;                     // (7) set page directory entry's permission
       }
       return &((pte_t *)KADDR(PDE_ADDR(*pdep)))[PTX(la)];      // (8) return page table entry
}
  • 请描述页目录项(Page Directory Entry)和页表项(Page Table Entry)中每个组成部分的含义以及对ucore而言的潜在用处。

页目录项:

名称 地址 ucore对应
Page Table 4KB Aligned Address 31 downto 12 对应的页表地址
Avail 11 downto 9 PTE_AVAIL
Ignored 8
Page Size 7 PTE_PS
0 6 PTE_MBZ
Accessed 5 PTE_A
Cache Disabled 4 PTE_PCD
Write Through 3 PTE_PWT
User/Supervisor 2 PTE_U
Read/Write 1 PTE_W
Present 0 PTE_P

页表项

名称 地址 ucore对应
Physical Page Address 31 downto 12 对应的物理地址高20位
Avail 11 downto 9 PTE_AVAIL
Global 8
0 7 PTE_MBZ
Dirty 6 PTE_D
Accessed 5 PTE_A
Cache Disabled 4 PTE_PCD
Write Through 3 PTE_PWT
User/Supervisor 2 PTE_U
Read/Write 1 PTE_W
Present 0 PTE_P
  • 如果ucore执行过程中访问内存,出现了页访问异常,请问硬件要做哪些事情?
    1.硬件陷入内核,在内核堆栈中保存程序计数器。
    2.启动一个汇编代码例程保存通用寄存器和其他易失的信息,以免被操作系统破坏。
    3.当操作系统发现一个缺页中断时,尝试发现需要哪个虚拟页面。通常一个硬件寄存器包含了这一信息,如果没有的话,操作系统必须检索程序计数器,取出这条指令,用软件分析这条指令,看看它在缺页中断时正在做什么。
    4.一旦知道了发生缺页中断的虚拟地址,操作系统检查这个地址是否有效,并检查存取与保护是否一致。如果不一致,向进程发出一个信号或杀掉该进程。如果地址有效且没有保护错误发生,系统则检查是否有空闲页框。如果没有空闲页框,执行页面置换算法寻找一个页面来淘汰。
    5.如果选择的页框“脏”了,安排该页写回磁盘,并发生一次上下文切换,挂起产生缺页中断的进程,让其他进程运行直至磁盘传输结束。无论如何,该页框被标记为忙,以免因为其他原因而被其他进程占用。
    6.一旦页框“干净”后(无论是立刻还是在写回磁盘后),操作系统查找所需页面在磁盘上的地址,通过磁盘操作将其装入。该页面被装入后,产生缺页中断的进程仍然被挂起,并且如果有其他可运行的用户进程,则选择另一个用户进程运行。
    7.当磁盘中断发生时,表明该页已经被装入,页表已经更新可以反映它的位置,页框也被标记为正常状态。
    8.恢复发生缺页中断指令以前的状态,程序计数器重新指向这条指令。
    9.调度引发缺页中断的进程,操作系统返回调用它的汇编语言。
    10.该例程恢复寄存器和其他状态信息。

练习3:释放某虚地址所在的页并取消对应二级页表项的映射

当释放一个包含某虚地址的物理内存页时,需要让对应此物理内存页的管理数据结构Page做相关的清除处理,使得此物理内存页成为空闲;另外还需把表示虚地址与物理地址对应关系的二级页表项清除。请仔细查看和理解page_remove_pte函数中的注释。为此,需要补全在 kern/mm/pmm.c中的page_remove_pte函数。page_remove_pte函数的调用关系图如下所示:

图2 page_remove_pte函数的调用关系图
请在实验报告中简要说明你的设计实现过程。请回答如下问题:
数据结构Page的全局变量(其实是一个数组)的每一项与页表中的页目录项和页表项有无对应关系?如果有,其对应关系是啥?
如果希望虚拟地址与物理地址相等,则需要如何修改lab2,完成此事? 鼓励通过编程来具体完成这个问题

扩展练习Challenge:buddy system(伙伴系统)分配算法(需要编程)

Buddy System算法把系统中的可用存储空间划分为存储块(Block)来进行管理, 每个存储块的大小必须是2的n次幂(Pow(2, n)), 即1, 2, 4, 8, 16, 32, 64, 128…
参考伙伴分配器的一个极简实现, 在ucore中实现buddy system分配算法,要求有比较充分的测试用例说明实现的正确性,需要有设计文档。

扩展练习Challenge:任意大小的内存单元slub分配算法(需要编程)

slub算法,实现两层架构的高效内存单元分配,第一层是基于页大小的内存分配,第二层是在第一层基础上实现基于任意大小的内存分配。可简化实现,能够体现其主体思想即可。

参考linux的slub分配算法/,在ucore中实现slub分配算法。要求有比较充分的测试用例说明实现的正确性,需要有设计文档。
Challenges是选做,做一个就很好了。完成Challenge的同学可单独提交Challenge。完成得好的同学可获得最终考试成绩的加分。

发布了173 篇原创文章 · 获赞 6 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/shidonghang/article/details/102685610
今日推荐