• Lab2 内存管理(实现细节)


    lab2 中的变动

    bootloader 的入口发生了改变
    bootloader不像lab1那样,直接调用kern_init函数,而是先调用位于lab2/kern/init/entry.S中的kern_entry函数。kern_entry函数的主要任务是为执行kern_init建立一个良好的C语言运行环境(设置堆栈),而且临时建立了一个段映射关系,为之后建立分页机制的过程做一个准备(细节在3.5小节有进一步阐述)。完成这些工作后,才调用kern_init函数。

    通过 BIOS 中断获取内存布局有三种方式,都是基于INT 15h中断,分别为88h e801h e820h。但是 并非在所有情况下这三种方式都能工作。在 Linux kernel 里,采用的方法是依次尝试这三 种方法。而在本实验中,我们通过e820h中断获取内存信息。因为e820h中断必须在实模式下使用,所以我们在 bootloader 进入保护模式之前调用这个 BIOS 中断,并且把 e820 映 射结构保存在物理地址0x8000处。

    以页为单位管理物理内存##

    Page的定义在kern/mm/memlayout.h中。以页为单位的物理内存分配管理的实现在kern/default_pmm.[ch]。

    首先就是 Page 结构用来描述物理页

    +ref 被引用的次数
    +flags 状态
    +property 算法使用的状态
    +page-link 连接其他的自由变量

    接下来需要解决两个问题:

    管理页级物理内存空间所需的Page结构的内存空间从哪里开始 占多大空间?
    空闲内存空间的起始地址在哪里?

    首先根据bootloader 给出的内存布局信息找出最大的物理内存地址 maxpa(定义在page_init 中的局部变量), 由于x86 的起始物理内存地址为0, 所以可以得到需要管理的物理页个数为 : npage=maxpa / PGSIZE

    这样可以估算出管理页级物理内存空间所需的 Page 结构的空间大小为
    sizeof(struct Page) *npage

    由于bootloader 加载ucore的结束地址 用全局变量指针end 记录

    以上的空间没有被使用,所以我们可以把end按页大小为边界取整后,作为管理页级物理内存空间所需的Page结构的内存空间,记为
    pages = (struct Page *)ROUNDUP((void *)end, PGSIZE);

    为了简化起见,从地址0到地址pages+ sizeof(struct Page) * npage)结束的物理内存空间设定为已占用物理内存空间(起始0~640KB的空间是空闲的),地址pages+ sizeof(struct Page) * npage)以上的空间为空闲物理内存空间,这时的空闲空间起始地址为

    uintptr_t freemem = PADDR((uintptr_t)pages + sizeof(struct Page) * npage);

    for (i = 0; i < npage; i ++) {
    SetPageReserved(pages + i);
    }

    init_memmap(pa2page(begin), (end - begin) / PGSIZE);

    其实SetPageReserved只需把物理地址对应的Page结构中的flags标志设置为PG_reserved ,表示这些页已经被使用了,将来不能被用于分配。而init_memmap函数则是把空闲物理页对应的Page结构中的flags和引用计数ref清零,并加到free_area.free_list指向的双向列表中,为将来的空闲页管理做好初始化准备工作。

    只实现最简单的内存页分配算法。
    下面是内存分配器结构的结论批
    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); //分配n个物理内存页
    void (
    free_pages)(struct Page base, size_t n); //释放n个物理内存页
    size_t (
    nr_free_pages)(void); //返回当前剩余的空闲页数
    void (
    check)(void); //用于检测分配/释放实现是否正确的辅助函数
    };

    kern/mm/pmm.h 中定义了一个通用的分配算法的函数列表,用pmm_mamager 表示。 init 函数是用来初始话 free_area 变量的

    first_fit 可以重用default_init 函数的实现。
    init_memmao 函数需要根据现有的内存情况构建空闲块列表的初始状态

    pmm.c 模块

    get_pte 获取一个线性地址对应的页表项地址

    页表项除去状态位然后 剩下物理地址 根据这个物理地址取出 PPN 在pages 结构中找到对应的页项 或者说 pte2page

    get_page 获取一个物理地址对应的页项 会存储这个页表项到第三个参数

    page_remove_pte 接受一个页表项 删除 如果对应的页没有引用了 就free 那个一个页 还要将tlb无效化

    page_insert 使一个线性地址 被映射到一个页中 , 那个线性地址原先如果已经有映射了 那就取消这个映射

    那么还需要最后一个封装
    使用 alloc_page 申请得到一个新的页面
    使用 page_insert 将这个线性地址插入到对应的pte中去
    就是 pgdir_alloc_page

    get_pte 的一些细节##

    输入 pde_t * pgdir uintptr_t la, bool create

    取得页目录项地址 pde_t * pde = &pgdir[PDX(la)]

    取得页表物理地址 PDE_ADDR(*pde)

    取得页表的内核虚拟地址 KADDR(PDE_ADDR(*pde))

    取得页表项的虚拟地址 &((pte_t )KADDR(PDE_ADDR(pde)))[PTX(la )]

  • 相关阅读:
    Python杂记
    设置Python打印格式
    SFTP和FTS协议的区别
    C#6.0语法糖剖析(一)
    .NET Framework 4.0之Tuple(元组)
    以Self Host的方式来寄宿Web API
    以Web Host的方式来寄宿Web API
    IIS在默认情况并不支持对PUT和DELETE请求的支持
    ASP.NET Web API 特性
    在Windows下编写并运行第一个ASP.NET 5 Preview Web API程序
  • 原文地址:https://www.cnblogs.com/sfzyk/p/9027241.html
Copyright © 2020-2023  润新知