博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux 内存管理 代码,《LINUX3.0内核源代码分析》第四章:内存管理(3)
阅读量:6904 次
发布时间:2019-06-27

本文共 10003 字,大约阅读时间需要 33 分钟。

1.1.1.1快速分配流程

/**

*遍历管理区列表,分配一个页面。

*gfp_mask:分配标志。

*nodemask:在哪些节点中进行分配,一般未指定。

*order:分配的页面数量为2^order。

*zonelist:在哪些管理区中分配,根据gfs_mask确定。

*high_zoneidx:从哪一个管理区开始分配。

*alloc_flags:分配标志,此标志可以用于控制水线。

*preferred_zone:优先从这个管理区开始分配。

*migratetype:页面迁移类型.

*/

static struct page *

get_page_from_freelist(gfp_t gfp_mask, nodemask_t *nodemask, unsigned int order,

struct zonelist *zonelist, int high_zoneidx, int alloc_flags,

struct zone *preferred_zone, int migratetype)

{

struct zoneref *z;

struct page *page = NULL;

int classzone_idx;

struct zone *zone;

nodemask_t *allowednodes = NULL;/* zonelist_cache approximation */

int zlc_active = 0;/* set if using zonelist_cache */

int did_zlc_setup = 0;/* just call zlc_setup() one time */

/**

*首先从preferred_zone这个管理区开始分配页面

*/

classzone_idx = zone_idx(preferred_zone);

zonelist_scan:

/*

* Scan zonelist, looking for a zone with enough free.

* See also cpuset_zone_allowed() comment in kernel/cpuset.c.

*/

/**

*在允许的节点中,遍历满足要求的管理区。

*/

for_each_zone_zonelist_nodemask(zone, z, zonelist,

high_zoneidx, nodemask) {

if (NUMA_BUILD && zlc_active &&/*是第一遍分配,在其他管理区中分配页面时需要考虑其页面是否充足*/

!zlc_zone_worth_trying(zonelist, z, allowednodes))/*该管理区页面不是很充足,考虑下一个管理区*/

continue;

if ((alloc_flags & ALLOC_CPUSET) &&

!cpuset_zone_allowed_softwall(zone, gfp_mask))/*当前分配标志不允许在该管理区中分配页面。*/

goto try_next_zone;

BUILD_BUG_ON(ALLOC_NO_WATERMARKS < NR_WMARK);

if (!(alloc_flags & ALLOC_NO_WATERMARKS)) {/*分配时需要考虑水线*/

unsigned long mark;

int ret;

/*根据分配标志,确定使用哪一个水线*/

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

if (zone_watermark_ok(zone, order, mark,

classzone_idx, alloc_flags))/*该管理区的可用内存可以满足本次分配的要求*/

goto try_this_zone;

if (zone_reclaim_mode == 0)

goto this_zone_full;

/*运行到此,说明该管理区中内存不足,需要对该管理区进行回收*/

ret = zone_reclaim(zone, gfp_mask, order);

switch (ret) {

case ZONE_RECLAIM_NOSCAN:/*当前管理区还没有进行回收*/

/* did not scan */

goto try_next_zone;

case ZONE_RECLAIM_FULL:/*进行了回收,但是没有可回收的内存*/

/* scanned but unreclaimable */

goto this_zone_full;

default:/*回收部分内存*/

/* did we reclaim enough */

if (!zone_watermark_ok(zone, order, mark,

classzone_idx, alloc_flags))/*回收的内存较少,仍然不满足分配要求*/

goto this_zone_full;

}

}

/**

*当前管理区中有足够的可用内存,试图在此管理区中分配内存

*/

try_this_zone:

/**

*调用伙伴系统的分配函数

*/

page = buffered_rmqueue(preferred_zone, zone, order,

gfp_mask, migratetype);

if (page)/*从伙伴系统分配成功,退出*/

break;

/*当管理区中不能满足分配要求时,运行到此*/

this_zone_full:

if (NUMA_BUILD)/*标记该管理区空间不足,下次分配时将略过本管理区,避免浪费太多时间*/

zlc_mark_zone_full(zonelist, z);

try_next_zone:

/**

*当前管理区内存不足,需要加大在其他区中的分配力度。

*/

if (NUMA_BUILD && !did_zlc_setup && nr_online_nodes > 1) {

/*

* we do zlc_setup after the first zone is tried but only

* if there are multiple nodes make it worthwhile

*/

allowednodes = zlc_setup(zonelist, alloc_flags);

zlc_active = 1;

did_zlc_setup = 1;

}

}

/**

*第一遍分配不成功,则取消zlc_active,这样会尽量从其他节点中分配内存。

*/

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

/* Disable zlc cache for second zonelist scan */

zlc_active = 0;

goto zonelist_scan;

}

