linux内存管理分析7.docx

上传人:b****6 文档编号:7900038 上传时间:2023-01-27 格式:DOCX 页数:17 大小:20.10KB
下载 相关 举报
linux内存管理分析7.docx_第1页
第1页 / 共17页
linux内存管理分析7.docx_第2页
第2页 / 共17页
linux内存管理分析7.docx_第3页
第3页 / 共17页
linux内存管理分析7.docx_第4页
第4页 / 共17页
linux内存管理分析7.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

linux内存管理分析7.docx

《linux内存管理分析7.docx》由会员分享,可在线阅读,更多相关《linux内存管理分析7.docx(17页珍藏版)》请在冰豆网上搜索。

linux内存管理分析7.docx

linux内存管理分析7

linux内存管理分析【七】

2012-12-1117:

11:

15|分类:

默认分类|标签:

|字号大中小订阅

6.4内存页的分配

include/linux/gfp.h

staticinlinestructpage*alloc_pages(gfp_tgfp_mask,unsignedintorder)

{

returnalloc_pages_current(gfp_mask,order);

}

【alloc_pages--->alloc_pages_current】

mm/mempolicy.c

structpage*alloc_pages_current(gfp_tgfp,unsignedorder)

{

structmempolicy*pol=current->mempolicy;

structpage*page;

unsignedintcpuset_mems_cookie;

获取内存分配策略

if(!

pol||in_interrupt()||(gfp&__GFP_THISNODE))

pol=&default_policy;

retry_cpuset:

cpuset_mems_cookie=get_mems_allowed();

if(pol->mode==MPOL_INTERLEAVE)

函数interleave_nodes根据分配策略返回内存分配的结点

page=alloc_page_interleave(gfp,order,interleave_nodes(pol));

else

在内存域备用列表中分配阶为order的页块

page=__alloc_pages_nodemask(gfp,order,

policy_zonelist(gfp,pol,numa_node_id()),

policy_nodemask(gfp,pol));

如果分配不成功就重新尝试分配

if(unlikely(!

put_mems_allowed(cpuset_mems_cookie)&&!

page))

gotoretry_cpuset;

returnpage;

}

【alloc_pages--->alloc_pages_current--->__alloc_pages_nodemask】

mm/page_alloc.c

structpage*__alloc_pages_nodemask(gfp_tgfp_mask,unsignedintorder,

structzonelist*zonelist,nodemask_t*nodemask)

{

enumzone_typehigh_zoneidx=gfp_zone(gfp_mask);

......

retry_cpuset:

high_zoneidx是参数中指定内存域,函数first_zones_zonelist就是找到该内存域preferred_zone,但在这个内存域中不一定能成功分配指定内存。

程序扫描备用内存域列表zonelist,最后成功分配内存的内存域甚至可能与preferred_zone根本不在一个节点中。

程序会根据这些情况来更新NUMA_HIT,NUMA_MISS,NUMA_FOREIGN这几个统计量。

first_zones_zonelist(zonelist,high_zoneidx,nodemask?

:

&cpuset_current_mems_allowed,&preferred_zone);

if(!

preferred_zone)

gotoout;

考虑内存限制,指定迁移类型,的情况下快速分配内存

page=get_page_from_freelist(gfp_mask|__GFP_HARDWALL,nodemask,order,zonelist,high_zoneidx,ALLOC_WMARK_LOW|ALLOC_CPUSET,preferred_zone,migratetype);

if(unlikely(!

page))

如果分配不成功就降低限制值,启动内存回收机制,然后分配内存

page=__alloc_pages_slowpath(gfp_mask,order,

zonelist,high_zoneidx,nodemask,

preferred_zone,migratetype);

trace_mm_page_alloc(page,order,gfp_mask,migratetype);

out:

if(unlikely(!

put_mems_allowed(cpuset_mems_cookie)&&!

page))

gotoretry_cpuset;

returnpage;

}

快速内存分配

【alloc_pages--->alloc_pages_current--->__alloc_pages_nodemask--->get_page_from_freelist】

mm/page_alloc.c

staticstructpage*

get_page_from_freelist(gfp_tgfp_mask,nodemask_t*nodemask,unsignedintorder,

structzonelist*zonelist,inthigh_zoneidx,intalloc_flags,

structzone*preferred_zone,intmigratetype)

