void __init mem_init(void)
{
unsigned int codepages, datapages, initpages;
int i, node;
codepages = &_etext - &_text;//内核代码段的大小。
datapages = &_end - &__data_start;//内核数据段的大小。
initpages = &__init_end - &__init_begin;//内核初始化相关函数指针空间段的大小。
#ifndef CONFIG_DISCONTIGMEM//如果系统内存空间连续的话。
max_mapnr = virt_to_page(high_memory) - mem_map;//max_mapnr保存着最大低端内存页数。
#endif
if (meminfo.nr_banks != 1)//如果bank不止一个,说明可能存在内存孔洞。
create_memmap_holes(&meminfo);//对每个node的bank进行检查,只要发现前后属于这个node的相邻bank的收尾页号不一
//样的时候,我们就调用free_memmap(),这个函数首先通过页号转换成这两个页对应的
//struct page所在的虚拟地址,然后使这两个虚拟转换成物理地址,并且进行按页对齐,
//最后调用free_bootmem_node()这个函数,使两页之间的页对应的页帧位码表中的相应
//位为0.
for (node = 0; node < numnodes; node++) {//遍历所有节点
pg_data_t *pgdat = NODE_DATA(node);
if (pgdat->node_spanned_pages != 0)
totalram_pages += free_all_bootmem_node(pgdat);//totalram_pages保存释放出低端内存的总页数,我们接下来
//看看free_all_bootmem_node()是如何释放不必要的内存的。
}
unsigned long __init free_all_bootmem_node (pg_data_t *pgdat)
{
return(free_all_bootmem_core(pgdat));
}
static unsigned long __init free_all_bootmem_core(pg_data_t *pgdat)
{
struct page *page;
bootmem_data_t *bdata = pgdat->bdata;
unsigned long i, count, total = 0;
unsigned long idx;
unsigned long *map;
int gofast = 0;
BUG_ON(!bdata->node_bootmem_map);//node_bootmem_map=本node所有内存空间对应的页帧位码表的虚拟地址,如果这
//地址不存在,即指针指向null,系统则崩溃。
count = 0;//操作页数计数器清0
page = virt_to_page(phys_to_virt(bdata->node_boot_start));//page指向该内存node开始页的struct page结构
idx = bdata->node_low_pfn - (bdata->node_boot_start >> PAGE_SHIFT);//本节点低端内存的总页数,包括内存孔洞。
map = bdata->node_bootmem_map;//map指向本node的页帧位码表的虚拟地址。
if (bdata->node_boot_start == 0 ||
ffs(bdata->node_boot_start) - PAGE_SHIFT > ffs(BITS_PER_LONG))//ffs(x)函数可以通过x求出第一个1所在的位号+1,
//当node的起始地址是0或者至少是按2的17次方对齐的,也就是说页号是按
//至少是32对齐的,这样对应的页帧位码表也是按一个字对齐的。
gofast = 1;//这里是做一个标志。
for (i = 0; i < idx; ) {
unsigned long v = ~map[i / BITS_PER_LONG];//v是第i页所在页帧位码表中的位码字的反码
if (gofast && v == ~0UL) {//只有当i页内存的页号能被BITS_PER_LONG整除的时候,我们执行下面的语句。
int j;
count += BITS_PER_LONG;//引用计数器按这个数量级递增。
__ClearPageReserved(page);//对struct page结构flags标志字中的PG_reserved标志位清除。
set_page_count(page, 1);//对page->_count=0,表示这个页可以释放!
for (j = 1; j < BITS_PER_LONG; j++) {//这个for循环就是把这页后面的31页的struct page结构_count设置为0.
if (j + 16 < BITS_PER_LONG)
prefetchw(page + j + 16);
__ClearPageReserved(page + j);
}
__free_pages(page, ffs(BITS_PER_LONG)-1);//释放32页内存页。我们来看看这个函数具体做些什么。
fastcall void __free_pages(struct page *page, unsigned int order)//参数page指向要释放内存空间的首页struct page结构
//指针,参数order是要释放1<
//是页。
{
if (!PageReserved(page) && put_page_testzero(page)) {//这里是判断page->flags成员没有pagereserve标志,page->
//_count=0
if (order == 0)//只是释放一页。
free_hot_page(page);//把该页释放到该页所属内存node的内存页区的当前处理器的“热区”高数缓存内存中。
else
__free_pages_ok(page, order);//否者就调用伙伴系统内存释放操作函数。
}
}//其实这里的释放涉及到“热区”,“冷区”高速缓存内存和伙伴系统的相关知识问题,这里没细讲,我们会在以后的文章去完善的。
i += BITS_PER_LONG;
page += BITS_PER_LONG;
} else if (v) {//这里可以看出,这连续的32页里面有并非空闲的(其实就是说不全是内存孔洞)。
unsigned long m;
for (m = 1; m && i < idx; m<<=1, page++, i++) {
if (v & m) {//通过上面的for再加上这个判断,如果为真的话,就说面改为为1,就说明这个页空闲,可以释放。
count++;
__ClearPageReserved(page);
set_page_count(page, 1);
__free_page(page);//这个是上述__free_pages()的特殊情况,这个是专门释放一页的。
}
}
} else {//说明v=0的,这样就说明这32页全部都不空闲。这样就要增加i和page指针的值,检测后面的32页。
i+=BITS_PER_LONG;
page += BITS_PER_LONG;
}
}
total += count;//统计一共释放了多少空闲页(其实就只是释放孔洞页数)。
page = virt_to_page(bdata->node_bootmem_map);//获取本次内存node所有内存页对应的页帧位码表所在页的首页struct
//page结构体。
count = 0;//操作计数清零。
for (i = 0; i < ((bdata->node_low_pfn-(bdata->node_boot_start >> PAGE_SHIFT))/8 + PAGE_SIZE-1)/PAGE_SIZE;
i++,page++) {//很明显,for循环中间的判断部分其实就是计算页帧位码表占了多少页。
count++;
__ClearPageReserved(page);
set_page_count(page, 1);
__free_page(page);
}//很明显上面就是求得页数后,再把这些页给释放掉了。这样这个内存node就不存在页帧位码表啦。
total += count;
bdata->node_bootmem_map = NULL;//既然存放页帧位码表的空间已经不存在了,我们就是这个指针变量指向null。
return total;
}//现在返回mem_init()函数,继续下面语句的讨论。
printk(KERN_INFO "Memory:");//打印内存信息。
num_physpages = 0;//这个变量是存放系统实际存在的物理内存页的总数的。
for (i = 0; i < meminfo.nr_banks; i++) {
num_physpages += meminfo.bank[i].size >> PAGE_SHIFT;//统计每个bnak有多少物理内存。
printk(" %ldMB", meminfo.bank[i].size >> 20);//内核打印每个bank的容量,以MB为单位。
}
printk(" = %luMB total/n", num_physpages >> (20 - PAGE_SHIFT));
printk(KERN_NOTICE "Memory: %luKB available (%dK code, "
"%dK data, %dK init)/n",(unsigned long) nr_free_pages() << (PAGE_SHIFT-10),codepages >> 10, datapages >>
10, initpages >> 10);//上面两行都是一些打印信息,这里nr_free_pages()函数的作用是统计系统所有zone的free_pages(空闲页)
//的总数量。
if (PAGE_SIZE >= 16384 && num_physpages <= 128) {
extern int sysctl_overcommit_memory;//引入外部变量。
sysctl_overcommit_memory = OVERCOMMIT_ALWAYS;//如果是以上if的物理内存尺寸范围的话,就允许系统过量使用内存。
}
}这样mem_init()就介绍完了。