这此来分析Main中的第三个函数Init_Mem(bootInfo)。
参数bootInfo是在主函数Main中传入的,为struct Boot_Info类型
定义在./include/geekos/bootinfo.h
struct Boot_Info { int bootInfoSize; /* size of this struct; for versioning */ int memSizeKB; /* number of KB, as reported by int 15h */ };
内存信息是setup代码获得的,在进入main函数前按照Boot_Info *类型作为参数压入。
如下
xor eax, eax mov ax, [(SETUPSEG<<4)+mem_size_kbytes] push eax ; memSizeKB push dword 8 ; bootInfoSize ; Pass pointer to Boot_Info struct as argument to kernel ; entry point. push esp ;注意这里,传入的是一个指针!!作为Main函数的参数 ; Push return address to make this look like a call , yes, it like a call. ; XXX - untested push dword (SETUPSEG<<4)+.returnAddr ; Far jump into kernel jmp KERNEL_CS:ENTRY_POINT ; 直接跳到Main函数入口处!!!!!!!
Init_Mem函数位于./src/geekos/mem.c中,有点长了。
/* * Initialize memory management data structures. * Enables the use of Alloc_Page() and Free_Page() functions. */ void Init_Mem(struct Boot_Info* bootInfo) { ulong_t numPages = bootInfo->memSizeKB >> 2; ulong_t endOfMem = numPages * PAGE_SIZE; unsigned numPageListBytes = sizeof(struct Page) * numPages; ulong_t pageListAddr; ulong_t kernEnd; KASSERT(bootInfo->memSizeKB > 0); /* * Before we do anything, switch from setup.asm's temporary GDT * to the kernel's permanent GDT. */ Init_GDT(); /* * We'll put the list of Page objects right after the end * of the kernel, and mark it as "kernel". This will bootstrap * us sufficiently that we can start allocating pages and * keeping track of them. */ pageListAddr = Round_Up_To_Page((ulong_t) &end); g_pageList = (struct Page*) pageListAddr; kernEnd = Round_Up_To_Page(pageListAddr + numPageListBytes); s_numPages = numPages; /* * The initial kernel thread and its stack are placed * just beyond the ISA hole. */ KASSERT(ISA_HOLE_END == KERN_THREAD_OBJ); KASSERT(KERN_STACK == KERN_THREAD_OBJ + PAGE_SIZE); /* * Memory looks like this: * 0 - start: available (might want to preserve BIOS data area) * start - end: kernel * end - ISA_HOLE_START: available * ISA_HOLE_START - ISA_HOLE_END: used by hardware (and ROM BIOS?) * ISA_HOLE_END - HIGHMEM_START: used by initial kernel thread * HIGHMEM_START - end of memory: available * (the kernel heap is located at HIGHMEM_START; any unused memory * beyond that is added to the freelist) */ Add_Page_Range(0, PAGE_SIZE, PAGE_UNUSED); Add_Page_Range(PAGE_SIZE, KERNEL_START_ADDR, PAGE_AVAIL); Add_Page_Range(KERNEL_START_ADDR, kernEnd, PAGE_KERN); Add_Page_Range(kernEnd, ISA_HOLE_START, PAGE_AVAIL); Add_Page_Range(ISA_HOLE_START, ISA_HOLE_END, PAGE_HW); Add_Page_Range(ISA_HOLE_END, HIGHMEM_START, PAGE_ALLOCATED); Add_Page_Range(HIGHMEM_START, HIGHMEM_START + KERNEL_HEAP_SIZE, PAGE_HEAP); Add_Page_Range(HIGHMEM_START + KERNEL_HEAP_SIZE, endOfMem, PAGE_AVAIL); /* Initialize the kernel heap */ Init_Heap(HIGHMEM_START, KERNEL_HEAP_SIZE); Print("%uKB memory detected, %u pages in freelist, %d bytes in kernel heap\n", bootInfo->memSizeKB, g_freePageCount, KERNEL_HEAP_SIZE); }
先看前两句
ulong_t numPages = bootInfo->memSizeKB >> 2;
ulong_t endOfMem = numPages * PAGE_SIZE;
实际调试中,bootInfo->memSizeKB = 32768
numPages=8192 PAGE_SIZE则是一个物理页的大小4K,
endOfMem为8192*4K,为32768K
memSizeKB是系统可用的物理内存,以K为单位,系统为每一个4K的物理页建立一个描述项,numPages得到描述项的个数。
endOfMem则得到可用内存的最终地址。
比如说如果boot_SIZE为32769,那么最终得到endOfMem为32768,剩余的1K不用,因为这个物理页不完整,无法用一个描述项描述。
下一句
unsigned numPageListBytes = sizeof(struct Page) * numPages;
得到这些描述项共占内存大小。
struct Page是物理页描述项的类型
定义在./include/geekos/mem.h中
/*
* Each page of physical memory has one of these structures
* associated with it, to do allocation and bookkeeping.
*/
struct Page {
unsigned flags; /* Flags indicating state of page */
DEFINE_LINK(Page_List, Page); /* Link fields for Page_List */
};
其中DEFINE_LINK()是一个宏,定义在./include/geekos/list.h中
/* * Define members of a struct to be used as link fields for * membership in given list type. */ #define DEFINE_LINK(listTypeName, nodeTypeName) \ struct nodeTypeName * prev##listTypeName, * next##listTypeName
在struct Page中展开就是
struct Page{
unsigned flags;
struct Page * prevPage_List, * nextPage_list;
};
熟悉吧,^_^,DEFINE_LINK不就是Linux内核中的struct list_head双向链表结构吗,这里就把所有的物理页描述项连接了起来。
再看下一句
KASSERT(bootInfo->memSizeKB > 0);
物理内存大小大于0,这当然是必须的了。
下一句
Init_GDT();
明显是初始化全局描述符表GDT的,看它的实现
位于./src/geekos/gdt.c
/* * Initialize the kernel's GDT. */ void Init_GDT(void) { ushort_t limitAndBase[3]; ulong_t gdtBaseAddr = (ulong_t) s_GDT; struct Segment_Descriptor* desc; int i; KASSERT(sizeof(struct Segment_Descriptor) == 8); /* Clear out entries. */ for (i = 0; i < NUM_GDT_ENTRIES; ++i) { desc = &s_GDT[ i ]; Init_Null_Segment_Descriptor(desc); desc->avail = 1; } /* Kernel code segment. */ desc = Allocate_Segment_Descriptor(); Init_Code_Segment_Descriptor( desc, 0, /* base address */ 0x100000, /* num pages (== 2^20) */ 0 /* privilege level (0 == kernel) */ ); KASSERT(Get_Descriptor_Index(desc) == (KERNEL_CS >> 3)); /* Kernel data segment. */ desc = Allocate_Segment_Descriptor(); Init_Data_Segment_Descriptor( desc, 0, /* base address */ 0x100000, /* num pages (== 2^20) */ 0 /* privilege level (0 == kernel) */ ); KASSERT(Get_Descriptor_Index(desc) == (KERNEL_DS >> 3)); /* Activate the kernel GDT. */ limitAndBase[0] = sizeof(struct Segment_Descriptor) * NUM_GDT_ENTRIES; limitAndBase[1] = gdtBaseAddr & 0xffff; limitAndBase[2] = gdtBaseAddr >> 16; Load_GDTR(limitAndBase); }
好,来看一下这个Init_GDT()函数,先看s_GDT这个东东是哪儿来的
位于./src/geekos/gdt.c
/* * Number of entries in the kernel GDT. */ #define NUM_GDT_ENTRIES 16 /* * This is the kernel's global descriptor table. */ static struct Segment_Descriptor s_GDT[ NUM_GDT_ENTRIES ];
GDT就是系统进入保护模式后的全局描述符表。
这里定义了16个全局描述符,Segment_Descriptor结构就不说了,还是那一套。
下一句
KASSERT(sizeof(struct Segment_Descriptor) == 8);
下一句用for循环清空了这一段GDT内存,并置标志avail为1,avail为1表示此项可用。Init_Null_Segment_Descriptor调用了memset函数来完成了清空内存。
再看下句Allocate_Segment_Descriptor()函数。
位于gdt.c
/* * Allocate an descriptor from the GDT. * Returns null if there are none left. */ struct Segment_Descriptor* Allocate_Segment_Descriptor(void) { struct Segment_Descriptor* result = 0; int i; bool iflag; iflag = Begin_Int_Atomic(); /* Note; entry 0 is unused (thus never allocated) */ for (i = 1; i < NUM_GDT_ENTRIES; ++i) { struct Segment_Descriptor *desc = &s_GDT[ i ]; if (desc->avail) { ++s_numAllocated; desc->avail = 0; result = desc; break; } } End_Int_Atomic(iflag); return result; }
分配了一个描述符项,分配之后将描述符项中的avail标志置0表示此项已用。
看下一句
/* Kernel code segment. */
desc = Allocate_Segment_Descriptor();
Init_Code_Segment_Descriptor(
desc,
0, /* base address */
0x100000, /* num pages (== 2^20) */
0 /* privilege level (0 == kernel) */
);
KASSERT(Get_Descriptor_Index(desc) == (KERNEL_CS >> 3));
初始化了s_GDT中的第1项(s_GDT的第0项不使用)。
完成了代码段描述符的设置。映射了4G的内存。
再下面是完成数据段描述符的设置,同样映射了4G的内存。
KASSERT()用于确保偏移字节一致。
Init_GDT函数的最后4句
/* Activate the kernel GDT. */
limitAndBase[0] = sizeof(struct Segment_Descriptor) * NUM_GDT_ENTRIES;
limitAndBase[1] = gdtBaseAddr & 0xffff;
limitAndBase[2] = gdtBaseAddr >> 16;
Load_GDTR(limitAndBase);
设置GDT的基址和界限并将新的GDT载入到gdtr寄存器。
最后一个函数Load_GDTR位于src/geekos/lowlevel.asm
align 8
Load_GDTR:
mov eax, [esp+4]
lgdt [eax]
; Reload segment registers
mov ax, KERNEL_DS
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp KERNEL_CS:.here
.here:
ret
Init_GDT函数到此结束。
回到Init_Mem()函数中,下面
pageListAddr = Round_Up_To_Page((ulong_t) &end);
g_pageList = (struct Page*) pageListAddr;
kernEnd = Round_Up_To_Page(pageListAddr + numPageListBytes);
s_numPages = numPages;
内联函数Round_Up_To_Page((ulong_t) &end)将end地址向上补齐成4K对齐。
g_pageList是全局变量,在这里设置成为Page链表的开始内存地址。
这四句准备在end地址后面建立物理页的描述链表。
接下来
Add_Page_Range(0, PAGE_SIZE, PAGE_UNUSED);
Add_Page_Range(PAGE_SIZE, KERNEL_START_ADDR, PAGE_AVAIL);
Add_Page_Range(KERNEL_START_ADDR, kernEnd, PAGE_KERN);
Add_Page_Range(kernEnd, ISA_HOLE_START, PAGE_AVAIL);
Add_Page_Range(ISA_HOLE_START, ISA_HOLE_END, PAGE_HW);
Add_Page_Range(ISA_HOLE_END, HIGHMEM_START, PAGE_ALLOCATED);
Add_Page_Range(HIGHMEM_START, HIGHMEM_START + KERNEL_HEAP_SIZE, PAGE_HEAP);
Add_Page_Range(HIGHMEM_START + KERNEL_HEAP_SIZE, endOfMem, PAGE_AVAIL);
在链表中增加了描述项,参数分别为起始地址,结束地址,标志。
可以看到内存的存储情况,结合前面的注释,一看就懂了。
来看一下Add_Page_Range()的实现,位于./src/geekos/mem.c中
/* * Add a range of pages to the inventory of physical memory. */ static void Add_Page_Range(ulong_t start, ulong_t end, int flags) { ulong_t addr; KASSERT(Is_Page_Multiple(start)); KASSERT(Is_Page_Multiple(end)); KASSERT(start < end); for (addr = start; addr < end; addr += PAGE_SIZE) { struct Page *page = Get_Page(addr); page->flags = flags; if (flags == PAGE_AVAIL) { /* Add the page to the freelist */ Add_To_Back_Of_Page_List(&s_freeList, page); /* Update free page count */ ++g_freePageCount; } else { Set_Next_In_Page_List(page, 0); Set_Prev_In_Page_List(page, 0); } } }
再看一下for循环中的Get_Page实现
位于/include/geekos/mem.h
/* * Get the index of the page in memory. */ static __inline__ int Page_Index(ulong_t addr) { return (int) (addr >> PAGE_POWER); } /* * Get the Page struct associated with given address. */ static __inline__ struct Page *Get_Page(ulong_t addr) { extern struct Page* g_pageList; return &g_pageList[Page_Index(addr)]; }
看Add_Page_Range下一句
Add_To_Back_Of_Page_List(&s_freeList, page);
居然找不到s_freeList的定义,函数也找不到,包括下面的Set_Next_In_Page_List(page, 0)和Set_Prev_In_Page_List(page, 0);????!!!!
怎么搞的。。正好我先偷个懒。。。
调试出来了,它跑到list.h里执行了一个很长的宏IMPLEMENT_LIST(Page_List, Page);
原来在mem.h中声明了此宏
IMPLEMENT_LIST(Page_List, Page);
IMPLEMENT_LIST就为以Page_List为头,Page为节点类型的链表定义了一套方法。
列一下好了
/* * Define inline list manipulation and access functions. */ #define IMPLEMENT_LIST(LType, NType) \ static __inline__ void Clear_##LType(struct LType *listPtr) { \ listPtr->head = listPtr->tail = 0; \ } \ static __inline__ bool Is_Member_Of_##LType(struct LType *listPtr, struct NType *nodePtr) { \ struct NType *cur = listPtr->head; \ while (cur != 0) { \ if (cur == nodePtr) \ return true; \ cur = cur->next##LType; \ } \ return false; \ } \ static __inline__ struct NType * Get_Front_Of_##LType(struct LType *listPtr) { \ return listPtr->head; \ } \ static __inline__ struct NType * Get_Back_Of_##LType(struct LType *listPtr) { \ return listPtr->tail; \ } \ static __inline__ struct NType * Get_Next_In_##LType(struct NType *nodePtr) { \ return nodePtr->next##LType; \ } \ static __inline__ void Set_Next_In_##LType(struct NType *nodePtr, struct NType *value) { \ nodePtr->next##LType = value; \ } \ static __inline__ struct NType * Get_Prev_In_##LType(struct NType *nodePtr) { \ return nodePtr->prev##LType; \ } \ static __inline__ void Set_Prev_In_##LType(struct NType *nodePtr, struct NType *value) { \ nodePtr->prev##LType = value; \ } \ static __inline__ void Add_To_Front_Of_##LType(struct LType *listPtr, struct NType *nodePtr) { \ KASSERT(!Is_Member_Of_##LType(listPtr, nodePtr)); \ nodePtr->prev##LType = 0; \ if (listPtr->head == 0) { \ listPtr->head = listPtr->tail = nodePtr; \ nodePtr->next##LType = 0; \ } else { \ listPtr->head->prev##LType = nodePtr; \ nodePtr->next##LType = listPtr->head; \ listPtr->head = nodePtr; \ } \ } \ static __inline__ void Add_To_Back_Of_##LType(struct LType *listPtr, struct NType *nodePtr) { \ KASSERT(!Is_Member_Of_##LType(listPtr, nodePtr)); \ nodePtr->next##LType = 0; \ if (listPtr->tail == 0) { \ listPtr->head = listPtr->tail = nodePtr; \ nodePtr->prev##LType = 0; \ } \ else { \ listPtr->tail->next##LType = nodePtr; \ nodePtr->prev##LType = listPtr->tail; \ listPtr->tail = nodePtr; \ } \ } \ static __inline__ void Append_##LType(struct LType *listToModify, struct LType *listToAppend) { \ if (listToAppend->head != 0) { \ if (listToModify->head == 0) { \ listToModify->head = listToAppend->head; \ listToModify->tail = listToAppend->tail; \ } else { \ KASSERT(listToAppend->head != 0); \ KASSERT(listToModify->tail != 0); \ listToAppend->head->prev##LType = listToModify->tail; \ listToModify->tail->next##LType = listToAppend->head; \ listToModify->tail = listToAppend->tail; \ } \ } \ listToAppend->head = listToAppend->tail = 0; \ } \ static __inline__ struct NType * Remove_From_Front_Of_##LType(struct LType *listPtr) { \ struct NType *nodePtr; \ nodePtr = listPtr->head; \ KASSERT(nodePtr != 0); \ listPtr->head = listPtr->head->next##LType; \ if (listPtr->head == 0) \ listPtr->tail = 0; \ else \ listPtr->head->prev##LType = 0; \ return nodePtr; \ } \ static __inline__ void Remove_From_##LType(struct LType *listPtr, struct NType *nodePtr) { \ KASSERT(Is_Member_Of_##LType(listPtr, nodePtr)); \ if (nodePtr->prev##LType != 0) \ nodePtr->prev##LType->next##LType = nodePtr->next##LType; \ else \ listPtr->head = nodePtr->next##LType; \ if (nodePtr->next##LType != 0) \ nodePtr->next##LType->prev##LType = nodePtr->prev##LType; \ else \ listPtr->tail = nodePtr->prev##LType; \ } \ static __inline__ bool Is_##LType##_Empty(struct LType *listPtr) { \ return listPtr->head == 0; \ }
追宗溯源,看一下是怎么得出的。
在mem.c中s_freeList是这样定义的
static struct Page_List s_freeList;
在mem.h中有
/*
* List datatype for doubly-linked list of Pages.
*/
DEFINE_LIST(Page_List, Page);
再看DEFINE_LIST这个宏
位于list.h中
/* * Define a list type. */ #define DEFINE_LIST(listTypeName, nodeTypeName) \ struct listTypeName { \ struct nodeTypeName *head, *tail; \ }
再结合Page结构
struct Page {
unsigned flags; /* Flags indicating state of page */
struct Page *prevPage_List, *nextPage_List;/*这里我把宏DEFINE_LINK展开了*/
};
来看这个函数的具体实现
Add_To_Back_Of_Page_List(&s_freeList, page);
看IMPLEMENT_LIST宏对应的函数
static __inline__ void Add_To_Back_Of_##LType(struct LType *listPtr, struct NType *nodePtr) { \ KASSERT(!Is_Member_Of_##LType(listPtr, nodePtr)); \ nodePtr->next##LType = 0; \ if (listPtr->tail == 0) { \ listPtr->head = listPtr->tail = nodePtr; \ nodePtr->prev##LType = 0; \ } \ else { \ listPtr->tail->next##LType = nodePtr; \ nodePtr->prev##LType = listPtr->tail; \ listPtr->tail = nodePtr; \ } \ }
static __inline__ void Add_To_Back_Of_Page_List(struct Page_List *listPtr, struct Page *nodePtr) { \ KASSERT(!Is_Member_Of_Page_List(listPtr, nodePtr)); \ nodePtr->nextPage_List = 0; \ if (listPtr->tail == 0) { \ listPtr->head = listPtr->tail = nodePtr; \ nodePtr->prevPage_List = 0; \ } \ else { \ listPtr->tail->nextPage_List = nodePtr; \ nodePtr->prevPage_List = listPtr->tail; \ listPtr->tail = nodePtr; \ } \ }
终于到最后一个函数了
Init_Heap(HIGHMEM_START, KERNEL_HEAP_SIZE);
初始化内核堆,最后系统分配了1M的内核堆。
Init_Heap函数实现在./src/geekos/malloc.c中
/* * Initialize the heap starting at given address and occupying * specified number of bytes. */ void Init_Heap(ulong_t start, ulong_t size) { /*Print("Creating kernel heap: start=%lx, size=%ld\n", start, size);*/ bpool((void*) start, size); }
bpool函数在内存池中加入一段内存。
位于./src/geekos/bget.c中
/* BPOOL -- Add a region of memory to the buffer pool. */ void bpool(buf, len) void *buf; bufsize len; { struct bfhead *b = BFH(buf);//将内存头部一段空间作为bfhead结构 struct bhead *bn; #ifdef SizeQuant len &= ~(SizeQuant - 1);//分配需要SizeQuant个字节对齐,这里向下裁剪len。 #endif #ifdef BECtl if (pool_len == 0) {//pool_len初始化为0, pool_len = len; } else if (len != pool_len) {//-1表示不是所有分配的内存块大小都是相同的 pool_len = -1; } #ifdef BufStats numpget++; /* Number of block acquisitions */ numpblk++; /* Number of blocks total */ assert(numpblk == numpget - numprel); #endif /* BufStats */ #endif /* BECtl */ /* Since the block is initially occupied by a single free buffer, it had better not be (much) larger than the largest buffer whose size we can store in bhead.bsize. */ //调试发现右侧为4字节,有符号整数类型的最大正整数 assert(len - sizeof(struct bhead) <= -((bufsize) ESent + 1)); /* Clear the backpointer at the start of the block to indicate that there is no free block prior to this one. That blocks recombination when the first block in memory is released. */ b->bh.prevfree = 0;//0表示此块之前的内存块是已分配的,不能随意访问。 /* Chain the new block to the free list. 将新的内存池结构体链接到链表中*/ assert(freelist.ql.blink->ql.flink == &freelist); assert(freelist.ql.flink->ql.blink == &freelist); b->ql.flink = &freelist; //blink表示先驱节点,flink表示后继节点。 b->ql.blink = freelist.ql.blink; //这里将结构插入到链表的头。 freelist.ql.blink = b; b->ql.blink->ql.flink = b; /* Create a dummy allocated buffer at the end of the pool. This dummy buffer is seen when a buffer at the end of the pool is released and blocks recombination of the last buffer with the dummy buffer at the end. The length in the dummy buffer is set to the largest negative number to denote the end of the pool for diagnostic routines (this specific value is not counted on by the actual allocation and release functions). */ len -= sizeof(struct bhead); b->bh.bsize = (bufsize) len; #ifdef FreeWipe //清空可用内存空间 V memset(((char *) b) + sizeof(struct bfhead), 0x55, (MemSize) (len - sizeof(struct bfhead))); #endif //在内存尾部添加bhead结构 bn = BH(((char *) b) + len); bn->prevfree = (bufsize) len;//说明在此地址前面可用的内存大小 /* Definition of ESent assumes two's complement! */ assert((~0) == -1); bn->bsize = ESent;//在空间尾部放置最大的负数,来做标记。 }
#define BFH(p) ((struct bfhead *) (p))
/* Header in free buffers */ struct bfhead { struct bhead bh; /* Common allocated/free header */ struct qlinks ql; /* Links on free list */ };
/* Header in allocated and free buffers */ struct bhead { bufsize prevfree; /* Relative link back to previous free buffer in memory or 0 if previous buffer is allocated. */ bufsize bsize; /* Buffer size: positive if free, negative if allocated. */ };
/* Queue links */ struct qlinks { struct bfhead *flink; /* Forward link */ struct bfhead *blink; /* Backward link */ };
static struct bfhead freelist = { /* List of free buffers */ {0, 0}, {&freelist, &freelist} };
到这里Init_Mem分析完毕。