__zone_watermark_ok分析
最近在学习linux 内存管理这一块。
看到了函数__zone_watermark_ok。想起来之前也曾经见过该函数,当时是在分析init.rc的内存,最终也看到了这个函数,不过当时对其并不是很理解。
最近又遇到了它,结合积攒的一点知识,总算对其有了个大致了解。
先概述一下其用途。
从名称来看,zone, watermaker, 还有ok。
直观上来看,就是在某个zone上分配内存时,检查是否满足给定的watermark的要求。
zone就是memory zone,类型种类如下:
enum zone_type {
#ifdef CONFIG_ZONE_DMA
/*
* ZONE_DMA is used when there are devices that are not able
* to do DMA to all of addressable memory (ZONE_NORMAL). Then we
* carve out the portion of memory that is needed for these devices.
* The range is arch specific.
*
* Some examples
*
* Architecture Limit
* ---------------------------
* parisc, ia64, sparc <4G
* s390 <2G
* arm Various
* alpha Unlimited or 0-16MB.
*
* i386, x86_64 and multiple other arches
* <16M.
*/
ZONE_DMA,
#endif
#ifdef CONFIG_ZONE_DMA32
/*
* x86_64 needs two ZONE_DMAs because it supports devices that are
* only able to do DMA to the lower 16M but also 32 bit devices that
* can only do DMA areas below 4G.
*/
ZONE_DMA32,
#endif
/*
* Normal addressable memory is in ZONE_NORMAL. DMA operations can be
* performed on pages in ZONE_NORMAL if the DMA devices support
* transfers to all addressable memory.
*/
ZONE_NORMAL,
#ifdef CONFIG_HIGHMEM
/*
* A memory area that is only addressable by the kernel through
* mapping portions into its own address space. This is for example
* used by i386 to allow the kernel to address the memory beyond
* 900MB. The kernel will set up special mappings (page
* table entries on i386) for each page that the kernel needs to
* access.
*/
ZONE_HIGHMEM,
#endif
ZONE_MOVABLE,
__MAX_NR_ZONES
};
watermark的种类:
enum zone_watermarks {
WMARK_MIN, // 说明当前可以内存达到最低限了
WMARK_LOW, // 可用内存很少了,但还没到最低限,不是很紧急的情况,就不要占用内存了
WMARK_HIGH, // 剩余内存丰富,大家放心使用
NR_WMARK
};
来看看__zone_watermark_ok的代码。
/*
* Return true if free pages are above 'mark'. This takes into account the order
* of the allocation.
*/
static bool __zone_watermark_ok(struct zone *z, int order, unsigned long mark,
int classzone_idx, int alloc_flags, long free_pages)
{
/* free_pages may go negative - that's OK */
long min = mark; // 既然是检查是否满足mark要求,先将最小值设置为mark
int o;
free_pages -= (1 << order) + 1; // 检查谁满足mark要求?是free pages,并且是分配出去2^order个page之后的free pages. 为何要+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]) // 如果free pages已经不大于保留内存和min之和,说明此次分配请求不满足wartmark要求
return false;
for (o = 0; o < order; o++) { // 遍历buddy中比当前请求分配的order小的所有的order,依次检查free pages是否满足watermark要求。
/* At the next order, this order's pages become unavailable */
// 每个循环当中,先把当前order(从0至请求的order-1)的free pages从总free pages中减掉
free_pages -= z->free_area[o].nr_free << o;
// 然后将min,即mark,即判断标准作相应的缩小
// linux中默认每次缩小一半,
// android中引入了min_free_order_shift配置项,并且默认配置为4
/* Require fewer higher order pages to be free */
min >>= min_free_order_shift;
// 比较处理后的free pages和min,看是否满足water mark要求
if (free_pages <= min)
return false;
}
return true;
}
函数中循环的目的可归结为:
依次循环,检查内存中是否有足够多的大块(即order比较高)空闲内存。
每次循环处理中,先把当前order的free page从总free pages中减掉,因为我们是看是否有足够多的大块内存。
当然,既然已经把free pages中的一部分已经划掉了,比较标准也应该相应放宽。
放宽多少,就是前面说的对min的右移处理。
举个例子,
如果请求分配的order是1,还有100个free pages,其中order 0的有96 pages,order 1的有4pages,处理后的min是16.
这样在第一轮循环中,free_pages即变为4,min假设右移了1位为8,这样判断下来不满足watermark要求。
如果将要求放宽,即将min_free_order_shift设置为4,这样第一轮循环中min变为1,free pages满足watermark要求。