/**

*两次扫描节点,不管是否成功,都向上层返回分配结果。

*/

return page;

}

1.1.1.2慢速分配流程

/**

*当无法快速分配页面时,如果调用者允许等待,则通过本函数进行慢速分配。

*此时允许进行内存回收。

*/

static inline struct page *

__alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,

struct zonelist *zonelist, enum zone_type high_zoneidx,

nodemask_t *nodemask, struct zone *preferred_zone,

int migratetype)

{

const gfp_t wait = gfp_mask & __GFP_WAIT;

struct page *page = NULL;

int alloc_flags;

unsigned long pages_reclaimed = 0;

unsigned long did_some_progress;

bool sync_migration = false;

/*

* In the slowpath, we sanity check order to avoid ever trying to

* reclaim >= MAX_ORDER areas which will never succeed. Callers may

* be using allocators in order of preference for an area that is

* too large.

*/

if (order >= MAX_ORDER) {/*这里进行参数合法性检测*/

WARN_ON_ONCE(!(gfp_mask & __GFP_NOWARN));

return NULL;

}

/*

* GFP_THISNODE (meaning __GFP_THISNODE, __GFP_NORETRY and

* __GFP_NOWARN set) should not cause reclaim since the subsystem

* (f.e. slab) using GFP_THISNODE may choose to trigger reclaim

* using a larger set of nodes after it has established that the

* allowed per node queues are empty and that nodes are

* over allocated.

*/

/**

*调用者指定了GFP_THISNODE标志,表示不能进行内存回收。

*上层调用者应当在指定了GFP_THISNODE失败后,使用其他标志进行分配。

*/

if (NUMA_BUILD && (gfp_mask & GFP_THISNODE) == GFP_THISNODE)

goto nopage;

restart:

if (!(gfp_mask & __GFP_NO_KSWAPD))/*如果调用者没有禁止kswapd,则唤醒该线程进行内存回收。*/

wake_all_kswapd(order, zonelist, high_zoneidx,

zone_idx(preferred_zone));

/*

* OK, we're below the kswapd watermark and have kicked background

* reclaim. Now things get more complex, so set up alloc_flags according

* to how we want to proceed.

*/

alloc_flags = gfp_to_alloc_flags(gfp_mask);/*根据分配标志确定内部标志,主要是用于水线*/

/*

* Find the true preferred zone if the allocation is unconstrained by

* cpusets.

*/

/**

*如果不受cpuset的限制,那么找到真正的优先用于分配的管理区

*/

if (!(alloc_flags & ALLOC_CPUSET) && !nodemask)

first_zones_zonelist(zonelist, high_zoneidx, NULL,

&preferred_zone);

rebalance:

/* This is the last chance, in general, before the goto nopage. */

/**

*与快速分配流程相比,这里的分配标志使用了低的水线。

*在进行内存回收操作前,我们使用低水线再尝试分配一下。

*当然,不管是否允许ALLOC_NO_WATERMARKS标志,我们都将它清除。

*/

page = get_page_from_freelist(gfp_mask, nodemask, order, zonelist,

high_zoneidx, alloc_flags & ~ALLOC_NO_WATERMARKS,

preferred_zone, migratetype);

if (page)/*庆幸的是,分配到内存了,退出。*/

goto got_pg;

/* Allocate without watermarks if the context allows */

if (alloc_flags & ALLOC_NO_WATERMARKS) {/*某些上下文,如内存回收进程及被杀死的任务,都允许它完全突破水线的限制分配内存。*/

page = __alloc_pages_high_priority(gfp_mask, order,

zonelist, high_zoneidx, nodemask,

preferred_zone, migratetype);

if (page)/*在不考虑水线的情况下,分配到了内存*/

goto got_pg;

}

/* Atomic allocations - we can't balance anything */

if (!wait)/*调用者希望原子分配内存,此时不能等待内存回收,返回NULL */

goto nopage;

/* Avoid recursion of direct reclaim */

if (current->flags & PF_MEMALLOC)/*调用者本身就是内存回收进程,不能进入后面的内存回收处理流程,否则死锁*/

goto nopage;

/* Avoid allocations with no watermarks from looping endlessly */

/**

*当前线程正在被杀死,它可以完全突破水线分配内存。这里向上层返回NULL,是为了避免系统进入死循环。

*当然,如果上层调用不允许失败,则死循环继续分配,等待其他线程释放一点点内存。

*/

if (test_thread_flag(TIF_MEMDIE) && !(gfp_mask & __GFP_NOFAIL))

goto nopage;

/*

* Try direct compaction. The first pass is asynchronous. Subsequent

* attempts after direct reclaim are synchronous

*/

/**

*尝试压缩内存。这样可以将一些小的外碎片合并成大页面,这样也许能够满足调用者的内存分配要求。

*内存压缩是通过页面迁移实现的。

*第一次调用的时候,是非同步的。第二次调用则是同步方式。

*/

page = __alloc_pages_direct_compact(gfp_mask, order,

zonelist, high_zoneidx,

nodemask,

alloc_flags, preferred_zone,

migratetype, &did_some_progress,

sync_migration);

if (page)/*庆幸,通过压缩内存,分配到了内存*/

goto got_pg;

sync_migration = true;/*将页面迁移标志设置为同步方式,这样第二次页面迁移都会使用同步方式*/

/* Try direct reclaim and then allocating */

/**

*直接在内存分配上下文中进行内存回收操作。

*/

page = __alloc_pages_direct_reclaim(gfp_mask, order,

zonelist, high_zoneidx,

nodemask,

alloc_flags, preferred_zone,

migratetype, &did_some_progress);

if (page)/*庆幸,回收了一些内存后,满足了上层分配需求*/

goto got_pg;

/*

* If we failed to make any progress reclaiming, then we are

* running out of options and have to consider going OOM

*/

if (!did_some_progress) {/*内存回收过程没有回收到内存,系统真的内存不足了*/

/**

*调用者不是文件系统的代码,允许进行文件系统操作,并且允许重试。

*这里需要__GFP_FS标志可能是进入OOM流程后会杀进程或进入panic,需要文件操作。

*/

if ((gfp_mask & __GFP_FS) && !(gfp_mask & __GFP_NORETRY)) {

if (oom_killer_disabled)/*系统禁止了OOM,向上层返回NULL */

goto nopage;

/**

*杀死其他进程后再尝试分配内存

*/

page = __alloc_pages_may_oom(gfp_mask, order,

zonelist, high_zoneidx,

nodemask, preferred_zone,

migratetype);

if (page)

goto got_pg;

if (!(gfp_mask & __GFP_NOFAIL)) {/*调用者允许失败*/

/*

* The oom killer is not called for high-order

* allocations that may fail, so if no progress

* is being made, there are no other options and

* retrying is unlikely to help.

*/

if (order > PAGE_ALLOC_COSTLY_ORDER)/*要求的页面数量较多,再试意义不大*/

goto nopage;

/*

* The oom killer is not called for lowmem

* allocations to prevent needlessly killing

* innocent tasks.

*/

if (high_zoneidx < ZONE_NORMAL)/*是从DMA区域中分配内存,容易出现内存不足,重试可能也释放不了多少内存*/

goto nopage;

}

goto restart;

}

}

/* Check if we should retry the allocation */

/*内存回收过程回收了一些内存,接下来判断是否有必要继续重试*/

pages_reclaimed += did_some_progress;

if (should_alloc_retry(gfp_mask, order, pages_reclaimed)) {/*如果需要重试,则等待后重试*/

/* Wait for some write requests to complete then retry */

wait_iff_congested(preferred_zone, BLK_RW_ASYNC, HZ/50);

goto rebalance;

} else {/*不需要再重试了*/

/*

* High-order allocations do not necessarily loop after

* direct reclaim and reclaim/compaction depends on compaction

* being called after reclaim so call directly if necessary

*/

/*再次压缩内存,这次是同步方式,如果还分配不到内存就放弃了*/

page = __alloc_pages_direct_compact(gfp_mask, order,

zonelist, high_zoneidx,

nodemask,

alloc_flags, preferred_zone,

migratetype, &did_some_progress,

sync_migration);

if (page)

goto got_pg;

}

nopage:

/*内存分配失败了,打印内存分配失败的警告*/

warn_alloc_failed(gfp_mask, order, NULL);

return page;

got_pg:

if (kmemcheck_enabled)/*运行到这里,说明成功分配了内存,这里进行内存检测调试*/

kmemcheck_pagealloc_alloc(page, order, gfp_mask);

return page;

}

