页面置换机制

结构体

现在我们来看看ucore页面置换机制的实现。

页面置换机制中, 我们需要维护一些”不在内存当中但是也许会用到“的页,它们存储在磁盘的交换区里,也有对应的虚拟地址,但是因为它们不在内存里,在页表里并没有对它们的虚拟地址进行映射。但是在发生Page Fault之后,会把访问到的页放到内存里,这时也许会把其他页扔出去,来给要用的页腾出地方。页面置换算法的核心任务,主要就是确定”把谁扔出去“。

页表里的信息只是"当前哪些数据在内存条里以及它们物理地址和虚拟地址的对应关系", 这里我们需要一些页表之外的数据结构来维护当前页表里没映射的页。也就是这些信息:有哪些虚拟地址对应的页当前在磁盘上,分别在磁盘上的哪个位置。有哪些虚拟地址对应的页面当前放在内存里。这两类页面(内存上的,磁盘上的)会相互转换(换入/换出内存),所以我们将这两类页面一起维护,也就是维护”所有可用的虚拟地址/虚拟页的集合“(不论当前这个虚拟地址对应的页在内存上还是在硬盘上)。之后我们将要实现进程机制,对于不同的进程,可用的虚拟地址(虚拟页)的集合常常是不一样的,因此每个进程需要一个页表,也需要一个数据结构来维护“所有可用的虚拟地址”。

我们在vmm.h定义两个结构体 (vmm: virtural memory management)。

vma_struct结构体描述一段连续的虚拟地址,从vm_startvm_end。 通过包含一个list_entry_t成员,我们可以把同一个页表对应的多个vma_struct结构体串成一个链表,在链表里把它们按照区间的起始点进行排序。

vm_flags表示的是一段虚拟地址对应的权限(可读,可写,可执行等),这个权限在页表项里也要进行对应的设置。

我们注意到,每个页表(每个虚拟地址空间)可能包含多个vma_struct, 也就是多个访问权限可能不同的,不相交的连续地址区间。我们用mm_struct结构体把一个页表对应的信息组合起来,包括vma_struct链表的首指针,对应的页表在内存里的指针,vma_struct链表的元素个数。

// kern/mm/vmm.h
//pre define
struct mm_struct;

// the virtual continuous memory area(vma), [vm_start, vm_end),
// addr belong to a vma means  vma.vm_start<= addr <vma.vm_end
struct vma_struct {
    struct mm_struct *vm_mm; // the set of vma using the same PDT
    uintptr_t vm_start;      // start addr of vma
    uintptr_t vm_end;        // end addr of vma, not include the vm_end itself
    uint_t vm_flags;       // flags of vma
    list_entry_t list_link;  // linear list link which sorted by start addr of vma
};

#define le2vma(le, member)                  \
    to_struct((le), struct vma_struct, member)

#define VM_READ                 0x00000001
#define VM_WRITE                0x00000002
#define VM_EXEC                 0x00000004

// the control struct for a set of vma using the same Page Table
struct mm_struct {
    list_entry_t mmap_list;        // linear list link which sorted by start addr of vma
    struct vma_struct *mmap_cache; // current accessed vma, used for speed purpose
    pde_t *pgdir;                  // the Page Table of these vma
    int map_count;                 // the count of these vma
    void *sm_priv;                   // the private data for swap manager
};

函数

create

我们需要为vma_structmm_struct定义和实现一些接口:包括它们的构造函数,以及如何把新的vma_struct插入到mm_struct对应的链表里。注意这两个结构体占用的内存空间需要用kmalloc()函数动态分配。

check_vma_overlap

在插入一个新的vma_struct之前,我们要保证它和原有的区间都不重合。

insert_vma_struct&find_vma

我们可以插入一个新的vma_struct, 也可以查找某个虚拟地址对应的vma_struct是否存在

pgfault_handler

考虑当发生Page Fault的时候我们怎么做。回顾异常处理程序,我们的trapFrame传递了badvaddrdo_pgfault()函数。这实际上是stval这个寄存器的数值(在旧版的RISCV标准里叫做sbadvaddr),这个寄存器存储一些关于异常的数据,对于PageFault它存储的是访问出错的虚拟地址。

do_pgfault()

do_pgfault()函数在vmm.c定义,是页面置换机制的核心。如果可行,我们要对页表做对应的修改,加入对应的页表项,并把硬盘上的数据换进内存。这时可能要把内存里的一个页换出去。

最后更新于

这有帮助吗?