将lab1中已完成的代码更新到lab2中,在这里分别尝试了diff+patch和meld两种方法
在lab2目录下,trap.c的更新如下(注意可能需要手动在.patch文件中处理合并部分)
diff -c ./kern/trap/trap.c ../lab1/kern/trap/trap.c > ./kern/trap/trap.patch
patch -p2 -i ./kern/trap/trap.patch
对kdebug.c的更新使用meld,命令行输入meld
后直接拖入要合并的文件并选择合并方式即可
首先来看页的结构定义在kern/mm/memlayout.h
中
/* *
* struct Page - Page descriptor structures. Each Page describes one
* physical page. In kern/mm/pmm.h, you can find lots of useful functions
* that convert Page to other data types, such as phyical address.
* */
struct Page {
int ref; // page frame's reference counter
uint32_t flags; // array of flags that describe the status of the page frame
unsigned int property; // the num of free block, used in first fit pm manager
list_entry_t page_link; // free list link
};
/* Flags describing the status of a page frame */
#define PG_reserved 0
#define PG_property 1
ref
表示页被引用的计数器,当有一个虚拟页映射到了这个结构描述的物理页,计数器加1,反之减1,当变为0的时候要设置其标记位表示页是未分配的flags
表示页的状态标记,有2个标记,bit0表示此页是否被保留,若被保留则设置为1,bit1表示此页是否是空闲的,若是空闲则设置为1,可以通过SetPageReserved
等宏进行标记的设置property
表示某连续内存空闲块的大小,即地址连续空闲页的个数,需要设置此变量的页是头页,连续内存空闲块地址最小的一页page_link
表示把多个连续内存空闲块链接在一起的双向链表指针,具体定义及操作参考libs/list.h
为了有效管理一系列地址不连续的多个小连续内存空闲块(块内的物理页是连续的),定义了free_area_t
数据结构,包含有list_entry_t
双向链表指针和nr_free
记录当前空闲页个数的变量
/* free_area_t - maintains a doubly linked list to record free (unused) pages */
typedef struct {
list_entry_t free_list; // the list header
unsigned int nr_free; // # of free pages in this free list
} free_area_t;
ucore为了管理内存分配和释放,建立了一个物理内存页管理框架包含一系列操作,定义在kern/mm/pmm.h
中
// pmm_manager is a physical memory management class. A special pmm manager - XXX_pmm_manager
// only needs to implement the methods in pmm_manager class, then XXX_pmm_manager can be used
// by ucore to manage the total physical memory space.
struct pmm_manager {
const char *name; // XXX_pmm_manager's name
void (*init)(void); // initialize internal description&management data structure
// (free block list, number of free block) of XXX_pmm_manager
void (*init_memmap)(struct Page *base, size_t n); // setup description&management data structcure according to
// the initial free physical memory space
struct Page *(*alloc_pages)(size_t n); // allocate >=n pages, depend on the allocation algorithm
void (*free_pages)(struct Page *base, size_t n); // free >=n pages with "base" addr of Page descriptor structures(memlayout.h)
size_t (*nr_free_pages)(void); // return the number of free pages
void (*check)(void); // check the correctness of XXX_pmm_manager
};
first-fit是通过遍历空闲块链表,将第一个符合要求的块分配出去的算法,包含改变标记位、从链表中删除、处理多余的页(符合要求的块大于所需的大小)
实验1中重点实现init_memmap
、alloc_pages
、free_pages
这三个函数,基于实验代码中部分完成的部分进一步修改如下
/*reuse the demo default_init fun*/
static void
default_init(void) {
list_init(&free_list);
nr_free = 0;
}
static void
default_init_memmap(struct Page *base, size_t n) {
assert(n > 0);
struct Page *p = base;
for (; p != base + n; p ++) {
assert(PageReserved(p));
p->flags = 0;
SetPageProperty(p);
p->property = 0;
set_page_ref(p, 0);
list_add_before(&free_list, &(p->page_link)); //'add before' -> low to high addr
}
base->property = n; //head page need to set property
nr_free += n;
}
static struct Page *
default_alloc_pages(size_t n) {
assert(n > 0);
if (n > nr_free) {
return NULL;
}
struct Page *page = NULL;
list_entry_t *le = &free_list, *len;
while ((le = list_next(le)) != &free_list) {
struct Page *p = le2page(le, page_link);
if (p->property >= n) { //first-fit then break
page = p;
break;
}
}
if (page != NULL) {
if (page->property > n) {
struct Page *p = page + n;
p->property = page->property - n; //update the spare pages' head page
}
nr_free -= n;
for(int i=0; i//initialize the allocated pages
len = list_next(le);
struct Page *pp = le2page(le, page_link);
SetPageReserved(pp);
ClearPageProperty(pp);
list_del(le);
le = len;
}
}
return page;
}
static void
default_free_pages(struct Page *base, size_t n) {
assert(n > 0);
assert(PageReserved(base));
nr_free += n;
struct Page *p;
list_entry_t *le = &free_list;
while((le = list_next(le)) != &free_list){
p = le2page(le, page_link);
if(p > base){ //to find the correct position to insert(from low to high addr)
break;
}
}
for(p = base; p < base + n; p++){ //reset the freed pages and insert them into page_link
list_add_before(le, &(p->page_link));
p->flags = 0;
p->property = 0;
set_page_ref(p, 0);
ClearPageReserved(p);
SetPageProperty(p);
}
base->property = n; //assume base is a head page
if(base + n == le2page(le, page_link)){ //merge with high addr
base->property += p->property;
p->property = 0;
}
le = list_prev(&(base->page_link));
p = le2page(le, page_link);
if(le != &free_list && p == base - 1){ //merge with low addr
while(le != &free_list){
if(p->property > 0){ //find the previous free block's head page and merge
p->property += base->property;
base->property = 0; //base is not a head page, reset its property
break;
}
le = list_prev(le);
p = le2page(le, page_link);
}
}
}
ucore下的段机制使得虚拟地址到线性地址存在对等映射的关系,而二级页表使得线性地址到物理地址存在映射关系,基本模式如下图(没有体现TLB等因素),地址映射的四个阶段可以参考实验指导手册
https://chyyuu.gitbooks.io/ucore_os_docs/content/lab2/lab2_3_3_5_4_maping_relations.html
具体到分页机制,ucore在kern/mm/mmu.h
中定义了线性地址的组成及一些宏操作,线性地址由10位页目录表索引、10位页表项索引和12位页内偏移构成
// A linear address 'la' has a three-part structure as follows:
//
// +--------10------+-------10-------+---------12----------+
// | Page Directory | Page Table | Offset within Page |
// | Index | Index | |
// +----------------+----------------+---------------------+
// \--- PDX(la) --/ \--- PTX(la) --/ \---- PGOFF(la) ----/
// \----------- PPN(la) -----------/
//
// The PDX, PTX, PGOFF, and PPN macros decompose linear addresses as shown.
// To construct a linear address la from PDX(la), PTX(la), and PGOFF(la),
// use PGADDR(PDX(la), PTX(la), PGOFF(la)).
// page directory index
#define PDX(la) ((((uintptr_t)(la)) >> PDXSHIFT) & 0x3FF)
// page table index
#define PTX(la) ((((uintptr_t)(la)) >> PTXSHIFT) & 0x3FF)
// page number field of address
#define PPN(la) (((uintptr_t)(la)) >> PTXSHIFT)
// offset in page
#define PGOFF(la) (((uintptr_t)(la)) & 0xFFF)
// construct linear address from indexes and offset
#define PGADDR(d, t, o) ((uintptr_t)((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))
// address in page table or page directory entry
#define PTE_ADDR(pte) ((uintptr_t)(pte) & ~0xFFF)
#define PDE_ADDR(pde) PTE_ADDR(pde)
需要完成的get_pte
实际上就是获得Page Table中Entry的地址,其函数说明如下
//get_pte - get pte and return the kernel virtual address of this pte for la
// - if the PT contians this pte didn't exist, alloc a page for PT
// parameter:
// pgdir: the kernel virtual base address of PDT
// la: the linear address need to map
// create: a logical value to decide if alloc a page for PT
// return vaule: the kernel virtual address of this pte
则本函数的基本流程为
实现如下,与lab2_result
中的实现略有不同,这里不对比分析
pde_t *pdep = &pgdir[PDX(la)]; //get page directory entry(physical addr)
if(!(*pdep & PTE_P)){
struct Page *page;
if(!create || (page = alloc_page()) == NULL){
return NULL;
}
set_page_ref(page,1);
uintptr_t pa = page2pa(page); //get the new page's physical addr
memset(KADDR(pa), 0, PGSIZE); //initialize the memory(need KADDR() to get the virtual addr as OS process virual addr)
*pdep = pa | PTE_P | PTE_W | PTE_U; //set flags
}
return &((pte_t *)KADDR(PDE_ADDR(*pdep)))[PTX(la)]; //PDE_ADDR(): align to 4k, KADDR(): convert pa to va, PTX(): get page table index
在练习2的基础上,练习3提供了一些函数更为方便的释放页并更新页表映射,直接给出代码如下
if (*ptep & PTE_P) { //check if this page table entry is present
struct Page *page = pte2page(*ptep); //find corrsponding page to pte
if (page_ref_dec(page) == 0) { //decrease page ref
free_page(page); //if page ref == 0 then free it
}
*ptep = 0; //clear second page table entry
tlb_invalidate(pgdir, la); //update TLB
}
在完成所有内容后,调用make debug
并在GDB
中输入continue
后运行结果如下,函数功能实现成功
lab2$ (THU.CST) os is loading ...
Special kernel symbols:
entry 0xc010002a (phys)
etext 0xc0105a3e (phys)
edata 0xc0117a36 (phys)
end 0xc0118968 (phys)
Kernel executable memory footprint: 99KB
ebp:0xc0116f48 eip:0xc0100a52 args:0x00010094 0x00010094 0xc0116f78 0xc01000a9
kern/debug/kdebug.c:309: print_stackframe+22
ebp:0xc0116f58 eip:0xc0100d3e args:0x00000000 0x00000000 0x00000000 0xc0116fc8
kern/debug/kmonitor.c:129: mon_backtrace+10
ebp:0xc0116f78 eip:0xc01000a9 args:0x00000000 0xc0116fa0 0xffff0000 0xc0116fa4
kern/init/init.c:49: grade_backtrace2+19
ebp:0xc0116f98 eip:0xc01000cb args:0x00000000 0xffff0000 0xc0116fc4 0x00000029
kern/init/init.c:54: grade_backtrace1+27
ebp:0xc0116fb8 eip:0xc01000e8 args:0x00000000 0xc010002a 0xffff0000 0xc010006d
kern/init/init.c:59: grade_backtrace0+19
ebp:0xc0116fd8 eip:0xc0100109 args:0x00000000 0x00000000 0x00000000 0xc0105a40
kern/init/init.c:64: grade_backtrace+26
ebp:0xc0116ff8 eip:0xc010007a args:0x00000000 0x00000000 0x0000ffff 0x40cf9a00
kern/init/init.c:29: kern_init+79
memory management: default_pmm_manager
e820map:
memory: 0009fc00, [00000000, 0009fbff], type = 1.
memory: 00000400, [0009fc00, 0009ffff], type = 2.
memory: 00010000, [000f0000, 000fffff], type = 2.
memory: 07ee0000, [00100000, 07fdffff], type = 1.
memory: 00020000, [07fe0000, 07ffffff], type = 2.
memory: 00040000, [fffc0000, ffffffff], type = 2.
check_alloc_page() succeeded!
check_pgdir() succeeded!
check_boot_pgdir() succeeded!
-------------------- BEGIN --------------------
PDE(0e0) c0000000-f8000000 38000000 urw
|-- PTE(38000) c0000000-f8000000 38000000 -rw
PDE(001) fac00000-fb000000 00400000 -rw
|-- PTE(000e0) faf00000-fafe0000 000e0000 urw
|-- PTE(00001) fafeb000-fafec000 00001000 -rw
--------------------- END ---------------------
++ setup timer interrupts
100 ticks
100 ticks
lab2需要对内存管理机制有深入的了解,博主还没有很好的掌握,先写这么多