1.1.1.3辅助函数

__zone_watermark_ok函数用于确定管理区的内存水线:

/**

*计算管理区中的水线是否充足

*/

static bool __zone_watermark_ok(struct zone *z, int order, unsigned long mark,

int classzone_idx, int alloc_flags, long free_pages)

{

/* free_pages my go negative - that's OK */

long min = mark;

int o;

/**

*本次内存分配后,剩余的内存数量

*/

free_pages -= (1 << order) + 1;

if (alloc_flags & ALLOC_HIGH)/*在紧急内存中分配,首先将水线减半*/

min -= min / 2;

if (alloc_flags & ALLOC_HARDER)/*内存稍显不足,指定此标志表示适当降低水线*/

min -= min / 4;

/*当前管理区已经低于水线了,退出*/

if (free_pages <= min + z->lowmem_reserve[classzone_idx])

return false;

for (o = 0; o < order; o++) {/*如果是分配大块内存,那么需要将低阶的伙伴系统中的内存去除再判断*/

/* At the next order, this order's pages become unavailable */

free_pages -= z->free_area[o].nr_free << o;/*当前阶的空闲内存要从总的空闲内存中去除*/

/* Require fewer higher order pages to be free */

min >>= 1;/*越高阶,就越要降低内存水线*/

if (free_pages <= min)/*水线不足了,alloc_pages函数应当考虑从其他管理区或者其他节点分配内存,或者传入另外的标志,降低水线标准*/

return false;

}

return true;

}