{

......

获取preferred_zone的内存区域类型,如ZONE_NORMAL……

classzone_idx=zone_idx(preferred_zone);

zonelist_scan:

遍历类型值小于high_zoneidx的所欲内存域

for_each_zone_zonelist_nodemask(zone,z,zonelist,

high_zoneidx,nodemask){

第一次扫描zonelist时zlc_active为0,表示不需要判断内存域Z是否有可分配内存。

if(NUMA_BUILD&&zlc_active&&

!

zlc_zone_worth_trying(zonelist,z,allowednodes))

continue;

如果设置了标志ALLOC_CPUSET,就判断当前CPU是否允许在内存域zone所在结点中分配内存。

if((alloc_flags&ALLOC_CPUSET)&&

!

cpuset_zone_allowed_softwall(zone,gfp_mask))

continue;

如果设置了低水印标志,并且为写分配页,就检查内存域的赃页是否超过其限制值,如果赃页过多就设置该内存域得满标志

if((alloc_flags&ALLOC_WMARK_LOW)&&

(gfp_mask&__GFP_WRITE)&&!

zone_dirty_ok(zone))

gotothis_zone_full;

BUILD_BUG_ON(ALLOC_NO_WATERMARKS

判断是否检测水印值

if(!

(alloc_flags&ALLOC_NO_WATERMARKS)){

unsignedlongmark;

intret;

数组zone->watermark中记录了该内存域高、中、低三种水印值,下面获取指定所采用的水印值。

mark=zone->watermark[alloc_flags&ALLOC_WMARK_MASK];

检测内存域中空闲页是否符合水印限制

if(zone_watermark_ok(zone,order,mark,

classzone_idx,alloc_flags))

gototry_this_zone;

指定内存域中内存不足,准备到其他内存域中分配内存

if(NUMA_BUILD&&!

did_zlc_setup&&nr_online_nodes>1){

allowednodes=zlc_setup(zonelist,alloc_flags);

zlc_active=1;

did_zlc_setup=1;

}

if(zone_reclaim_mode==0)

gotothis_zone_full;

检查当前CPU是否可以在内存域Z所在结点中分配内存,和该内存域是否还有已满。

if(NUMA_BUILD&&zlc_active&&

!

zlc_zone_worth_trying(zonelist,z,allowednodes))

continue;

启用内存回收机制

ret=zone_reclaim(zone,gfp_mask,order);

switch(ret){

caseZONE_RECLAIM_NOSCAN:

continue;没有进行回收扫描

caseZONE_RECLAIM_FULL:

continue;扫描了但是没有可回收的页

default:

回收了部分页,查看是否可以满足分配要求

if(!

zone_watermark_ok(zone,order,mark,

classzone_idx,alloc_flags))

gotothis_zone_full;

}

}

try_this_zone:

从zone中分配order阶的页帧

page=buffered_rmqueue(preferred_zone,zone,order,

gfp_mask,migratetype);

if(page)

break;

this_zone_full:

if(NUMA_BUILD)将内存域z标记为满

zlc_mark_zone_full(zonelist,z);

}

if(unlikely(NUMA_BUILD&&page==NULL&&zlc_active)){

zlc_active=0;

gotozonelist_scan;

}

returnpage;

}

【alloc_pages--->alloc_pages_current--->__alloc_pages_nodemask--->get_page_from_freelist--->buffered_rmqueue】

mm/page_alloc.c

staticinlinestructpage*buffered_rmqueue(structzone*preferred_zone,

structzone*zone,intorder,gfp_tgfp_flags,

intmigratetype)

{

unsignedlongflags;

structpage*page;

intcold=!

!

(gfp_flags&__GFP_COLD);

again:

if(likely(order==0)){

structper_cpu_pages*pcp;

structlist_head*list;

local_irq_save(flags);

获取每CPU缓存管理结构

pcp=&this_cpu_ptr(zone->pageset)->pcp;

获取每CPU缓存中指定迁移类型的链表头

list=&pcp->lists[migratetype];

如果指定迁移类型的链表中没有空闲页帧,就从伙伴系统中分配一批(pcp->batch)页帧到每CPU缓存中来。

if(list_empty(list)){

pcp->count+=rmqueue_bulk(zone,0,

pcp->batch,list,

migratetype,cold);

if(unlikely(list_empty(list)))

gotofailed;

}

如果分配的是热页就从链表头取页帧,如果分配的是冷页就从链表尾取页帧

if(cold)

page=list_entry(list->prev,structpage,lru);

else

page=list_entry(list->next,structpage,lru);

将页帧从每CPU缓存中删除

list_del(&page->lru);

pcp->count--;减少每CPU缓存计数

}else{

if(unlikely(gfp_flags&__GFP_NOFAIL)){

WARN_ON_ONCE(order>1);

}

如果分配的是多页就直接从伙伴系统中分配页帧

spin_lock_irqsave(&zone->lock,flags);

page=__rmqueue(zone,order,migratetype);

spin_unlock(&zone->lock);

if(!

page)

gotofailed;

__mod_zone_page_state(zone,NR_FREE_PAGES,-(1<

}

__count_zone_vm_events(PGALLOC,zone,1<

更新统计量

zone_statistics(preferred_zone,zone,gfp_flags);

......

failed:

local_irq_restore(flags);

returnNULL;

}

【alloc_pages--->alloc_pages_current--->__alloc_pages_nodemask--->get_page_from_freelist--->buffered_rmqueue--->rmqueue_bulk】

staticintrmqueue_bulk(structzone*zone,unsignedintorder,

unsignedlongcount,structlist_head*list,

intmigratetype,intcold)

{

intmt=migratetype,i;

spin_lock(&zone->lock);

从伙伴系统中循环分配指定的页数

for(i=0;i

从伙伴系统中分配指定迁移类型的页

structpage*page=__rmqueue(zone,order,migratetype);

if(unlikely(page==NULL))

break;

如果分配热页就将分配到的页链接到链表的头部,如果是冷页就将其链接到链表尾

if(likely(cold==0))

list_add(&page->lru,list);

else

list_add_tail(&page->lru,list);

if(IS_ENABLED(CONFIG_CMA)){

获取页的迁移类型

mt=get_pageblock_migratetype(page);

if(!

is_migrate_cma(mt)&&mt!

=MIGRATE_ISOLATE)

mt=migratetype;

}

设置页的迁移类型

set_page_private(page,mt);

list=&page->lru;

}

更新状态统计信息

__mod_zone_page_state(zone,NR_FREE_PAGES,-(i<

spin_unlock(&zone->lock);

returni;

}

【alloc_pages--->alloc_pages_current--->__alloc_pages_nodemask--->get_page_from_freelist--->buffered_rmqueue--->rmqueue_bulk--->__rmqueue】

staticstructpage*__rmqueue(structzone*zone,unsignedintorder,intmigratetype)

{

structpage*page;

retry_reserve:

在内存域zone的空闲列表中,扫描order及以上的各阶,分配迁移类型为migratetype阶为order的页。

page=__rmqueue_smallest(zone,order,migratetype);

如果没有分配到指定的页,就调用函数__rmqueue_fallback从迁移类型备用列表中分配满足要求的页

if(unlikely(!

page)&&migratetype!

=MIGRATE_RESERVE){

page=__rmqueue_fallback(zone,order,migratetype);

if(!

page){

migratetype=MIGRATE_RESERVE;

gotoretry_reserve;

}

}

trace_mm_page_alloc_zone_locked(page,order,migratetype);

returnpage;

}

【alloc_pages--->alloc_pages_current--->__alloc_pages_nodemask--->get_page_from_freelist--->buffered_rmqueue--->rmqueue_bulk--->__rmqueue--->__rmqueue_smallest】

staticinlinestructpage*__rmqueue_smallest(structzone*zone,unsignedintorder,

intmigratetype)

{

unsignedintcurrent_order;

structfree_area*area;

structpage*page;

遍历order以上的每一个分配阶,直到找到有迁移类型为migratetype的空闲页块为止。

for(current_order=order;current_order

area=&(zone->free_area[current_order]);

if(list_empty(&area->free_list[migratetype]))

continue;

page=list_entry(area->free_list[migratetype].next,

structpage,lru);

将页从链表中删除

list_del(&page->lru);

删除伙伴系统标志,将页阶设为0

rmv_page_order(page);

递减相应链表的空闲页计数

area->nr_free--;

将高阶页块拆为低阶页块连接到低阶空闲链表中

expand(zone,page,order,current_order,area,migratetype);

returnpage;

}

returnNULL;

}

【alloc_pages--->alloc_pages_current--->__alloc_pages_nodemask--->get_page_from_freelist--->buffered_rmqueue--->rmqueue_bulk--->__rmqueue--->__rmqueue_smallest--->expand】

staticinlinevoidexpand(structzone*zone,structpage*page,

intlow,inthigh,structfree_area*area,

intmigratetype)

{

unsignedlongsize=1<

Low是将要分配的页块的阶,high是找到的有空闲页快的阶。

将高阶页块拆分为低阶页块链接到低阶空闲链表中去。

while(high>low){

area--;

high--;

size>>=1;

list_add(&page[size].lru,&area->free_list[migratetype]);

area->nr_free++;增加相应阶空闲页计数

set_page_order(&page[size],high);设置首页阶数

}

}

【alloc_pages--->alloc_pages_current--->__alloc_pages_nodemask--->get_page_from_freelist--->buffered_rmqueue--->rmqueue_bulk--->__rmqueue--->__rmqueue_fallback】

staticinlinestructpage*__rmqueue_fallback(structzone*zone,intorder,intstart_migratetype)

{

structfree_area*area;

intcurrent_order;

structpage*page;

intmigratetype,i;

函数__rmqueue_smallest中没有分配到指定迁移类型的页块。

在函数__rmqueue_fallback中就要从其他迁移类型的链表中取来一个页块,转化为指定迁移类型。

为了避免引起内存碎片,在转化迁移类型时尽量挑大的内存块。

for(current_order=MAX_ORDER-1;current_order>=order;

--current_order){

for(i=0;;i++){

数组Fallbacks[]是一个迁移类型相关的表,它记录了,如果在一种指定迁移类型中没有找到空闲页块,接下来该到那种迁移类型的链表中去分配空闲页块

migratetype=fallbacks[start_migratetype][i];

if(migratetype==MIGRATE_RESERVE)

break;

到对应页阶中去找对应迁移类型的链表。

area=&(zone->free_area[current_order]);

if(list_empty(&area->free_list[migratetype]))

continue;

page=list_entry(area->free_list[migratetype].next,

structpage,lru);

area->nr_free--;

pageblock

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 工学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1