目录
boot_alloc()
这个简单的物理内存分配器只在 JOS 启动它的虚拟内存系统时使用,page_alloc()才是真正的物理页面分配器。
static void *
boot_alloc(uint32_t n)
{
static char *nextfree; // virtual address of next byte of free memory下一个空闲内存的虚拟地址
char *result;
//如果这是第一次,则初始化 nextfree。
// 'end' 是链接器自动生成的符号,指向内核 bss 段的结尾:链接器*未*分配给任何内核代码或全局变量的第一个虚拟地址。
if (!nextfree) {
extern char end[];
nextfree = ROUNDUP((char *) end, PGSIZE);// 向上舍入到最接近的 n 倍数分配了4K个字节的内存
}
// 分配一个足够大的块来容纳“n”个字节,然后更新下一个空闲。确保 nextfree 保持对齐为 PGSIZE 的倍数。
result=nextfree;
nextfree=ROUNDUP(nextfree+n,PGSIZE);
if((uint32_t)(nextfree-KERNBASE)>npages*PGSIZE)//如果需要的内存超过了物理内存的总量
panic("out of memory!\n");
return result;
}
page_init()
初始化页面结构和内存空闲列表。完成此操作后,不能再次使用 boot_alloc。 只通过 page_free_list来进行物理内存的分配与取消。
根据注释完成的代码:
(1) 第零页的物理内存留给实模式的IDT以及BIOS系统。
(2) 剩下的[PGSIZE, npages_basemem * PGSIZE)的基本内存空闲。
(3)IO口[IOPHYSMEM, EXTPHYSMEM)的内存不能被分配。
(4) 在extend memory中已经被使用的内存,如内核,该内存可以通过boot_alloc()得到。
size_t npages; // Amount of physical memory (in pages)物理内存页面总数
static size_t npages_basemem; // Amount of base memory (in pages)基本内存页面总数
struct PageInfo *pages; // Physical page state array物理页状态数组,每个元素都是物理页面。
static struct PageInfo *page_free_list; // Free list of physical pages物理页空闲表
void
page_init(void)
{
pages[0].pp_ref=1;//IDT、BIOS
pages[0].pp_link=NULL;
size_t i;
for (i = 1; i < npages_basemem; i++) {//BASEMEM
pages[i].pp_ref = 0;//引用计数设为零
pages[i].pp_link = page_free_list;//头插,指向下一个空闲链表
page_free_list = &pages[i];
}
for (i ; i < EXTPHYSMEM/PGSIZE; i++) {
pages[i].pp_ref = 1;
}
//扩展内存之外的,有哪些内存被使用了?boot_alloc()
size_t epuse=(size_t)PADDR(boot_alloc(0))/PGSIZE;
for(i ; i<epuse;i++){
pages[i].pp_ref=1;//引用计数设为1,被kernel使用
pages[i].pp_link=NULL;//不加入空闲链表
}
for(i;i<npages;i++){//EXTMEM中的空闲页面
pages[i].pp_ref = 0;
pages[i].pp_link = page_free_list;
page_free_list = &pages[i];
}
}
page_alloc()
分配一个物理页。 if (alloc_flags & ALLOC_ZERO),填充整个返回的物理页带 0字节,且不增加页面的引用计数。
struct PageInfo *
page_alloc(int alloc_flags)
{
if(!page_free_list)//如果没有空闲页面
return NULL;
struct PageInfo* result;//分配的页面
struct PageInfo* next=page_free_list->pp_link;//空闲页面链表的下一个节点
page_free_list->pp_link=NULL;//将空闲页面的第一页分配,防止双重释放的错误。
result=page_free_list;
page_free_list=next;//更新链表头
if(alloc_flags&ALLOC_ZERO){//是否将分配的内存填0
memset(page2kva(result),0,PGSIZE);//因为C函数中的指针都是虚拟地址,因此将分配的页面转化为虚拟地址后再进行memset操作
}
// cprintf("xxx%x\n:",page2pa(result));
return result;
}
page_free()
当物理页面的引用计数达到0时,证明页面不再存在映射,因此将此物理页面释放到空闲页面的链表中。
void
page_free(struct PageInfo *pp)
{
if(pp->pp_ref!=0||(pp->pp_link))//当pp->pp_link==NULL的时候才证明不属于空闲链表
panic("page not free");
pp->pp_link=page_free_list;//插入到空闲页面头
page_free_list=pp;
return;
}
以上就是物理页面管理的全部实现。