在内存分配过程中,需要与文件系统的缓存管理、页面回写交互,以进行内存回收,这部分内容在以后的文件系统分析中介绍。

另,内存回收过程是容易出现故障的地方。前一段时间接连处理了多个与内存管理相关的故障,涉及到死循环、dead lock、live lock等等。感兴趣的朋友可以参见http://www.spinics.net/lists/linux-fsdevel/msg45346.html,这里列出了18个相关的补丁。正是由于忙于处理故障,这两周才没有更新日志。

接下来我们将描述一下伙伴系统的主要函数。敬请关注。

转载地址:http://dqodl.baihongyu.com/

你可能感兴趣的文章
数字资产交易所开发:平台币快速吸金的背后
查看>>
小程序自定义音频组件,带滚动条,IOS循环失效问题
查看>>
Swift开发之粒子动画的实现
查看>>
我学Java我傲娇
查看>>
挖矿蠕虫肆虐,详解云防火墙如何轻松“制敌”
查看>>
Linux -- Samba之客户端访问(Linux和windows)
查看>>
八个Docker的真实应用场景
查看>>
vpc的使用方法
查看>>
GitExtensions GitCredentialWinStore syntax error near unexpected token `('
查看>>
Java获取EXE文件图标的方法
查看>>
“驱动程序在 \Device\Harddisk0\D 上检测到控制器错误”的根本解决办法!
查看>>
ubuntu 之修改权限的问题
查看>>
php 框架ci去index.php的方法
查看>>
Hyper-v学习(四),SMB虚拟机实时迁移
查看>>
基于spring3注解的google分页
查看>>
实用命令行工具详解—crontab
查看>>
redis+ssh-keygen免认证登录案例
查看>>
linux svn命令
查看>>
Android中获取CPU负载和进程cpu时间
查看>>
docker容器启动后添加端口映射
查看>>