linux内存管理分析7Word文件下载.docx
《linux内存管理分析7Word文件下载.docx》由会员分享,可在线阅读,更多相关《linux内存管理分析7Word文件下载.docx(17页珍藏版)》请在冰豆网上搜索。
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_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);
......
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);
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);
如果分配不成功就降低限制值,启动内存回收机制,然后分配内存
page=__alloc_pages_slowpath(gfp_mask,order,
zonelist,high_zoneidx,nodemask,
preferred_zone,migratetype);
trace_mm_page_alloc(page,order,gfp_mask,migratetype);
out:
快速内存分配
__alloc_pages_nodemask--->
get_page_from_freelist】
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))
如果设置了低水印标志,并且为写分配页,就检查内存域的赃页是否超过其限制值,如果赃页过多就设置该内存域得满标志
ALLOC_WMARK_LOW)&
(gfp_mask&
__GFP_WRITE)&
zone_dirty_ok(zone))
gotothis_zone_full;
BUILD_BUG_ON(ALLOC_NO_WATERMARKS<
NR_WMARK);
判断是否检测水印值
(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;
指定内存域中内存不足,准备到其他内存域中分配内存
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)
检查当前CPU是否可以在内存域Z所在结点中分配内存,和该内存域是否还有已满。
启用内存回收机制
ret=zone_reclaim(zone,gfp_mask,order);
switch(ret){
caseZONE_RECLAIM_NOSCAN:
没有进行回收扫描
caseZONE_RECLAIM_FULL:
扫描了但是没有可回收的页
default:
回收了部分页,查看是否可以满足分配要求
zone_watermark_ok(zone,order,mark,
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;
get_page_from_freelist--->
buffered_rmqueue】
staticinlinestructpage*buffered_rmqueue(structzone*preferred_zone,
structzone*zone,intorder,gfp_tgfp_flags,
intmigratetype)
unsignedlongflags;
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)){
count+=rmqueue_bulk(zone,0,
batch,list,
migratetype,cold);
if(unlikely(list_empty(list)))
gotofailed;
如果分配的是热页就从链表头取页帧,如果分配的是冷页就从链表尾取页帧
if(cold)
page=list_entry(list->
prev,structpage,lru);
next,structpage,lru);
将页帧从每CPU缓存中删除
list_del(&
page->
lru);
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(&
lock);
page)
__mod_zone_page_state(zone,NR_FREE_PAGES,-(1<
<
order));
__count_zone_vm_events(PGALLOC,zone,1<
order);
更新统计量
zone_statistics(preferred_zone,zone,gfp_flags);
failed:
local_irq_restore(flags);
returnNULL;
buffered_rmqueue--->
rmqueue_bulk】
staticintrmqueue_bulk(structzone*zone,unsignedintorder,
unsignedlongcount,structlist_head*list,
intmigratetype,intcold)
intmt=migratetype,i;
spin_lock(&
从伙伴系统中循环分配指定的页数
for(i=0;
i<
count;
++i){
从伙伴系统中分配指定迁移类型的页
structpage*page=__rmqueue(zone,order,migratetype);
if(unlikely(page==NULL))
如果分配热页就将分配到的页链接到链表的头部,如果是冷页就将其链接到链表尾
if(likely(cold==0))
list_add(&
lru,list);
list_add_tail(&
if(IS_ENABLED(CONFIG_CMA)){
获取页的迁移类型
mt=get_pageblock_migratetype(page);
is_migrate_cma(mt)&
mt!
=MIGRATE_ISOLATE)
mt=migratetype;
设置页的迁移类型
set_page_private(page,mt);
lru;
更新状态统计信息
__mod_zone_page_state(zone,NR_FREE_PAGES,-(i<
returni;
rmqueue_bulk--->
__rmqueue】
staticstructpage*__rmqueue(structzone*zone,unsignedintorder,intmigratetype)
retry_reserve:
在内存域zone的空闲列表中,扫描order及以上的各阶,分配迁移类型为migratetype阶为order的页。
page=__rmqueue_smallest(zone,order,migratetype);
如果没有分配到指定的页,就调用函数__rmqueue_fallback从迁移类型备用列表中分配满足要求的页
page)&
migratetype!
=MIGRATE_RESERVE){
page=__rmqueue_fallback(zone,order,migratetype);
page){
migratetype=MIGRATE_RESERVE;
gotoretry_reserve;
trace_mm_page_alloc_zone_locked(page,order,migratetype);
__rmqueue--->
__rmqueue_smallest】
staticinlinestructpage*__rmqueue_smallest(structzone*zone,unsignedintorder,
unsignedintcurrent_order;
structfree_area*area;
遍历order以上的每一个分配阶,直到找到有迁移类型为migratetype的空闲页块为止。
for(current_order=order;
current_order<
MAX_ORDER;
++current_order){
area=&
(zone->
free_area[current_order]);
if(list_empty(&
area->
free_list[migratetype]))
page=list_entry(area->
free_list[migratetype].next,
structpage,lru);
将页从链表中删除
删除伙伴系统标志,将页阶设为0
rmv_page_order(page);
递减相应链表的空闲页计数
nr_free--;
将高阶页块拆为低阶页块连接到低阶空闲链表中
expand(zone,page,order,current_order,area,migratetype);
__rmqueue_smallest--->
expand】
staticinlinevoidexpand(structzone*zone,structpage*page,
intlow,inthigh,structfree_area*area,
unsignedlongsize=1<
high;
Low是将要分配的页块的阶,high是找到的有空闲页快的阶。
将高阶页块拆分为低阶页块链接到低阶空闲链表中去。
while(high>
low){
area--;
high--;
size>
>
=1;
page[size].lru,&
free_list[migratetype]);
nr_free++;
增加相应阶空闲页计数
set_page_order(&
page[size],high);
设置首页阶数
__rmqueue_fallback】
staticinlinestructpage*__rmqueue_fallback(structzone*zone,intorder,intstart_migratetype)
intcurrent_order;
intmigratetype,i;
函数__rmqueue_smallest中没有分配到指定迁移类型的页块。
在函数__rmqueue_fallback中就要从其他迁移类型的链表中取来一个页块,转化为指定迁移类型。
为了避免引起内存碎片,在转化迁移类型时尽量挑大的内存块。
for(current_order=MAX_ORDER-1;
current_order>
=order;
--current_order){
;
i++){
数组Fallbacks[]是一个迁移类型相关的表,它记录了,如果在一种指定迁移类型中没有找到空闲页块,接下来该到那种迁移类型的链表中去分配空闲页块
migratetype=fallbacks[start_migratetype][i];
if(migratetype==MIGRATE_RESERVE)
到对应页阶中去找对应迁移类型的链表。
pageblock