zoukankan      html  css  js  c++  java
  • 6.828 lab2


    进程为程序提供了私有的环境,或者说是地址空间,保证这个地址空间不会被其他程序读写。操作系统要结合硬件的内存管理单元来实现这个功能。

    先来看看xv6

    * 分页


    x86的分页机制使用页表(page table)来将虚拟地址转换(或者称为映射)成物理地址。虚拟地址是程序指令看到的地址,物理地址是处理器硬件看到的地址。
    一个页表有2^20个入口项PTE(page table entry)。每个PTE有20-bit的物理页号(physical page number PPN)和一些标志。
    如下图,实际的转换过程分为两级,映射过程:
    1. 用虚拟地址的最高10位(Dir)到页目录中找到相应页目录项(CR3寄存器中保存页目录表的物理地址)。
    2. 页目录项的PPN保存页表的基地址。如果该页目录项存在(Preset位为1),则用虚拟地址的中间10位(Table)到页表中寻址页表项。
    3. 页表项的PPN保存物理页的基地址。如果页表项存在(Preset位为1),这里就可以顺利找到该虚拟地址对应的物理页基址。
    4. 虚拟地址的低12位(Offset)是页内偏移。知道了物理页的基地址和页内偏移,那么该虚拟地址对应的物理地址也就找到了。

    页目录其实就是一个页表,由于这里用到了两级查找,所以第一级的页表称为页目录。
    每个页表项PTE的标志位向MMU硬件描述该虚拟地址的用法。

    * 地址空间 Address space


    xv6利用分页机制为每个进程提供私有的地址空间,每个进程有自己的页表。进程的地址空间中除了用户空间映射,也包括内核空莘的映射。执行一个系统调用时,就会切换到进程地址空间中的内核映射上。这样一来,内核中的系统调用代码就可以直接访问用户空间的内存。

    __attribute__((__aligned__(PGSIZE)))
    pde_t entrypgdir[NPDENTRIES] = {
    // Map VA's [0, 4MB) to PA's [0, 4MB)
    [0] = (0) | PTE_P | PTE_W | PTE_PS,
    // Map VA's [KERNBASE, KERNBASE+4MB) to PA's [0, 4MB)
    [KERNBASE>>PDXSHIFT] = (0) | PTE_P | PTE_W | PTE_PS,
    };

    entrypgdir的地址要存到CR3寄存器中,这个数组就是页目录,当然要注意4KB对齐。

    这里的映射用到了super page(PTE_PS),大页直接映射4MB空间,无需二级页表。

     

    还需要注意的一点是,entry.S最后跳入main使用了间接跳转。

    mov $main, %eax
    jmp *%eax

    这里是从低地址跳到高地址,必须这样。如果直接jmp main的话,汇编过后,它实际上是相对当前位置的偏称跳转,跳完过后仍然在低地址。

     

    * 创建第一个进程

    xv6每个进程都用一个struct proc结构来表示,最多支持64个进程。而所有的进程都预分配好了。

    struct {
    struct spinlock lock;
    struct proc proc[NPROC];
    } ptable;

    当要创建一个进程时,就到这个ptable.proc[]里去找UNUSED的proc结构。

     


    lab 2

    本实验将要编写一些内存管理相关的代码。内存管理包括两个方面的内容:

    1. 分配和释放内存。实现这个功能,需要一些数据结构来记录物理页的使用情况。

    2. 虚拟内存。将虚拟地址转换成物理地址。

     

    第一部分:管理物理内存

    操作系统必须清楚哪些物理内存是空闲的,哪些被使用了。JOS以页为单位来管理物理内存,大概的思想是:

    1. 收集内存信息,知道当前总共可用的物理内存大小,按页来计算的话总共有npages个PGSIZE大小的页。

    2. JOS此时需要映射自己的内存布局。那么先分配一页做为页目录表kern_pgdir。内核本身占用掉的空间是[KERNBASE, end)。所以就从end后面取一页内存,注意PGSIZE对齐。

    3. 按页管理所有的物理内存,每页用一个struct Page表示,那么总共有npages个struct Page结构。紧接着kern_pgdir后面为这些Page结构分配足够的空间。

    4. 注意上面2,3两步分配内存都是直接在内存空间向后取,而现在我们有pages来表示每个物理页,就将所有空闲的物理页page链成一个单向链表,表头为page_free_list。这之后,每当我们需要分配内存时,就到链表中去拿page_alloc,用完了再page_free释放回链表。

     

    第二部分:虚拟内存

     

    * 虚拟地址、线性地址、物理地址

    对于x86来说:

    虚拟地址包括一个段选择子和段内偏移地址。

    线性地址是经过分段转换之后得到的地址。

    物理地址是经过分段和分页转换之后得到的地址,这个地址是在真正的物理内存上存在的。

     

    JOS中分段机制已经被弱化的几乎像是不存在的。在boot.S中对gdt的设置可见,代码段和数据段都直接映射了0~0xffff,ffff这4GB空间,这两个段是重叠的。这之后,cs和ds一直指向这两个固定的描述符。所以在JOS中虚拟地址和线性地址一一映射,可以说是等价的。段选择子在之后要用其最低两位来切换权限等级,除此之外,在JOS中其对于内存转换没有作用。

    gdt:
    SEG_NULL # null seg
    SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg
    SEG(STA_W, 0x0, 0xffffffff) # data seg

     

     第三部分:内核地址空间

    JOS将处理器32位线性地址空间分成两个部分:用户空间和内核空间,分界线是ULIM。

     

    使用页表里的权限位来控制访问权限。用户程序不能访问内核空间的数据。

    Question

    1. Assuming that the following JOS kernel code is correct, what type should variable x have, uintptr_t or physaddr_t?

    mystery_t x;
    char* value = return_a_pointer();
    *value = 10;
    x = (mystery_t) value;

    x的类型是uintptr_t

     

    2. What entries (rows) in the page directory have been filled in at this point? What addresses do they map and where do they point? In other words, fill out this table as much as possible:

     

    3. (From Lecture 3) We have placed the kernel and user environment in the same address space. Why will user programs not be able to read or write the kernel's memory? What specific mechanisms protect the kernel memory?

    页表里的权限标志定义了该页的访问权限,而内核和用户进程运行时的段选择子cs最低两位定义了该程序运行时的权限。这两个地方就决定了程序是否可以访问某页。


    4. What is the maximum amount of physical memory that this operating system can support? Why?

    32位CPU最大支持4GB物理内存,地址总线只有32根。x86后来有个PAE模式可以支持更多的物理内存。


    5. How much space overhead is there for managing memory, if we actually had the maximum amount of physical memory? How is this overhead broken down?

    每个物理页用一个struct Page结构来管理。一个Page结构占8字节。256MB有65536页,要花掉512KB空间来管理。如果有4GB物理内存,则要花掉8MB空间来管理。


    6. Revisit the page table setup in kern/entry.S and kern/entrypgdir.c. Immediately after we turn on paging, EIP is still a low number (a little over 1MB). At what point do we transition to running at an EIP above KERNBASE? What makes it possible for us to continue executing at a low EIP between when we enable paging and when we begin running at an EIP above KERNBASE? Why is this transition necessary?

    mov $relocated, %eax
    jmp *%eax

    这句jmp间接跳转,就跳到了KERNBASE以上的空间。

    kern_pgdir[]页目录中设置映射关系,有两块虚拟地址[0, 0x0040,0000)和[0xf000,0000, 0xffff,ffff)映射到同一物理地址[0, 4MB)。所以在低地址和高地址都能正确执行。

    如果只映射[0xf000,0000, 0xffff,ffff),那么开启分页转换后eip仍是低地址,此时经过MMU转换无法找到正确的数据(指令)。

     

    实验  

    按照下面的步骤做一做实验,加深对MMU分页机制的理解

    6.828 2011 Lecture 4: Process Creation
    
    big picture
      getting xv6 as far as the first process
      load kernel
      temporary page table
      real page table
      create process
      switch to process
      exec /init
    
    why do we care about VM?
      implements address spaces:
        force each process to only r/w its own memory (bugs, security)
        user code at predictable addresses
        big contiguous user address space
    
    where we were on Wednesday
      setting up a page table for xv6 kernel
      diagram of virtual address space
         0x80000000
         0x00000000   
      each process has its own page table
      plus one for when not running a process (e.g. early in boot)
    
    quick review of x86 page directory / page table
      [diagram: cr3, 1024 PDEs, 1024 page table pages]
      see last week's handout
      PTE: 22 bits phys addr, 12 flag bits
      translation: 10, 10, 12
    
    we were early in main(), in kvmalloc(), after setupkvm()
      sheet 17
    
    let's look at the page table (kpgdir) that setupkvm produces
    (gdb) break kvmalloc
    (gdb) next
    (gdb) print/x kpgdir[0]
    why is is zero?
    
    let's look up a virtual address
      how about the first instruction of kvmalloc
      (gdb) x/i kvmalloc
      0x80107990 <kvmalloc>:  push   %ebp
      how would we translate 0x80107990 to a physical address?
    这里可以看到kvmalloc第一条指令,位于虚拟地址0x80107990处,那么这个虚拟地址是如何被转换成物理地址的呢?下面的步骤一步一步转换
    (gdb) print/x 0x80107990 >> 22 $4 = 0x200 (gdb) print/x kpgdir[0x200] $6 = 0x114007 Q: what is this?
    根据虚拟地址可以算出PDX,再到页目录中找对应页目录项,这个目录项的值指定了页表的位置 Q: what is the PPN?
    PPN就是页表位置,0x114000 Q: what does the 7 mean?
    7是页目录项的权限位,由于页目录项和页表项都有权限位。内核设计使用页表的权限位,那么页目录项中的权限全部打开。7意味着PTE_P|PTE_W|PTE_U. (gdb) print/x (0x80107990 >> 12) & 0xfff $6 = 0x107 (gdb) print/x ((int*)0x114000)[0x107] $12 = 0x107001 Q: what is this?
    0x107是PTX,在页表中的索引。0x107001就是该页表项的值。 Q: why 1 in the low bits?
    1表示该页表项映射的物理页只置了PTE_P标志 (gdb) print/x 0x107000 + 0x990 $13 = 0x107990 (gdb) x/i 0x107990 wait!!! why did the physical address work in gdb? 此时已经打开了分页模式,gdb当然无法直接查看物理地址中的数据。所有的地址都是虚拟地址。只是XV6最初也将虚拟地址的低4MB直接映射到物理地址的低4MB,所以这里的操作是没有问题的。
    最后x/i 0x107990与之前的x/i 0x80107990 输出都是相同的。因为两个虚拟地址都映射到同一个物理地址。
    back to kvmalloc it called setupkvm to create a page table now it calls switchkvm to start using it switchkvm loads kpgdir into %cr3 new page table much like the previous one maps more phys mem above the kernel does not have temporary mapping for low 4 MB and now 0x170990 won't work: (gdb) x/i 0x107990 0x107990: Cannot access memory at address 0x107990 切换到新的页目录kpgdir,kpgdir中没有映射最低4MB的虚拟地址,所以这里无法访问了。


  • 相关阅读:
    HTTP协议 (二) 基本认证
    HTTP协议详解
    Wireshark基本介绍和学习TCP三次握手
    Fiddler 教程
    UNIX网络编程——线程池模式比较(ICE线程池模型和L/F领导者跟随者模式)
    修改一行SQL代码 性能提升了100倍
    修改一行SQL代码 性能提升了100倍
    vector map 多层嵌套使用
    vector map 多层嵌套使用
    应该记住的话
  • 原文地址:https://www.cnblogs.com/sammei/p/3295608.html
Copyright © 2011-2022 走看看