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\n", (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\n");
 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.\n", 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.\n", 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:\t mips vm init success\n");
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\n");
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\n",*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\n",&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\n",&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\n",p->pp_ref,answer1[j]);
523                 assert(p->pp_ref==answer1[j++]);
524                 //printf("ptr: 0x%x v: %d\n",(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\n",test_pages[4].pp_ref);
534         LIST_INSERT_AFTER(&test_pages[4], q, pp_link);
535         //printf("---%d\n",LIST_NEXT(&test_pages[4],pp_link)->pp_ref);
536         p = LIST_FIRST(&test_free);
537         j=0;
538         //printf("into test\n");
539         while(p!=NULL){
540         //      printf("%d %d\n",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\n");
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\n",va2pa(boot_pgdir, 0x0));
584     printf("page2pa(pp1) is %x\n",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\n");
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\n",pp2->pp_ref);
619     assert(pp2->pp_ref == 0);
620     printf("end page_insert\n");
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!\n");
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:\t@@@___0x%x___@@@  ins a page \n", 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。

 

posted @ 2020-04-24 02:08  LittleNyima  阅读(906)  评论(0编辑  收藏  举报