linux内存管理分析7.docx
《linux内存管理分析7.docx》由会员分享,可在线阅读,更多相关《linux内存管理分析7.docx(17页珍藏版)》请在冰豆网上搜索。
![linux内存管理分析7.docx](https://file1.bdocx.com/fileroot1/2023-1/26/5dd200fe-c61b-4295-a109-1762298c8a2f/5dd200fe-c61b-4295-a109-1762298c8a2f1.gif)
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_orderarea=&(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