• BUAA_OS_2020_Lab2_Code_Review


    本来打算返校上机之前再继续写code review的(拖延症),不过看来返校还遥遥无期,所以就先写了⑧。

    Lab2文件树如下,新增文件已用*标出,本lab相关的主要是内存管理方面的文件。

     1 .
     2 ├── boot
     3 │   ├── Makefile
     4 │   └── start.S
     5 ├── drivers
     6 │   ├── gxconsole
     7 │   │   ├── console.c
     8 │   │   ├── dev_cons.h
     9 │   │   └── Makefile
    10 │   └── Makefile
    11 ├── gxemul
    12 │   ├── elfinfo
    13 │   ├── r3000
    14 │   ├── r3000_test
    15 │   ├── test
    16 │   └── view *
    17 ├── include
    18 │   ├── args.h *
    19 │   ├── asm
    20 │   │   ├── asm.h
    21 │   │   ├── cp0regdef.h
    22 │   │   └── regdef.h
    23 │   ├── asm-mips3k
    24 │   │   ├── asm.h
    25 │   │   ├── cp0regdef.h
    26 │   │   └── regdef.h
    27 │   ├── env.h
    28 │   ├── error.h
    29 │   ├── kclock.h
    30 │   ├── mmu.h
    31 │   ├── pmap.h
    32 │   ├── printf.h
    33 │   ├── print.h
    34 │   ├── queue.h
    35 │   ├── sched.h
    36 │   ├── stackframe.h
    37 │   ├── trap.h
    38 │   ├── types.h
    39 │   └── unistd.h *
    40 ├── include.mk
    41 ├── init
    42 │   ├── init.c
    43 │   ├── main.c
    44 │   └── Makefile
    45 ├── lib
    46 │   ├── env.c *
    47 │   ├── genex.S *
    48 │   ├── Makefile
    49 │   ├── printBackUp
    50 │   ├── print.c
    51 │   └── printf.c
    52 ├── Makefile
    53 ├── mm *
    54 │   ├── Makefile *
    55 │   ├── pmap.c *
    56 │   └── tlb_asm.S *
    57 ├── readelf
    58 │   ├── kerelf.h
    59 │   ├── main.c
    60 │   ├── Makefile
    61 │   ├── readelf.c
    62 │   ├── testELF
    63 │   └── types.h
    64 └── tools
    65     └── scse0_3.lds
    Lab2文件树(已折叠)

    一些实用宏

    链表操作的宏在./include/queue.h中给出,虽然文件名是queue.h但其实定义了双向链表、双向尾队列和循环队列三种数据结构。

      1 /*
      2  * ./include/queue.h
      3  */
      4 
      5 #ifndef _SYS_QUEUE_H_
      6 #define _SYS_QUEUE_H_
      7 
      8 /*
      9  * This file defines three types of data structures: lists, tail queues,
     10  * and circular queues.
     11  *
     12  * A list is headed by a single forward pointer(or an array of forward
     13  * pointers for a hash table header). The elements are doubly linked
     14  * so that an arbitrary element can be removed without a need to
     15  * traverse the list. New elements can be added to the list before
     16  * or after an existing element or at the head of the list. A list
     17  * may only be traversed in the forward direction.
     18  *
     19  * A tail queue is headed by a pair of pointers, one to the head of the
     20  * list and the other to the tail of the list. The elements are doubly
     21  * linked so that an arbitrary element can be removed without a need to
     22  * traverse the list. New elements can be added to the list before or
     23  * after an existing element, at the head of the list, or at the end of
     24  * the list. A tail queue may only be traversed in the forward direction.
     25  *
     26  * A circle queue is headed by a pair of pointers, one to the head of the
     27  * list and the other to the tail of the list. The elements are doubly
     28  * linked so that an arbitrary element can be removed without a need to
     29  * traverse the list. New elements can be added to the list before or after
     30  * an existing element, at the head of the list, or at the end of the list.
     31  * A circle queue may be traversed in either direction, but has a more
     32  * complex end of list detection.
     33  *
     34  * For details on the use of these macros, see the queue(3) manual page.
     35  */
     36 
     37 /*
     38  * List declarations.
     39  */
     40 
     41 /*
     42  * A list is headed by a structure defined by the LIST_HEAD macro.  This structure con‐
     43  * tains a single pointer to the first element on the list.  The elements are doubly
     44  * linked so that an arbitrary element can be removed without traversing the list.  New
     45  * elements can be added to the list after an existing element or at the head of the list.
     46  * A LIST_HEAD structure is declared as follows:
     47  *
     48  *       LIST_HEAD(HEADNAME, TYPE) head;
     49  *
     50  * where HEADNAME is the name of the structure to be defined, and TYPE is the type of the
     51  * elements to be linked into the list.
     52  */
     53 #define LIST_HEAD(name, type)                                           
     54         struct name {                                                           
     55                 struct type *lh_first;  /* first element */                     
     56         }
     57 
     58 /*
     59  * Set a list head variable to LIST_HEAD_INITIALIZER(head)
     60  * to reset it to the empty list.
     61  */
     62 #define LIST_HEAD_INITIALIZER(head)                                     
     63         { NULL }
     64 
     65 /*
     66  * Use this inside a structure "LIST_ENTRY(type) field" to use
     67  * x as the list piece.
     68  *
     69  * The le_prev points at the pointer to the structure containing
     70  * this very LIST_ENTRY, so that if we want to remove this list entry,
     71  * we can do *le_prev = le_next to update the structure pointing at us.
     72  */
     73 #define LIST_ENTRY(type)                                                
     74         struct {                                                                
     75                 struct type *le_next;   /* next element */                      
     76                 struct type **le_prev;  /* address of previous next element */  
     77         }
     78 
     79 /*
     80  * List functions.
     81  */
     82 
     83 /*
     84  * Detect the list named "head" is empty.
     85  */
     86 #define LIST_EMPTY(head)        ((head)->lh_first == NULL)
     87 
     88 /*
     89  * Return the first element in the list named "head".
     90  */
     91 #define LIST_FIRST(head)        ((head)->lh_first)
     92 
     93 /*
     94  * Iterate over the elements in the list named "head".
     95  * During the loop, assign the list elements to the variable "var"
     96  * and use the LIST_ENTRY structure member "field" as the link field.
     97  */
     98 #define LIST_FOREACH(var, head, field)                                  
     99         for ((var) = LIST_FIRST((head));                                
    100                  (var);                                                 
    101                  (var) = LIST_NEXT((var), field))
    102 
    103 /*
    104  * Reset the list named "head" to the empty list.
    105  */
    106 #define LIST_INIT(head) do {                                            
    107                 LIST_FIRST((head)) = NULL;                                      
    108         } while (0)
    109 
    110 /*
    111  * Insert the element "elm" *after* the element "listelm" which is
    112  * already in the list.  The "field" name is the link element
    113  * as above.
    114  */
    115 
    116 
    117 #define LIST_INSERT_AFTER(listelm, elm, field) do { 
    118                LIST_NEXT((elm), field) = LIST_NEXT((listelm), field); 
    119                if (LIST_NEXT((listelm), field)) { 
    120                        LIST_NEXT((listelm), field)->field.le_prev = &LIST_NEXT((elm), field); 
    121                } 
    122                LIST_NEXT((listelm), field) = (elm); 
    123                (elm)->field.le_prev = &LIST_NEXT((listelm), field); 
    124         }while (0)
    125         // Note: assign a to b <==> a = b
    126         //Step 1, assign elm.next to listelem.next.
    127         //Step 2: Judge whether listelm.next is NULL, if not, then assign listelm.pre to a proper value.
    128         //step 3: Assign listelm.next to a proper value.
    129         //step 4: Assign elm.pre to a proper value.
    130 
    131 
    132 /*
    133  * Insert the element "elm" *before* the element "listelm" which is
    134  * already in the list.  The "field" name is the link element
    135  * as above.
    136  */
    137 #define LIST_INSERT_BEFORE(listelm, elm, field) do {                    
    138                 (elm)->field.le_prev = (listelm)->field.le_prev;                
    139                 LIST_NEXT((elm), field) = (listelm);                            
    140                 *(listelm)->field.le_prev = (elm);                              
    141                 (listelm)->field.le_prev = &LIST_NEXT((elm), field);            
    142         } while (0)
    143 
    144 /*
    145  * Insert the element "elm" at the head of the list named "head".
    146  * The "field" name is the link element as above.
    147  */
    148 #define LIST_INSERT_HEAD(head, elm, field) do {                         
    149                 if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL)     
    150                         LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);
    151                 LIST_FIRST((head)) = (elm);                                     
    152                 (elm)->field.le_prev = &LIST_FIRST((head));                     
    153         } while (0)
    154 
    155 /*
    156  * Insert the element "elm" at the tail of the list named "head".
    157  * The "field" name is the link element as above. You can refer to LIST_INSERT_HEAD.
    158  * Note: this function has big differences with LIST_INSERT_HEAD !
    159  */
    160 #define LIST_INSERT_TAIL(head, elm, field) do { 
    161                 if (LIST_FIRST((head)) != NULL) { 
    162                         LIST_NEXT((elm), field) = LIST_FIRST((head)); 
    163                         while (LIST_NEXT(LIST_NEXT((elm), field), field) != NULL) {  
    164                             LIST_NEXT((elm), field) = LIST_NEXT(LIST_NEXT((elm), field), field); 
    165                         } 
    166                         LIST_NEXT(LIST_NEXT((elm), field), field) = (elm); 
    167                         (elm)->field.le_prev = &LIST_NEXT(LIST_NEXT((elm), field), field); 
    168                         LIST_NEXT((elm), field) = NULL; 
    169                 } else { 
    170                     LIST_INSERT_HEAD((head), (elm), field); 
    171                 } 
    172         } while (0)
    173 
    174 
    175 #define LIST_NEXT(elm, field)   ((elm)->field.le_next)
    176 
    177 /*
    178  * Remove the element "elm" from the list.
    179  * The "field" name is the link element as above.
    180  */
    181 #define LIST_REMOVE(elm, field) do {                                    
    182                 if (LIST_NEXT((elm), field) != NULL)                            
    183                         LIST_NEXT((elm), field)->field.le_prev =                
    184                                         (elm)->field.le_prev;                           
    185                 *(elm)->field.le_prev = LIST_NEXT((elm), field);                
    186         } while (0)
    187 
    188 /*
    189  * Tail queue definitions.
    190  */
    191 #define TAILQ_HEAD(name, type)                                          
    192         struct name {                                                           
    193                 struct type *tqh_first; /* first element */                     
    194                 struct type **tqh_last; /* addr of last next element */         
    195         }
    196 
    197 #define TAILQ_ENTRY(type)                                               
    198         struct {                                                                
    199                 struct type *tqe_next;  /* next element */                      
    200                 struct type **tqe_prev; /* address of previous next element */  
    201         }
    202 
    203 #endif  /* !_SYS_QUEUE_H_ */
    ./include/queue.h(已折叠)

    其中定义的宏的意义与用法示例如下,

    链表的声明相关宏:

    • LIST_HEAD(name, type):定义了链表的表头,其中含有一个指向链表头部的指针。用法:LIST_HEAD(headstruct, Type) head;
    • LIST_HEAD_INITIALIZER(head):用于将表头初始化为NULL,但似乎没有实现,如果实现的话应该是{head->lh_first = NULL;},用法:LIST_HEAD_INITIALIZER(&head);(注意取地址符)
    • LIST_ENTRY(type):相当于链表项的索引,含有指向上一个和下一个元素的指针,用法:LIST_ENTRY(Type) entry;

    链表操作相关宏:

    • LIST_EMPTY(head):判断链表是否为空,用法示例:if(LIST_EMPTY(&head)) {/*do something*/}
    • LIST_FIRST(head):用于获得链表第一个元素的指针,用法:Type* first = LIST_FIRST(head);
    • LIST_FOREACH(var, head, field):用于遍历链表,用法:LIST_FOREACH(item, &head, field) {/*do something*/}
    • LIST_INIT(head):与LIST_HEAD_INITIALIZER相同,用法:LIST_INIT(&head);
    • LIST_INSERT_AFTER(listelm, elm, field)LIST_INSERT_BEFORE(listelm, elm, field):分别是在链表中指定元素的后方与前方插入元素,listelm是链表中元素、elm是要插入的元素,field是访问时用到的域。
    • LIST_INSERT_HEAD(head, elm, field)LIST_INSERT_TAIL(head, elm, field):分别是在链表的头部与尾部插入元素elm。
    • LIST_NEXT(elm, field):用于访问链表中指定元素的下一个元素。
    • LIST_REMOVE(elm, field):用于从链表中移除指定元素

    尾队列暂时没有用到所以略过。需要注意的是,这种链表的结构其实非常特殊,不同于普通的链表。以本次的Page结构体为例,其结构为:

    其中pp_link是由LIST_ENTRY定义,可以看出LIST_ENTRY是作为Page结构体的子结构体使用的,其prev指针也并不是指向了上一个Page结构体,而是指向了(指向自身所在的Page结构体的指针)。

    观察代码可以注意到,许多宏都定义为了do{...} while(0)的形式,定义函数宏有多种方式,例如直接将宏定义为多个语句,或者将宏定义为一个语句块(即由大括号括起的一组语句),也可以像此处一样定义为do-while的形式。这三种各有优缺点。以SWAP为例,第一种方式为:

    1 #define SWAP(a, b, type) type tmp = *a;
    2     *a = *b;
    3     *b = tmp;

    第二种方式为:

    1 #define SWAP(a, b, type) {type tmp = *a;
    2     *a = *b;
    3     *b = tmp;
    4 }

    第三种方式为:

    1 #define SWAP(a, b, type) do{
    2     type tmp = *a;
    3     *a = *b;
    4     *b = tmp;
    5 } while(0)

    第一种的优点是方便,但如果跟在一个没有加大括号的if后边,只有第一个语句属于这个if,无形之间引入问题。第二种语句块整体的值是最后一个语句的值,相当于一个有返回值的函数宏,然而如果跟在一个没有加大括号的if后边,如果加了分号(例如if(a != b) SWAP(a, b, int); else ...),则if的作用域会被分号提前终结,导致在else处报错。第三种没有前两者的问题,但整体无返回值,相当于一个返回值为void的函数宏。

    在C语言中,使用宏函数主要的原因应当是出于性能考虑,使用宏而非普通函数,可以避免函数调用过程中的压栈、退栈、保存上下文的过程,可以有效地提高性能。但宏容易产生二义性,使用内联函数可以继承宏函数的优点,同时规避宏函数的不足:

    inline是C++关键字,在函数声明或定义中,函数返回类型前加上关键字inline,即可以把函数指定为内联函数。这样可以解决一些频繁调用的函数大量消耗栈空间(栈内存)的问题。关键字inline必须与函数定义放在一起才能使函数成为内联函数,仅仅将inline放在函数声明前面不起任何作用。inline是一种“用于实现”的关键字,而不是一种“用于声明”的关键字。

    pmap.h中定义的函数均为内联函数(后文将提到)。

    内存管理相关

    内存管理的核心代码位于./mm/pmap.c./include/pmap.h./include/mmu.h中,分别包含内存相关操作、物理内存布局与页式内存管理的相关代码。

      1 #ifndef _PMAP_H_
      2 #define _PMAP_H_
      3 
      4 #include "types.h"
      5 #include "queue.h"
      6 #include "mmu.h"
      7 #include "printf.h"
      8 
      9 
     10 LIST_HEAD(Page_list, Page);
     11 typedef LIST_ENTRY(Page) Page_LIST_entry_t;
     12 
     13 struct Page {
     14     Page_LIST_entry_t pp_link;    /* free list link */
     15 
     16     // Ref is the count of pointers (usually in page table entries)
     17     // to this page.  This only holds for pages allocated using
     18     // page_alloc.  Pages allocated at boot time using pmap.c's "alloc"
     19     // do not have valid reference count fields.
     20 
     21     u_short pp_ref;
     22 };
     23 
     24 extern struct Page *pages;
     25 
     26 static inline u_long
     27 page2ppn(struct Page *pp)
     28 {
     29     return pp - pages;
     30 }
     31 
     32 /* Get the physical address of Page 'pp'.
     33  */
     34 static inline u_long
     35 page2pa(struct Page *pp)
     36 {
     37     return page2ppn(pp) << PGSHIFT;
     38 }
     39 
     40 /* Get the Page struct whose physical address is 'pa'.
     41  */
     42 static inline struct Page *
     43 pa2page(u_long pa)
     44 {
     45     if (PPN(pa) >= npage) {
     46         panic("pa2page called with invalid pa: %x", pa);
     47     }
     48 
     49     return &pages[PPN(pa)];
     50 }
     51 
     52 /* Get the kernel virtual address of Page 'pp'.
     53  */
     54 static inline u_long
     55 page2kva(struct Page *pp)
     56 {
     57     return KADDR(page2pa(pp));
     58 }
     59 
     60 /* Transform the virtual address 'va' to physical address.
     61  */
     62 static inline u_long
     63 va2pa(Pde *pgdir, u_long va)
     64 {
     65     Pte *p;
     66 
     67     pgdir = &pgdir[PDX(va)];
     68 
     69     if (!(*pgdir & PTE_V)) {
     70         return ~0;
     71     }
     72 
     73     p = (Pte *)KADDR(PTE_ADDR(*pgdir));
     74 
     75     if (!(p[PTX(va)]&PTE_V)) {
     76         return ~0;
     77     }
     78 
     79     return PTE_ADDR(p[PTX(va)]);
     80 }
     81 
     82 /********** functions for memory management(see implementation in mm/pmap.c). ***********/
     83 
     84 void mips_detect_memory();
     85 
     86 void mips_vm_init();
     87 
     88 void mips_init();
     89 void page_init(void);
     90 void page_check();
     91 void physical_memory_manage_check();
     92 int page_alloc(struct Page **pp);
     93 void page_free(struct Page *pp);
     94 void page_decref(struct Page *pp);
     95 int pgdir_walk(Pde *pgdir, u_long va, int create, Pte **ppte);
     96 int page_insert(Pde *pgdir, struct Page *pp, u_long va, u_int perm);
     97 struct Page *page_lookup(Pde *pgdir, u_long va, Pte **ppte);
     98 void page_remove(Pde *pgdir, u_long va) ;
     99 void tlb_invalidate(Pde *pgdir, u_long va);
    100 
    101 void boot_map_segment(Pde *pgdir, u_long va, u_long size, u_long pa, int perm);
    102 
    103 extern struct Page *pages;
    104 
    105 
    106 #endif /* _PMAP_H_ */
    ./include/pmap.h(已折叠)

    pmap.h分为三个部分:变量与数据结构的声明、一些内联函数的定义与pmap.c中函数的定义。

    Page的结构已经在上一节给出,其pp_link域链接到其他的Page对象,用于空闲链表中,pp_ref记录该页面被引用的次数。

    pmap.h中定义的各个函数功能为:

    • static inline u_long page2ppn(struct Page *pp):由页面得到对应的物理页号,因为Page对象在内存中是连续分布的所以可以直接减去首地址得到。
    • static inline u_long page2pa(struct Page *pp):由页面得到其对应的物理地址,也就是页大小✖物理页号。
    • static inline struct Page * pa2page(u_long pa):由物理地址得到物理页面,是page2pa的逆操作。
    • static inline u_long page2kva(struct Page *pp):获得物理页面的内核态虚拟地址,也就是物理地址+ULIM。
    • static inline u_long va2pa(Pde *pgdir, u_long va):返回页表pgdir中虚拟地址va对应的物理地址。
      1 #ifndef _MMU_H_
      2 #define _MMU_H_
      3 
      4 
      5 /*
      6  * This file contains:
      7  *
      8  *    Part 1.  MIPS definitions.
      9  *    Part 2.  Our conventions.
     10  *    Part 3.  Our helper functions.
     11  */
     12 
     13 /*
     14  * Part 1.  MIPS definitions.
     15  */
     16 #define BY2PG        4096        // bytes to a page
     17 #define PDMAP        (4*1024*1024)    // bytes mapped by a page directory entry
     18 #define PGSHIFT        12
     19 #define PDSHIFT        22        // log2(PDMAP)
     20 #define PDX(va)        ((((u_long)(va))>>22) & 0x03FF)
     21 #define PTX(va)        ((((u_long)(va))>>12) & 0x03FF)
     22 #define PTE_ADDR(pte)    ((u_long)(pte)&~0xFFF)
     23 
     24 // page number field of address
     25 #define PPN(va)        (((u_long)(va))>>12)
     26 #define VPN(va)        PPN(va)
     27 
     28 #define VA2PFN(va)        (((u_long)(va)) & 0xFFFFF000 ) // va 2 PFN for EntryLo0/1
     29 #define PTE2PT        1024
     30 //$#define VA2PDE(va)        (((u_long)(va)) & 0xFFC00000 ) // for context
     31 
     32 /* Page Table/Directory Entry flags
     33  *   these are defined by the hardware
     34  */
     35 #define PTE_G        0x0100    // Global bit
     36 #define PTE_V        0x0200    // Valid bit
     37 #define PTE_R        0x0400    // Dirty bit ,'0' means only read ,otherwise make interrupt
     38 #define PTE_D        0x0002    // fileSystem Cached is dirty
     39 #define PTE_COW        0x0001    // Copy On Write
     40 #define PTE_UC        0x0800    // unCached
     41 #define PTE_LIBRARY        0x0004    // share memmory
     42 /*
     43  * Part 2.  Our conventions.
     44  */
     45 
     46 /*
     47  o     4G ----------->  +----------------------------+------------0x100000000
     48  o                      |       ...                  |  kseg3
     49  o                      +----------------------------+------------0xe000 0000
     50  o                      |       ...                  |  kseg2
     51  o                      +----------------------------+------------0xc000 0000
     52  o                      |   Interrupts & Exception   |  kseg1
     53  o                      +----------------------------+------------0xa000 0000
     54  o                      |      Invalid memory        |   /|
     55  o                      +----------------------------+----|-------Physics Memory Max
     56  o                      |       ...                  |  kseg0
     57  o  VPT,KSTACKTOP-----> +----------------------------+----|-------0x8040 0000-------end
     58  o                      |       Kernel Stack         |    | KSTKSIZE            /|
     59  o                      +----------------------------+----|------                |
     60  o                      |       Kernel Text          |    |                    PDMAP
     61  o      KERNBASE -----> +----------------------------+----|-------0x8001 0000    |
     62  o                      |   Interrupts & Exception   |   |/                    |/
     63  o      ULIM     -----> +----------------------------+------------0x8000 0000-------
     64  o                      |         User VPT           |     PDMAP                /|
     65  o      UVPT     -----> +----------------------------+------------0x7fc0 0000    |
     66  o                      |         PAGES              |     PDMAP                 |
     67  o      UPAGES   -----> +----------------------------+------------0x7f80 0000    |
     68  o                      |         ENVS               |     PDMAP                 |
     69  o  UTOP,UENVS   -----> +----------------------------+------------0x7f40 0000    |
     70  o  UXSTACKTOP -/       |     user exception stack   |     BY2PG                 |
     71  o                      +----------------------------+------------0x7f3f f000    |
     72  o                      |       Invalid memory       |     BY2PG                 |
     73  o      USTACKTOP ----> +----------------------------+------------0x7f3f e000    |
     74  o                      |     normal user stack      |     BY2PG                 |
     75  o                      +----------------------------+------------0x7f3f d000    |
     76  a                      |                            |                           |
     77  a                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                           |
     78  a                      .                            .                           |
     79  a                      .                            .                         kuseg
     80  a                      .                            .                           |
     81  a                      |~~~~~~~~~~~~~~~~~~~~~~~~~~~~|                           |
     82  a                      |                            |                           |
     83  o       UTEXT   -----> +----------------------------+                           |
     84  o                      |                            |     2 * PDMAP            |/
     85  a     0 ------------>  +----------------------------+ -----------------------------
     86  o
     87 */
     88 
     89 #define KERNBASE 0x80010000
     90 
     91 #define VPT (ULIM + PDMAP )
     92 #define KSTACKTOP (VPT-0x100)
     93 #define KSTKSIZE (8*BY2PG)
     94 #define ULIM 0x80000000
     95 
     96 #define UVPT (ULIM - PDMAP)
     97 #define UPAGES (UVPT - PDMAP)
     98 #define UENVS (UPAGES - PDMAP)
     99 
    100 #define UTOP UENVS
    101 #define UXSTACKTOP (UTOP)
    102 #define TIMESTACK 0x82000000
    103 
    104 #define USTACKTOP (UTOP - 2*BY2PG)
    105 #define UTEXT 0x00400000
    106 
    107 
    108 #define E_UNSPECIFIED    1    // Unspecified or unknown problem
    109 #define E_BAD_ENV       2       // Environment doesn't exist or otherwise
    110 // cannot be used in requested action
    111 #define E_INVAL        3    // Invalid parameter
    112 #define E_NO_MEM    4    // Request failed due to memory shortage
    113 #define E_NO_FREE_ENV   5       // Attempt to create a new environment beyond
    114 // the maximum allowed
    115 #define E_IPC_NOT_RECV  6    // Attempt to send to env that is not recving.
    116 
    117 // File system error codes -- only seen in user-level
    118 #define    E_NO_DISK    7    // No free space left on disk
    119 #define E_MAX_OPEN    8    // Too many files are open
    120 #define E_NOT_FOUND    9     // File or block not found
    121 #define E_BAD_PATH    10    // Bad path
    122 #define E_FILE_EXISTS    11    // File already exists
    123 #define E_NOT_EXEC    12    // File not a valid executable
    124 
    125 #define MAXERROR 12
    126 
    127 #ifndef __ASSEMBLER__
    128 
    129 /*
    130  * Part 3.  Our helper functions.
    131  */
    132 #include "types.h"
    133 void bcopy(const void *, void *, size_t);
    134 void bzero(void *, size_t);
    135 
    136 extern char bootstacktop[], bootstack[];
    137 
    138 extern u_long npage;
    139 
    140 typedef u_long Pde;
    141 typedef u_long Pte;
    142 
    143 extern volatile Pte *vpt[];
    144 extern volatile Pde *vpd[];
    145 
    146 // translates from kernel virtual address to physical address.
    147 #define PADDR(kva)                        
    148     ({                                
    149         u_long a = (u_long) (kva);                
    150         if (a < ULIM)                    
    151             panic("PADDR called with invalid kva %08lx", a);
    152         a - ULIM;                        
    153     })
    154 
    155 // translates from physical address to kernel virtual address.
    156 #define KADDR(pa)                        
    157     ({                                
    158         u_long ppn = PPN(pa);                    
    159         if (ppn >= npage)                    
    160             panic("KADDR called with invalid pa %08lx", (u_long)pa);
    161         (pa) + ULIM;                    
    162     })
    163 
    164 #define assert(x)    
    165     do {    if (!(x)) panic("assertion failed: %s", #x); } while (0)
    166 
    167 #define TRUP(_p)                           
    168     ({                                
    169         register typeof((_p)) __m_p = (_p);            
    170         (u_int) __m_p > ULIM ? (typeof(_p)) ULIM : __m_p;    
    171     })
    172 
    173 
    174 extern void tlb_out(u_int entryhi);
    175 
    176 #endif //!__ASSEMBLER__
    177 #endif // !_MMU_H_
    ./include/mmu.h(已折叠)

    mmu.h同样分为三部分:MIPS中的定义、操作系统规定的内存分布和错误码以及一些实用的函数。

    第一部分代码及其解释如下:

     1 #define BY2PG        4096             // 页面大小的Byte数,一个页面大小为4kb
     2 #define PDMAP        (4*1024*1024)    // 一个页表管理1024个页面,大小总共4*1024*1024Byte
     3 #define PGSHIFT        12             // 页面的偏移位数,4kb对应12位
     4 #define PDSHIFT        22        // 页表的偏移位数,同上,即log2(PDMAP)
     5 #define PDX(va)        ((((u_long)(va))>>22) & 0x03FF) // 取虚拟地址高10位,为页目录号
     6 #define PTX(va)        ((((u_long)(va))>>12) & 0x03FF) // 取虚拟地址高11~20位,为页表号
     7 #define PTE_ADDR(pte)    ((u_long)(pte)&~0xFFF) // 页表项取低12位,为页内偏移
     8 
     9 // page number field of address
    10 #define PPN(va)        (((u_long)(va))>>12) // 物理页号,为虚拟地址偏移12位
    11 #define VPN(va)        PPN(va) // 虚页号
    12 
    13 #define VA2PFN(va)        (((u_long)(va)) & 0xFFFFF000 )
    14 #define PTE2PT        1024
    15 //$#define VA2PDE(va)        (((u_long)(va)) & 0xFFC00000 )
    16 
    17 /* Page Table/Directory Entry flags
    18  *   these are defined by the hardware
    19  */
    20 #define PTE_G        0x0100    // 全局位
    21 #define PTE_V        0x0200    // 有效位
    22 #define PTE_R        0x0400    // 修改位,如果是0表示只对该页面进行过读操作,否则进行过写操作,要引发中断将内容写回内存
    23 #define PTE_D        0x0002    // 文件缓存的修改位dirty
    24 #define PTE_COW        0x0001    // 写时复制copy on write
    25 #define PTE_UC        0x0800    // 未缓存uncached
    26 #define PTE_LIBRARY        0x0004    // 共享内存

    前数行是对虚拟地址的一些操作,最后是页表项中的一些标记位,通过位运算可以修改或取出标记位。(最后四个标记位此处不用,在文件系统中使用)

    第二部分首先是一个内存布局图(见上方完整代码)。从图中可见,内存的低2G空间是用户段,用户代码段并不是从0地址开始,而是在保留了两页之后的地址开始。保留的两页大小是为了向前兼容一些古老的操作系统,它们在运行时要使用这一部分内存。然后从下向上依次是用户栈、不可用区、用户中断时栈、进程控制块、页表、VPT(virtual page table?意义暂时不明)。再以上是内核段。

    其余的一些定义为(异常码的解释略去):

     1 #define KERNBASE 0x80010000 // 内核基地址
     2 
     3 #define VPT (ULIM + PDMAP ) //
     4 #define KSTACKTOP (VPT-0x100) // 内核栈顶
     5 #define KSTKSIZE (8*BY2PG) // 内核栈大小
     6 #define ULIM 0x80000000 // 用户态地址上限
     7 
     8 #define UVPT (ULIM - PDMAP) // 
     9 #define UPAGES (UVPT - PDMAP) // 用户页表
    10 #define UENVS (UPAGES - PDMAP) // 用户进程控制块
    11 
    12 #define UTOP UENVS // 用户态高地址
    13 #define UXSTACKTOP (UTOP) // 用户态异常栈
    14 #define TIMESTACK 0x82000000 // 上下文保存栈
    15 
    16 #define USTACKTOP (UTOP - 2*BY2PG) // 用户栈
    17 #define UTEXT 0x00400000 // 用户代码段

    最后是一些函数与函数宏:

    • void bcopy(const void *, void *, size_t):内存拷贝
    • void bzero(void *, size_t):内存清空
    • PADDR(kva):内核虚拟地址映射到物理地址(直接清空高位)。
    • KADDR(pa):物理地址映射到内核虚拟地址。
    • assert(x):支持断言机制。
    • TRUP(_p):相当于min(_p, ULIM),似乎是为了防止用户读写内核段内存。

    pmap.c代码如下,❗❗长代码段预警❗❗+❗❗剧透预警❗❗

      1 #include "mmu.h"
      2 #include "pmap.h"
      3 #include "printf.h"
      4 #include "env.h"
      5 #include "error.h"
      6 
      7 
      8 /* These variables are set by mips_detect_memory() */
      9 u_long maxpa;            /* Maximum physical address */
     10 u_long npage;            /* Amount of memory(in pages) */
     11 u_long basemem;          /* Amount of base memory(in bytes) */
     12 u_long extmem;           /* Amount of extended memory(in bytes) */
     13 
     14 Pde *boot_pgdir;
     15 
     16 struct Page *pages;
     17 static u_long freemem;
     18 
     19 static struct Page_list page_free_list; /* Free list of physical pages */
     20 
     21 
     22 /* Overview:
     23         Initialize basemem and npage.
     24         Set basemem to be 64MB, and calculate corresponding npage value.*/
     25 
     26 void mips_detect_memory()
     27 {
     28     /* Step 1: Initialize basemem.
     29      * (When use real computer, CMOS tells us how many kilobytes there are). */
     30          maxpa = 0x4000000;
     31          basemem = 0x4000000;
     32          npage = 0x4000;
     33          extmem = 0x0;
     34 
     35     // Step 2: Calculate corresponding npage value.
     36     printf("Physical memory: %dK available, ", (int)(maxpa / 1024));
     37     printf("base = %dK, extended = %dK
    ", (int)(basemem / 1024),
     38            (int)(extmem / 1024));
     39 }
     40 
     41 /* Overview:
     42         Allocate `n` bytes physical memory with alignment `align`, if `clear` is set, clear the
     43         allocated memory.
     44         This allocator is used only while setting up virtual memory system.
     45 
     46    Post-Condition:
     47         If we're out of memory, should panic, else return this address of memory we have allocated.*/
     48 static void *alloc(u_int n, u_int align, int clear)
     49 {
     50     extern char end[];
     51     u_long alloced_mem;
     52 
     53     /* Initialize `freemem` if this is the first time. The first virtual address that the
     54      * linker did *not* assign to any kernel code or global variables. */
     55     if (freemem == 0) {
     56         freemem = (u_long)end;
     57     }
     58 
     59     /* Step 1: Round up `freemem` up to be aligned properly */
     60     freemem = ROUND(freemem, align);
     61 
     62     /* Step 2: Save current value of `freemem` as allocated chunk. */
     63     alloced_mem = freemem;
     64 
     65     /* Step 3: Increase `freemem` to record allocation. */
     66     freemem = freemem + n;
     67 
     68     /* Step 4: Clear allocated chunk if parameter `clear` is set. */
     69     if (clear) {
     70         bzero((void *)alloced_mem, n);
     71     }
     72 
     73     // We're out of memory, PANIC !!
     74     if (PADDR(freemem) >= maxpa) {
     75         panic("out of memorty
    ");
     76         return (void *)-E_NO_MEM;
     77     }
     78 
     79     /* Step 5: return allocated chunk. */
     80     return (void *)alloced_mem;
     81 }
     82 
     83 /* Overview:
     84         Get the page table entry for virtual address `va` in the given
     85         page directory `pgdir`.
     86         If the page table is not exist and the parameter `create` is set to 1,
     87         then create it.*/
     88 static Pte *boot_pgdir_walk(Pde *pgdir, u_long va, int create)
     89 {
     90 
     91     Pde *pgdir_entryp;
     92     Pte *pgtable, *pgtable_entry;
     93 
     94     /* Step 1: Get the corresponding page directory entry and page table. */
     95     /* Hint: Use KADDR and PTE_ADDR to get the page table from page directory
     96      * entry value. */
     97         pgdir_entryp = pgdir + PDX(va);
     98 
     99     /* Step 2: If the corresponding page table is not exist and parameter `create`
    100      * is set, create one. And set the correct permission bits for this new page
    101      * table. */
    102         if ((*pgdir_entryp & PTE_V) == 0) {
    103                 if (create) {
    104                         *pgdir_entryp = PADDR(alloc(BY2PG, BY2PG, 1));
    105                         *pgdir_entryp = *pgdir_entryp | PTE_V | PTE_R;
    106                 } else {
    107                         return 0;
    108                 }
    109         }
    110         pgtable = (Pte*) KADDR(PTE_ADDR(*pgdir_entryp));
    111 
    112     /* Step 3: Get the page table entry for `va`, and return it. */
    113         pgtable_entry = pgtable + PTX(va);
    114         return pgtable_entry;
    115 }
    116 
    117 /*Overview:
    118         Map [va, va+size) of virtual address space to physical [pa, pa+size) in the page
    119         table rooted at pgdir.
    120         Use permission bits `perm|PTE_V` for the entries.
    121         Use permission bits `perm` for the entries.
    122 
    123   Pre-Condition:
    124         Size is a multiple of BY2PG.*/
    125 void boot_map_segment(Pde *pgdir, u_long va, u_long size, u_long pa, int perm)
    126 {
    127     int i, va_temp;
    128         u_long rsize;
    129         u_int PERM;
    130     Pte *pgtable_entry;
    131 
    132         PERM = perm | PTE_V;
    133 
    134     /* Step 1: Check if `size` is a multiple of BY2PG. */
    135         rsize = ROUND(size, BY2PG);
    136 
    137     /* Step 2: Map virtual address space to physical address. */
    138     /* Hint: Use `boot_pgdir_walk` to get the page table entry of virtual address `va`. */
    139         for (i = 0; i < rsize; i = i + BY2PG) {
    140                 pgtable_entry = boot_pgdir_walk(pgdir, va+i, 1);
    141                 *pgtable_entry = PTE_ADDR(pa) | PERM;
    142         }
    143 
    144 }
    145 
    146 /* Overview:
    147     Set up two-level page table.
    148 
    149    Hint:
    150     You can get more details about `UPAGES` and `UENVS` in include/mmu.h. */
    151 void mips_vm_init()
    152 {
    153     extern char end[];
    154     extern int mCONTEXT;
    155     extern struct Env *envs;
    156 
    157     Pde *pgdir;
    158     u_int n;
    159 
    160     /* Step 1: Allocate a page for page directory(first level page table). */
    161     pgdir = alloc(BY2PG, BY2PG, 1);
    162     printf("to memory %x for struct page directory.
    ", freemem);
    163     mCONTEXT = (int)pgdir;
    164 
    165     boot_pgdir = pgdir;
    166 
    167     /* Step 2: Allocate proper size of physical memory for global array `pages`,
    168      * for physical memory management. Then, map virtual address `UPAGES` to
    169      * physical address `pages` allocated before. For consideration of alignment,
    170      * you should round up the memory size before map. */
    171     pages = (struct Page *)alloc(npage * sizeof(struct Page), BY2PG, 1);
    172     printf("to memory %x for struct Pages.
    ", freemem);
    173     n = ROUND(npage * sizeof(struct Page), BY2PG);
    174     boot_map_segment(pgdir, UPAGES, n, PADDR(pages), PTE_R);
    175 
    176     /* Step 3, Allocate proper size of physical memory for global array `envs`,
    177      * for process management. Then map the physical address to `UENVS`. */
    178     envs = (struct Env *)alloc(NENV * sizeof(struct Env), BY2PG, 1);
    179     n = ROUND(NENV * sizeof(struct Env), BY2PG);
    180     boot_map_segment(pgdir, UENVS, n, PADDR(envs), PTE_R);
    181 
    182     printf("pmap.c:	 mips vm init success
    ");
    183 }
    184 
    185 /*Overview:
    186         Initialize page structure and memory free list.
    187         The `pages` array has one `struct Page` entry per physical page. Pages
    188         are reference counted, and free pages are kept on a linked list.
    189   Hint:
    190         Use `LIST_INSERT_HEAD` to insert something to list.*/
    191 void
    192 page_init(void)
    193 {
    194         struct Page* page;
    195     /* Step 1: Initialize page_free_list. */
    196     /* Hint: Use macro `LIST_INIT` defined in include/queue.h. */
    197         LIST_INIT(&page_free_list);
    198 
    199     /* Step 2: Align `freemem` up to multiple of BY2PG. */
    200         freemem = ROUND(freemem, BY2PG);
    201 
    202     /* Step 3: Mark all memory below `freemem` as used(set `pp_ref`
    203      * filed to 1) */
    204         for (page = pages; page2kva(page) < freemem; page++) {
    205                 page -> pp_ref = 1;
    206         }
    207 
    208     /* Step 4: Mark the other memory as free. */
    209         for (page = (pages + PPN(PADDR(freemem))); page2ppn(page) < npage; page++) {
    210                 page -> pp_ref = 0;
    211                 LIST_INSERT_HEAD(&page_free_list, page, pp_link);
    212         }
    213 }
    214 
    215 /*Overview:
    216         Allocates a physical page from free memory, and clear this page.
    217 
    218   Post-Condition:
    219         If failed to allocate a new page(out of memory(there's no free page)),
    220         return -E_NO_MEM.
    221         Else, set the address of allocated page to *pp, and returned 0.
    222 
    223   Note:
    224         Does NOT increment the reference count of the page - the caller must do
    225         these if necessary (either explicitly or via page_insert).
    226 
    227   Hint:
    228         Use LIST_FIRST and LIST_REMOVE defined in include/queue.h .*/
    229 int
    230 page_alloc(struct Page **pp)
    231 {
    232     struct Page *ppage_temp;
    233 
    234     /* Step 1: Get a page from free memory. If fails, return the error code.*/
    235         if (LIST_EMPTY(&page_free_list)) {
    236                 return -E_NO_MEM;
    237         }
    238         ppage_temp = LIST_FIRST(&page_free_list);
    239         LIST_REMOVE(ppage_temp, pp_link);
    240 
    241     /* Step 2: Initialize this page.
    242      * Hint: use `bzero`. */
    243          bzero(page2kva(ppage_temp), BY2PG);
    244          *pp = ppage_temp;
    245          return 0;
    246 }
    247 
    248 /*Overview:
    249         Release a page, mark it as free if it's `pp_ref` reaches 0.
    250   Hint:
    251         When to free a page, just insert it to the page_free_list.*/
    252 void
    253 page_free(struct Page *pp)
    254 {
    255     /* Step 1: If there's still virtual address refers to this page, do nothing. */
    256         if (pp -> pp_ref > 0) {
    257                 return;
    258         }
    259 
    260     /* Step 2: If the `pp_ref` reaches to 0, mark this page as free and return. */
    261         if (pp -> pp_ref == 0) {
    262                 LIST_INSERT_HEAD(&page_free_list, pp, pp_link);
    263                 return;
    264         }
    265 
    266     /* If the value of `pp_ref` less than 0, some error must occurred before,
    267      * so PANIC !!! */
    268     panic("cgh:pp->pp_ref is less than zero
    ");
    269 }
    270 
    271 /*Overview:
    272         Given `pgdir`, a pointer to a page directory, pgdir_walk returns a pointer
    273         to the page table entry (with permission PTE_R|PTE_V) for virtual address 'va'.
    274 
    275   Pre-Condition:
    276         The `pgdir` should be two-level page table structure.
    277 
    278   Post-Condition:
    279         If we're out of memory, return -E_NO_MEM.
    280         Else, we get the page table entry successfully, store the value of page table
    281         entry to *ppte, and return 0, indicating success.
    282 
    283   Hint:
    284         We use a two-level pointer to store page table entry and return a state code to indicate
    285         whether this function execute successfully or not.
    286     This function have something in common with function `boot_pgdir_walk`.*/
    287 int
    288 pgdir_walk(Pde *pgdir, u_long va, int create, Pte **ppte)
    289 {
    290     Pde *pgdir_entryp;
    291     Pte *pgtable;
    292     struct Page *ppage;
    293 
    294     /* Step 1: Get the corresponding page directory entry and page table. */
    295         pgdir_entryp = pgdir + PDX(va);
    296 
    297     /* Step 2: If the corresponding page table is not exist(valid) and parameter `create`
    298      * is set, create one. And set the correct permission bits for this new page
    299      * table.
    300      * When creating new page table, maybe out of memory. */
    301         if ((*pgdir_entryp & PTE_V) == 0) {
    302                 if (create) {
    303                         if (page_alloc(&ppage) == -E_NO_MEM) {
    304                                 return -E_NO_MEM;
    305                         } else {
    306                                 *pgdir_entryp = page2pa(ppage);
    307                                 *pgdir_entryp = *pgdir_entryp | PTE_V | PTE_R;
    308                                 ppage->pp_ref++;
    309                         }
    310                 } else {
    311                         *ppte = 0;
    312                         return 0;
    313                 }
    314         }
    315         pgtable = (Pte*) KADDR(PTE_ADDR(*pgdir_entryp));
    316 
    317     /* Step 3: Set the page table entry to `*ppte` as return value. */
    318         *ppte = pgtable + PTX(va);
    319 
    320     return 0;
    321 }
    322 
    323 /*Overview:
    324         Map the physical page 'pp' at virtual address 'va'.
    325         The permissions (the low 12 bits) of the page table entry should be set to 'perm|PTE_V'.
    326 
    327   Post-Condition:
    328     Return 0 on success
    329     Return -E_NO_MEM, if page table couldn't be allocated
    330 
    331   Hint:
    332         If there is already a page mapped at `va`, call page_remove() to release this mapping.
    333         The `pp_ref` should be incremented if the insertion succeeds.*/
    334 int
    335 page_insert(Pde *pgdir, struct Page *pp, u_long va, u_int perm)
    336 {
    337     u_int PERM;
    338     Pte *pgtable_entry;
    339     PERM = perm | PTE_V;
    340 
    341     /* Step 1: Get corresponding page table entry. */
    342     pgdir_walk(pgdir, va, 0, &pgtable_entry);
    343 
    344     if (pgtable_entry != 0 && (*pgtable_entry & PTE_V) != 0) {
    345         if (pa2page(*pgtable_entry) != pp) {
    346             page_remove(pgdir, va);
    347         } else  {
    348             tlb_invalidate(pgdir, va);
    349             *pgtable_entry = page2pa(pp) | PERM;
    350             return 0;
    351         }
    352     }
    353 
    354     /* Step 2: Update TLB. */
    355 
    356     /* hint: use tlb_invalidate function */
    357         tlb_invalidate(pgdir, va);
    358 
    359 
    360     /* Step 3: Do check, re-get page table entry to validate the insertion. */
    361 
    362     /* Step 3.1 Check if the page can be insert, if can’t return -E_NO_MEM */
    363         if (pgdir_walk(pgdir, va, 1, &pgtable_entry) != 0) {
    364                 return -E_NO_MEM;
    365         }
    366 
    367     /* Step 3.2 Insert page and increment the pp_ref */
    368         *pgtable_entry = page2pa(pp) | PERM;
    369         pp->pp_ref = pp->pp_ref + 1;
    370     return 0;
    371 }
    372 
    373 /*Overview:
    374         Look up the Page that virtual address `va` map to.
    375 
    376   Post-Condition:
    377         Return a pointer to corresponding Page, and store it's page table entry to *ppte.
    378         If `va` doesn't mapped to any Page, return NULL.*/
    379 struct Page *
    380 page_lookup(Pde *pgdir, u_long va, Pte **ppte)
    381 {
    382     struct Page *ppage;
    383     Pte *pte;
    384 
    385     /* Step 1: Get the page table entry. */
    386     pgdir_walk(pgdir, va, 0, &pte);
    387 
    388     /* Hint: Check if the page table entry doesn't exist or is not valid. */
    389     if (pte == 0) {
    390         return 0;
    391     }
    392     if ((*pte & PTE_V) == 0) {
    393         return 0;    //the page is not in memory.
    394     }
    395 
    396     /* Step 2: Get the corresponding Page struct. */
    397 
    398     /* Hint: Use function `pa2page`, defined in include/pmap.h . */
    399     ppage = pa2page(*pte);
    400     if (ppte) {
    401         *ppte = pte;
    402     }
    403 
    404     return ppage;
    405 }
    406 
    407 // Overview:
    408 //      Decrease the `pp_ref` value of Page `*pp`, if `pp_ref` reaches to 0, free this page.
    409 void page_decref(struct Page *pp) {
    410     if(--pp->pp_ref == 0) {
    411         page_free(pp);
    412     }
    413 }
    414 
    415 // Overview:
    416 //      Unmaps the physical page at virtual address `va`.
    417 void
    418 page_remove(Pde *pgdir, u_long va)
    419 {
    420     Pte *pagetable_entry;
    421     struct Page *ppage;
    422 
    423     /* Step 1: Get the page table entry, and check if the page table entry is valid. */
    424     ppage = page_lookup(pgdir, va, &pagetable_entry);
    425 
    426     if (ppage == 0) {
    427         return;
    428     }
    429 
    430     /* Step 2: Decrease `pp_ref` and decide if it's necessary to free this page. */
    431 
    432     /* Hint: When there's no virtual address mapped to this page, release it. */
    433     ppage->pp_ref--;
    434     if (ppage->pp_ref == 0) {
    435         page_free(ppage);
    436     }
    437 
    438     /* Step 3: Update TLB. */
    439     *pagetable_entry = 0;
    440     tlb_invalidate(pgdir, va);
    441     return;
    442 }
    443 
    444 // Overview:
    445 //      Update TLB.
    446 void
    447 tlb_invalidate(Pde *pgdir, u_long va)
    448 {
    449     if (curenv) {
    450         tlb_out(PTE_ADDR(va) | GET_ENV_ASID(curenv->env_id));
    451     } else {
    452         tlb_out(PTE_ADDR(va));
    453     }
    454 }
    455 
    456 void
    457 physical_memory_manage_check(void)
    458 {
    459     struct Page *pp, *pp0, *pp1, *pp2;
    460     struct Page_list fl;
    461     int *temp;
    462 
    463     // should be able to allocate three pages
    464     pp0 = pp1 = pp2 = 0;
    465     assert(page_alloc(&pp0) == 0);
    466     assert(page_alloc(&pp1) == 0);
    467     assert(page_alloc(&pp2) == 0);
    468 
    469     assert(pp0);
    470     assert(pp1 && pp1 != pp0);
    471     assert(pp2 && pp2 != pp1 && pp2 != pp0);
    472 
    473 
    474 
    475     // temporarily steal the rest of the free pages
    476     fl = page_free_list;
    477     // now this page_free list must be empty!!!!
    478     LIST_INIT(&page_free_list);
    479     // should be no free memory
    480     assert(page_alloc(&pp) == -E_NO_MEM);
    481 
    482     temp = (int*)page2kva(pp0);
    483     //write 1000 to pp0
    484     *temp = 1000;
    485     // free pp0
    486     page_free(pp0);
    487     printf("The number in address temp is %d
    ",*temp);
    488 
    489     // alloc again
    490     assert(page_alloc(&pp0) == 0);
    491     assert(pp0);
    492 
    493     // pp0 should not change
    494     assert(temp == (int*)page2kva(pp0));
    495     // pp0 should be zero
    496     assert(*temp == 0);
    497 
    498     page_free_list = fl;
    499     page_free(pp0);
    500     page_free(pp1);
    501     page_free(pp2);
    502     struct Page_list test_free;
    503     struct Page *test_pages;
    504         test_pages= (struct Page *)alloc(10 * sizeof(struct Page), BY2PG, 1);
    505         LIST_INIT(&test_free);
    506         //LIST_FIRST(&test_free) = &test_pages[0];
    507         int i,j=0;
    508         struct Page *p, *q;
    509         //test inert tail
    510         for(i=0;i<10;i++) {
    511                 test_pages[i].pp_ref=i;
    512                 //test_pages[i].pp_link=NULL;
    513                 //printf("0x%x  0x%x
    ",&test_pages[i], test_pages[i].pp_link.le_next);
    514                 LIST_INSERT_TAIL(&test_free,&test_pages[i],pp_link);
    515                 //printf("0x%x  0x%x
    ",&test_pages[i], test_pages[i].pp_link.le_next);
    516         }
    517         p = LIST_FIRST(&test_free);
    518         int answer1[]={0,1,2,3,4,5,6,7,8,9};
    519         assert(p!=NULL);
    520         while(p!=NULL)
    521         {
    522                 //printf("%d %d
    ",p->pp_ref,answer1[j]);
    523                 assert(p->pp_ref==answer1[j++]);
    524                 //printf("ptr: 0x%x v: %d
    ",(p->pp_link).le_next,((p->pp_link).le_next)->pp_ref);
    525                 p=LIST_NEXT(p,pp_link);
    526 
    527         }
    528         // insert_after test
    529         int answer2[]={0,1,2,3,4,20,5,6,7,8,9};
    530         q=(struct Page *)alloc(sizeof(struct Page), BY2PG, 1);
    531         q->pp_ref = 20;
    532 
    533         //printf("---%d
    ",test_pages[4].pp_ref);
    534         LIST_INSERT_AFTER(&test_pages[4], q, pp_link);
    535         //printf("---%d
    ",LIST_NEXT(&test_pages[4],pp_link)->pp_ref);
    536         p = LIST_FIRST(&test_free);
    537         j=0;
    538         //printf("into test
    ");
    539         while(p!=NULL){
    540         //      printf("%d %d
    ",p->pp_ref,answer2[j]);
    541                         assert(p->pp_ref==answer2[j++]);
    542                         p=LIST_NEXT(p,pp_link);
    543         }
    544 
    545 
    546 
    547     printf("physical_memory_manage_check() succeeded
    ");
    548 }
    549 
    550 
    551 void
    552 page_check(void)
    553 {
    554     struct Page *pp, *pp0, *pp1, *pp2;
    555     struct Page_list fl;
    556 
    557     // should be able to allocate three pages
    558     pp0 = pp1 = pp2 = 0;
    559     assert(page_alloc(&pp0) == 0);
    560     assert(page_alloc(&pp1) == 0);
    561     assert(page_alloc(&pp2) == 0);
    562 
    563     assert(pp0);
    564     assert(pp1 && pp1 != pp0);
    565     assert(pp2 && pp2 != pp1 && pp2 != pp0);
    566 
    567     // temporarily steal the rest of the free pages
    568     fl = page_free_list;
    569     // now this page_free list must be empty!!!!
    570     LIST_INIT(&page_free_list);
    571 
    572     // should be no free memory
    573     assert(page_alloc(&pp) == -E_NO_MEM);
    574 
    575     // there is no free memory, so we can't allocate a page table
    576     assert(page_insert(boot_pgdir, pp1, 0x0, 0) < 0);
    577 
    578     // free pp0 and try again: pp0 should be used for page table
    579     page_free(pp0);
    580     assert(page_insert(boot_pgdir, pp1, 0x0, 0) == 0);
    581     assert(PTE_ADDR(boot_pgdir[0]) == page2pa(pp0));
    582 
    583     printf("va2pa(boot_pgdir, 0x0) is %x
    ",va2pa(boot_pgdir, 0x0));
    584     printf("page2pa(pp1) is %x
    ",page2pa(pp1));
    585 
    586     assert(va2pa(boot_pgdir, 0x0) == page2pa(pp1));
    587     assert(pp1->pp_ref == 1);
    588 
    589     // should be able to map pp2 at BY2PG because pp0 is already allocated for page table
    590     assert(page_insert(boot_pgdir, pp2, BY2PG, 0) == 0);
    591     assert(va2pa(boot_pgdir, BY2PG) == page2pa(pp2));
    592     assert(pp2->pp_ref == 1);
    593 
    594     // should be no free memory
    595     assert(page_alloc(&pp) == -E_NO_MEM);
    596 
    597     printf("start page_insert
    ");
    598     // should be able to map pp2 at BY2PG because it's already there
    599     assert(page_insert(boot_pgdir, pp2, BY2PG, 0) == 0);
    600     assert(va2pa(boot_pgdir, BY2PG) == page2pa(pp2));
    601     assert(pp2->pp_ref == 1);
    602 
    603     // pp2 should NOT be on the free list
    604     // could happen in ref counts are handled sloppily in page_insert
    605     assert(page_alloc(&pp) == -E_NO_MEM);
    606 
    607     // should not be able to map at PDMAP because need free page for page table
    608     assert(page_insert(boot_pgdir, pp0, PDMAP, 0) < 0);
    609 
    610     // insert pp1 at BY2PG (replacing pp2)
    611     assert(page_insert(boot_pgdir, pp1, BY2PG, 0) == 0);
    612 
    613     // should have pp1 at both 0 and BY2PG, pp2 nowhere, ...
    614     assert(va2pa(boot_pgdir, 0x0) == page2pa(pp1));
    615     assert(va2pa(boot_pgdir, BY2PG) == page2pa(pp1));
    616     // ... and ref counts should reflect this
    617     assert(pp1->pp_ref == 2);
    618     printf("pp2->pp_ref %d
    ",pp2->pp_ref);
    619     assert(pp2->pp_ref == 0);
    620     printf("end page_insert
    ");
    621 
    622     // pp2 should be returned by page_alloc
    623     assert(page_alloc(&pp) == 0 && pp == pp2);
    624 
    625     // unmapping pp1 at 0 should keep pp1 at BY2PG
    626     page_remove(boot_pgdir, 0x0);
    627     assert(va2pa(boot_pgdir, 0x0) == ~0);
    628     assert(va2pa(boot_pgdir, BY2PG) == page2pa(pp1));
    629     assert(pp1->pp_ref == 1);
    630     assert(pp2->pp_ref == 0);
    631 
    632     // unmapping pp1 at BY2PG should free it
    633     page_remove(boot_pgdir, BY2PG);
    634     assert(va2pa(boot_pgdir, 0x0) == ~0);
    635     assert(va2pa(boot_pgdir, BY2PG) == ~0);
    636     assert(pp1->pp_ref == 0);
    637     assert(pp2->pp_ref == 0);
    638 
    639     // so it should be returned by page_alloc
    640     assert(page_alloc(&pp) == 0 && pp == pp1);
    641 
    642     // should be no free memory
    643     assert(page_alloc(&pp) == -E_NO_MEM);
    644 
    645     // forcibly take pp0 back
    646     assert(PTE_ADDR(boot_pgdir[0]) == page2pa(pp0));
    647     boot_pgdir[0] = 0;
    648     assert(pp0->pp_ref == 1);
    649     pp0->pp_ref = 0;
    650 
    651     // give free list back
    652     page_free_list = fl;
    653 
    654     // free the pages we took
    655     page_free(pp0);
    656     page_free(pp1);
    657     page_free(pp2);
    658 
    659     printf("page_check() succeeded!
    ");
    660 }
    661 
    662 void pageout(int va, int context)
    663 {
    664     u_long r;
    665     struct Page *p = NULL;
    666 
    667     if (context < 0x80000000) {
    668         panic("tlb refill and alloc error!");
    669     }
    670 
    671     if ((va > 0x7f400000) && (va < 0x7f800000)) {
    672         panic(">>>>>>>>>>>>>>>>>>>>>>it's env's zone");
    673     }
    674 
    675     if (va < 0x10000) {
    676         panic("^^^^^^TOO LOW^^^^^^^^^");
    677     }
    678 
    679     if ((r = page_alloc(&p)) < 0) {
    680         panic ("page alloc error!");
    681     }
    682 
    683     p->pp_ref++;
    684 
    685     page_insert((Pde *)context, p, VA2PFN(va), PTE_R);
    686     printf("pageout:	@@@___0x%x___@@@  ins a page 
    ", va);
    687 }
    ./mm/pmap.c(已折叠)!剧透警告!

    在进行内存初始化时,mips_detect_memory()mips_vm_init()page_init()被依次调用。mips_detect_memory()用来初始化一些全局变量(此处将物理内存大小设置为64MB,在实际中,内存大小是由硬件得到的,这里只是模拟了检测物理内存大小这个过程)。其余的函数的功能为:

    • static void *alloc(u_int n, u_int align, int clear):申请一块内存,返回首地址。
    • static Pte *boot_pgdir_walk(Pde *pgdir, u_long va, int create):从页目录项中找出虚拟地址va对应的页表项,若create置位,则不存在时创建。
    • void boot_map_segment(Pde *pgdir, u_long va, u_long size, u_long pa, int perm):将虚拟地址va映射到物理地址pa
    • void mips_vm_init():创建一个二级页表。
    • void page_init(void):将内存分页并初始化空闲页表。
    • int page_alloc(struct Page **pp):分配一页内存并把值赋给pp。
    • void page_free(struct Page *pp):释放一页内存。
    • int pgdir_walk(Pde *pgdir, u_long va, int create, Pte **ppte):建立起二级页表结构后从页目录中找到va对应页表项的函数。
    • int page_insert(Pde *pgdir, struct Page *pp, u_long va, u_int perm):将物理页pp映射到va。
    • struct Page * page_lookup(Pde *pgdir, u_long va, Pte **ppte):找到虚拟地址va对应的物理页面。
    • void page_decref(struct Page *pp):降低物理页面的引用次数,降到0后释放页面。
    • void page_remove(Pde *pgdir, u_long va):释放虚拟地址va对应的页面。
    • void tlb_invalidate(Pde *pgdir, u_long va):更新TLB。

    最后一个函数通过调用tlb_asm.S中的汇编函数实现TLB的更新。

     1 #include <asm/regdef.h>
     2 #include <asm/cp0regdef.h>
     3 #include <asm/asm.h>
     4 
     5 LEAF(tlb_out)
     6 //1: j 1b
     7 nop
     8     mfc0    k1,CP0_ENTRYHI
     9     mtc0    a0,CP0_ENTRYHI
    10     nop
    11     tlbp
    12     nop
    13     nop
    14     nop
    15     nop
    16     mfc0    k0,CP0_INDEX
    17     bltz    k0,NOFOUND
    18     nop
    19     mtc0    zero,CP0_ENTRYHI
    20     mtc0    zero,CP0_ENTRYLO0
    21     nop
    22     tlbwi
    23 NOFOUND:
    24     mtc0    k1,CP0_ENTRYHI
    25     j    ra
    26     nop
    27 END(tlb_out)

    大概就是看一下TLB命不命中,若命中则写TLB,若不命中则直接返回。TLBPTLBWI分别用于查询TLB中的项与写TLB。

  • 相关阅读:
    QT5笔记:1.UI文件设计与运行机制
    QStringListModel的使用
    D pid(16916) tid(19140) 14:05:45 EdgeSnapFeature::PostExitSizeMove: WM_TWINVIEW_SHOW_GRIDLINES > off
    QT5笔记:3.手动撸界面和可视化托界面混合
    Qt QString的格式化与QString的拼接
    C# WinForm UDP 发送和接收消息
    关于Convert.ToUInt16(string? value, int fromBase);
    QT5笔记:2.可视化UI设计
    OpenCvSharp 打开rtsp视频并录制mp4文件
    C# List LinQ Lambda 表达式
  • 原文地址:https://www.cnblogs.com/littlenyima/p/12764653.html
Copyright © 2020-2023  润新知