// kern/init/init.cintkern_init(void){externchar edata[], end[];memset(edata,0, end - edata);cons_init();// init the consoleconstchar*message ="(THU.CST) os is loading ...\0";cputs(message);print_kerninfo();idt_init();// init interrupt descriptor tablepmm_init();// 新东西!clock_init();// init clock interruptintr_enable();// enable irq interrupt/* do nothing */while(1);}// kern/mm/pmm.c/* pmm_init - initialize the physical memory management */voidpmm_init(void){ // We need to alloc/free the physical memory (granularity is 4KB or other size). // So a framework of physical memory manager (struct pmm_manager)is defined in pmm.h // First we should init a physical memory manager(pmm) based on the framework. // Then pmm can alloc/free the physical memory.init_pmm_manager(); // detect physical memory space, reserve already used memory, // then use pmm->init_memmap to create free page listpage_init(); // use pmm->check to verify the correctness of the alloc/free function in a pmmcheck_alloc_page();externchar boot_page_table_sv39[];//我们把汇编里定义的页表所在位置的符号声明进来 satp_virtual =(pte_t*)boot_page_table_sv39; satp_physical =PADDR(satp_virtual);//然后输出页表所在的地址cprintf("satp virtual address: 0x%016lx\nsatp physical address: 0x%016lx\n", satp_virtual, satp_physical);}
page_init()的代码里,我们调用了一个函数init_memmap(), 这和我们的另一个结构体pmm_manager有关。虽然C语言基本上不支持面向对象,但我们可以用类似面向对象的思路,把”物理内存管理“的功能集中给一个结构体。我们甚至可以让函数指针作为结构体的成员,强行在C语言里支持了”成员函数“。可以看到,我们调用的init_memmap()实际上又调用了pmm_manager的一个”成员函数“。如果你不熟悉函数指针的用法,可以读一读《The C Programming Language》的相关章节(待补充:第几章第几节?)。
// libs/defs.h
/* Return the offset of 'member' relative to the beginning of a struct type */
#define offsetof(type, member) \
((size_t)(&((type *)0)->member))
/* *
* to_struct - get the struct from a ptr
* @ptr: a struct pointer of member
* @type: the type of the struct this is embedded in
* @member: the name of the member within the struct
* */
#define to_struct(ptr, type, member) \
((type *)((char *)(ptr) - offsetof(type, member)))
// 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 physical address.
* */
struct Page {
int ref; // page frame's reference counter
uint64_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 // if this bit=1: the Page is reserved for kernel, cannot be used in alloc/free_pages; otherwise, this bit=0
#define PG_property 1 // if this bit=1: the Page is the head page of a free memory block(contains some continuous_addrress pages), and can be used in alloc_pages; if this bit=0: if the Page is the the head page of a free memory block, then this Page and the memory block is alloced. Or this Page isn't the head page.
//这几个对page操作的宏用到了atomic.h的原子操作
#define SetPageReserved(page) set_bit(PG_reserved, &((page)->flags))
#define ClearPageReserved(page) clear_bit(PG_reserved, &((page)->flags))
#define PageReserved(page) test_bit(PG_reserved, &((page)->flags))
#define SetPageProperty(page) set_bit(PG_property, &((page)->flags))
#define ClearPageProperty(page) clear_bit(PG_property, &((page)->flags))
#define PageProperty(page) test_bit(PG_property, &((page)->flags))
// convert list entry to page
#define le2page(le, member) \
to_struct((le), struct Page, member)
/* 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;