linux 清理内存的函数,Linux中的内存分配和释放之mem_init()函数分析

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()就介绍完了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值