这个源文件主要讲的是页分配的过程。
enum palloc_flags
{
PAL_ASSERT = 001, /* Panic on failure. */
PAL_ZERO = 002, /* Zero page contents. */
PAL_USER = 004 /* User page. */
};
页分配时可以有三种状态,
PAL_ASSERT:如果页不能被分配,则kernel panics
PAL_ZERO:分配页时清零。
PAL_USER:从user pool中分配页。
前面两种是从kernel pool中分配页。
我们把内存分成两种类型。一种是user pool,一种是kernel pool。
struct pool
{
struct lock lock; /* Mutual exclusion. */
struct bitmap *used_map; /* Bitmap of free pages. */
uint8_t *base; /* Base of pool. */
};
这里我们用bitmap来表明空闲的页。这个和数据库的位图索引等都是差不多的。
lock是为了在寻找bitmap的时候只能是原子的。
void
palloc_init (size_t user_page_limit)
{
/* Free memory starts at 1 MB and runs to the end of RAM. */
uint8_t *free_start = ptov (1024 * 1024); //起始从1MB开始
uint8_t *free_end = ptov (init_ram_pages * PGSIZE); //结束是ram的页数*4KB
size_t free_pages = (free_end - free_start) / PGSIZE; //一共有几页空的
size_t user_pages = free_pages / 2; //如果一半空间不到user_page_limit,则不变,如果大于user_page_limit,则缩小为这个值。
size_t kernel_pages; //
if (user_pages > user_page_limit)
user_pages = user_page_limit;
kernel_pages = free_pages - user_pages; //其余的空闲的页赋给内核。
/* Give half of memory to kernel, half to user. */
init_pool (&kernel_pool, free_start, kernel_pages, "kernel pool");
init_pool (&user_pool, free_start + kernel_pages * PGSIZE,
user_pages, "user pool");
}
这个是内存的分池,user_page_limit参数是为了限制用户池的大小一定要在这个大小以内。
void *
palloc_get_multiple (enum palloc_flags flags, size_t page_cnt)
{
struct pool *pool = flags & PAL_USER ? &user_pool : &kernel_pool; //选择pool的类型。
void *pages;
size_t page_idx;
if (page_cnt == 0) //需求的页数量为0
return NULL;
lock_acquire (&pool->lock);
page_idx = bitmap_scan_and_flip (pool->used_map, 0, page_cnt, false); //寻找第一个连续page_cnt的空页的页位置,即是第几个页。
lock_release (&pool->lock);
if (page_idx != BITMAP_ERROR)
pages = pool->base + PGSIZE * page_idx; //找到页具体位置
else
pages = NULL;
if (pages != NULL)
{
if (flags & PAL_ZERO)
memset (pages, 0, PGSIZE * page_cnt); //如果PAL_ZERO,则分配是还要清零。
}
else
{
if (flags & PAL_ASSERT)
PANIC ("palloc_get: out of pages");
}
return pages;
}
//-----------------------------------------------------------------------------------------------------
static bool
page_from_pool (const struct pool *pool, void *page)
{
size_t page_no = pg_no (page);
size_t start_page = pg_no (pool->base);
size_t end_page = start_page + bitmap_size (pool->used_map);
return page_no >= start_page && page_no < end_page;
}
pg_no是把虚拟地址转为他所在的页号,因此如果page_no在start_page和end_page之间,则说明这个页已经分配了。
当free-page时要把这些页设为0xcc。
//-------------------------------------------------------------------------------------------------------
static void
init_pool (struct pool *p, void *base, size_t page_cnt, const char *name)
//池中除了要放空闲页,还要放bitmap,这个是用来记录的。因此bm_pages就是bitmap所占的页数。
p池为从base开始page_cnt页数的一个池,。
{
size_t bm_pages = DIV_ROUND_UP (bitmap_buf_size (page_cnt), PGSIZE);
if (bm_pages > page_cnt) //如果分配给池的大小还不能装的下bitmap。则不够。
PANIC ("Not enough memory in %s for bitmap.", name);
page_cnt -= bm_pages; //page_cnt-bm_pages为真正空闲的页数。
printf ("%zu pages available in %s.\n", page_cnt, name);
/* Initialize the pool. */
lock_init (&p->lock);
p->used_map = bitmap_create_in_buf (page_cnt, base, bm_pages * PGSIZE);
p->base = base + bm_pages * PGSIZE; //池的基地址是原来的base+bitmap_page*PGSIZE;这才是真正的基地址。池的base是指除了bitmap空间的基地址。
}
高
| || <-----整体是一个pool
|--------------------------------------------------------------------- || <-----page_get_multiple()后返回的指针
| 分配的空间 || 地址
| ||
|----------------------------------------------------------------------|| <-----base (一直不会变)
|DIV_ROUND_UP (bitmap_buf_size (page_cnt), PGSIZE);|| <-----page_cnt要用的空间
|----------------------------------------------------------------------|| |低