zoukankan      html  css  js  c++  java
  • 操作系统lab2实验总结——Part1

     

    这是MIPS虚拟映射布局图,在接下来的实验中,我们需要特别注意的地址分别是kuseg和kseg0区,首先列出这两个区域的意义。

    MIPS虚存映射布局

    32位的MIPS CPU最大寻址空间为4GB(2^32字节),这4GB虚存空间被划分为四个部分:

    1. kuseg (TLB-mapped cacheable user space, 0x00000000 - 0x7fffffff): 这一段是用户模式下可用的地址,大小为2G,也就是MIPS约定的用户内存空间。需要通过MMU进行虚拟地址到物理 地址的转换。

    2. kseg0 (direct-mapped cached kernel space, 0x80000000 - 0x9fffffff): 这一段是内核地址,其内存虚存地址到物理内存地址的映射转换不通过MMU,使用时只需要将地址的最高位清零 (& 0x7fffffff), 这些地址就被转换为物理地址。也就是说,这段逻辑地址被连续地映射到物理内存的低端512M空间。对这段地址 的存取都会通过高速缓存(cached)。通常在没有MMU的系统中,这段空间用于存放大多数程序和数据。对于有 MMU 的系统,操作系统的内核会存放在这个区域。

    kuseg中有三个大小为PDMAP(4MB)的区域,分别从0x7f400000开始是ENVS,PAGES和User VPT。需要注意的是PAGES和User VPT的关系:User VPT中存放了1024个页表,由于自映射的关系,这个页表里又包括了页目录的页表,每一个页表中又有1024个页表项。PAGES存放了48个页表,这48个页表映射的内容是16*1024个物理页框的结构体(struct Page)的物理内存。在lab2实验中,我们只用了boot_map_segment函数将PAGES的内容进行了映射,并没有操作User VPT的空间。

    第一幅图将是我们理解整个mm/pmap.c的核心,接下来将按照init/init.c中的顺序进行逐一解释每个函数的细节和整体的启动布局。

    1 //这是init/init.c里的函数调用顺序
    2 mips_detect_memory();
    3 mips_vm_init();
    4 page_init();
    init/init.c

    一、mips_detect_memory()

    1.代码块

     1 void mips_detect_memory()
     2 {
     3     //in bytes
     4     basemem = 64 * 1024 * 1024;//物理内存的大小为64MB-->26位
     5     npage = 16 * 1024;//物理页框的个数
     6     maxpa = basemem;//最大的物理地址
     7     extmem = 0;//延申的空间
     8     printf("Physical memory: %dK available, ", (int)(maxpa / 1024));
     9     printf("base = %dK, extended = %dK
    ", (int)(basemem / 1024),
    10            (int)(extmem / 1024));
    11 }
    mips_detect_memory

    2.初始化一些内存相关的参数,意义写在注释里,不赘述。

    二、mips_vm_init()

    1.代码块

     1 void mips_vm_init()
     2 {
     3     extern char end[];
     4     extern int mCONTEXT;
     5     extern struct Env *envs;
     6 
     7     Pde *pgdir;
     8     u_int n;
     9 
    10     /* Step 1 */
    11     pgdir = alloc(BY2PG, BY2PG, 1);
    12     printf("to memory %x for struct page directory.
    ", freemem);
    13     mCONTEXT = (int)pgdir;//mCONTEXT是虚拟地址
    14 
    15     boot_pgdir = pgdir;
    16 
    17     /* Step 2*/
    18     pages = (struct Page *)alloc(npage * sizeof(struct Page), BY2PG, 1);
    19     printf("to memory %x for struct Pages.
    ", freemem);
    20 
    21     n = ROUND(npage * sizeof(struct Page), BY2PG); 
    22 
    23     boot_map_segment(pgdir, UPAGES, n, PADDR(pages), PTE_R);
    24 
    25     /* Step 3*/
    26     envs = (struct Env *)alloc(NENV * sizeof(struct Env), BY2PG, 1);
    27     n = ROUND(NENV * sizeof(struct Env), BY2PG);
    28     boot_map_segment(pgdir, UENVS, n, PADDR(envs), PTE_R);
    29 
    30     printf("pmap.c:	 mips vm init success
    ");
    31 }
    View Code

    2.目的:

      step1:申请了4KB的页目录空间。(pgdir)

      step2:申请了192KB的结构体空间,将这一块物理内存进行二级映射。

      step3:申请了线程的空间。(这一块不在这里讨论)

    3.布局图

    在函数的起始位置声明了

    extern char end[];

     end[]的地址就是0x8040 0000,在tools/scse0_3.lds处进行了定义。

    理解这个函数的关键是理解alloc()函数
     1 static void *alloc(u_int n, u_int align, int clear)
     2 {
     3 
     4     extern char end[];
     5     u_long alloced_mem;
     6 
     7     if (freemem == 0)
     8     {
     9         freemem = (u_long)end; 
    10     }
    11 
    12     freemem = ROUND(freemem, align);
    13     alloced_mem = freemem;
    14     freemem = freemem + n;
    15 
    16     if (clear)
    17     {
    18         bzero((void *)alloced_mem, n);
    19     }
    20 
    21     if (PADDR(freemem) >= maxpa)
    22     {
    23         panic("out of memorty
    ");
    24         return (void *)-E_NO_MEM;
    25     }
    26 
    27     return (void *)alloced_mem;
    28 }
    alloc

    freemem(全局变量)是end到physics Memory Max的空闲内存,每一次调用都将alloced_mem移到分配空间的开头,freemem移到分配空间的结尾,并返回了alloced_mem的地址。

    所以mips_vm_init对于0x8040 0000以后的空间操作如下:

     

    首先给pgdir在0x80400000申请了一个4KB的空间,然后给全局变量struct Page *pages申请了npage * sizeof(struct Page)即16*1024*12=192KB的空间。

    1 struct Page{
    2          struct {
    3             struct Page *le_next;
    4             struct Page **le_prev;
    5         } pp_link;
    6         u_short pp_ref;
    7     };
    struct Page// sizeof(struct Page)=0xC

    接着在boot_map_segment()里,每一次映射在一级页表的索引是相同的,所以会创造一个二级页表。

    4.boot_map_segment(Pde *pgdir, u_long va, u_long size, u_long pa, int perm)

    4.1代码块

     1 void boot_map_segment(Pde *pgdir, u_long va, u_long size, u_long pa, int perm)
     2 {
     3     int i, va_temp;
     4     Pte *pgtable_entry;
     5 
     6     assert(size % BY2PG == 0);
     7 
     8     for (i = 0; i < size; i += BY2PG)
     9     {
    10         pgtable_entry = boot_pgdir_Walk(pgdir, va + i, 1);//返回的是页表项的虚拟地址
    11         *pgtable_entry = (pa + i) | perm | PTE_V; //物理地址映射上
    12     }
    13 }
    View Code

    4.2理解和画了一个的图(具体说明了end[]以后的虚拟空间是怎么布局的)

    这个函数的目的是将虚拟地址空间[va, va+size) 和物理地址空间[pa, pa+size)建立映射关系。什么叫建立映射关系呢?即在虚拟地址所对应的二级页表里存放(物理页框的)物理地址。下面以下面这个调用为例,说明二级页表的映射过程。

    boot_map_segment(pgdir, UPAGES, n, PADDR(pages), PTE_R);

    这个函数的具体映射是通过boot_pgdit_walk实现的。

    static Pte *boot_pgdir_walk(Pde *pgdir, u_long va, int create)

     

              

     下面说明一下Pde*的理解(Pde*和Pte*是一样的)

    Pde *pgdir_entryp; 
    

    在include/mmu.h中定义了

    typedef u_long Pde;
    

    Pde的类型就是unsigned long,占4B。它的指针是要寻找虚拟空间,大小也是一个页表项,4B。Pde和Pde*都是4B,使它们的转换非常方便。指针实际上一块以它为地址的内存单元。所以Pde的指针(Pde*)所指的地址就是虚拟地址,而这块地址的内容(Pde)是物理地址,所以实现了虚拟地址和物理地址的映射关系。

    三、page_init()

    1.代码块

     1 void page_init(void)
     2 {
     3     /* Step 1 */
     4     LIST_INIT(&page_free_list); //page free list is a struct
     5     /* Step 2*/
     6     freemem = ROUND(freemem, BY2PG);
     7     /* Step 3 */
     8     int i;
     9     int free_page_size = PADDR(freemem) / BY2PG;
    10     for (i = 0; i < free_page_size; i++)
    11     {
    12         pages[i].pp_ref = 1;
    13     }
    14     /* Step 4 */
    15     for (; i < npage; i++)
    16     {
    17         pages[i].pp_ref = 0;
    18         LIST_INSERT_HEAD(&page_free_list, &pages[i], pp_link);
    19     }
    20 }
    page_init()

    2.目的

    这个函数本身很简单,其中有两个问题需要注意:

    • 问题1:struct Page *pages; 为什么要申请这些结构体?它和我们的页表有什么关系?
    • 问题2:Step3"Mark all memory below `freemem` as used"的意义是什么?

    在之前的操作中,我们一直使用了两个虚拟地址,一个是在kuseg0(end[](0x8040 0000) 以后),一个是在kuseg(里面有UPAGES,UVPT,UENVS)。前者不需要mmu直接将高位清零得到物理地址,后者需要mmu将虚拟地址映射到物理地址。所以其实我们对于end[]以后的所有操作可以理解成这是在对物理内存进行操作。物理内存的大小有多少呢?64MB。64MB的物理内存中有16*1024个物理页框,则16*1024个struct Page和64MB的物理页框是一一对应的。我们知道对空闲物理内存进行管理有两种基本的操作:1.位图;2.链表。所以page_free_list可以把空闲的struct Page串起来。

    在step3中我们就将end[]后面已经使用的内核虚拟地址所对应的物理地址占用的内存,转换为 [PADDR(freemem) / BY2PG] 个页表,并把它们标记过已经使用的。

    struct Page和物理内存是怎么一一对应的呢?

      (pp - pages)<< PGSHIFT

    就是算出这是pages里的第几个Page,再乘上4KB,就是物理地址了。

    上面的两个问题就解决了。

  • 相关阅读:
    Python—函数基础篇
    C语言 文件操作(四)
    C语言 文件操作(三)
    C语言 文件操作(二)
    C语言 文件操作(一)
    C语言 生日快乐
    Matlab入门(二)
    Matlab入门(一)
    精华篇:数组指针
    汇编刷题:求一到100内的偶数之和(本程序将和按十六进制输出)
  • 原文地址:https://www.cnblogs.com/puublog/p/10657916.html
Copyright © 2011-2022 走看看