文章目录
- 一、遍历备用区域列表
- 二、启用 cpuset 检查判定
- 三、判定内存节点的脏页数量
在 【Linux 内核 内存管理】物理分配页 ② ( __alloc_pages_nodemask 函数参数分析 | __alloc_pages_nodemask 函数分配物理页流程 ) 博客中 , 分析了 __alloc_pages_nodemask
函数分配物理页流程如下 :
首先 , 根据 gfp_t gfp_mask
分配标志位 参数 , 得到 " 内存节点 “ 的 首选 ” 区域类型 " 和 " 迁移类型 " ;
然后 , 执行 " 快速路径 " , 第一次分配 尝试使用 低水线分配 ;
如果上述 " 快速路径 " 分配失败 , 则执行 " 慢速路径 " 分配 ;
上述涉及到了 " 快速路径 " 和 " 慢速路径 "
种物理页分配方式 ;
在 【Linux 内核 内存管理】物理分配页 ④ ( __alloc_pages_nodemask 函数源码分析 | 快速路径 | 慢速路径 | get_page_from_freelist 源码 ) 博客中 , 介绍了 快速路径 主要调用 定义在 Linux 内核源码的 linux-4.12mmpage_alloc.c#3017 位置的 get_page_from_freelist
函数 , 分配物理页内存 ;
一、遍历备用区域列表
在 函数中 , 主要操作是遍历 备用区域列表 ,
查找满足如下条件的 内存区域 :
① 区域类型 小于等于 首选区域类型 ,
② 内存节点 对应的 节点掩码 位 被设置为 处理状态 ;
代码语言:javascript复制static struct page *
get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
const struct alloc_context *ac)
{
/*
* Scan zonelist, looking for a zone with enough free.
* See also __cpuset_node_allowed() comment in kernel/cpuset.c.
*/
for_next_zone_zonelist_nodemask(zone, z, ac->zonelist, ac->high_zoneidx,
ac->nodemask){}
}
源码路径 : linux-4.12mmpage_alloc.c#3029
二、启用 cpuset 检查判定
如果 启用了 cpuset 功能 , 用户设置了 ALLOC_CPUSET
标志位 , 要求 检查 cpuset ,
如果 cpuset 不允许当前 进程 分配 该 内存节点 内存页 , 则直接 continue
, 本次循环 " 备用区域列表 " 操作退出 , 执行下一次循环 ;
static struct page *
get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
const struct alloc_context *ac)
{
/*
* Scan zonelist, looking for a zone with enough free.
* See also __cpuset_node_allowed() comment in kernel/cpuset.c.
*/
for_next_zone_zonelist_nodemask(zone, z, ac->zonelist, ac->high_zoneidx,
ac->nodemask){
if (cpusets_enabled() &&
(alloc_flags & ALLOC_CPUSET) &&
!__cpuset_zone_allowed(zone, gfp_mask))
continue;
}
}
源码路径 : linux-4.12mmpage_alloc.c#3034
相关标志位含义 , 参考 【Linux 内核 内存管理】物理分配页 ③ ( 物理页分配标志位分析 | ALLOC_WMARK_MIN | ALLOC_WMARK_MASK | ALLOC_HARDER ) 博客 ;
ALLOC_CPUSET
宏定义 , 表示 检查 cpuset , 是否允许分配内存页 ;
#define ALLOC_HARDER 0x10 /* try to alloc harder */
#define ALLOC_HIGH 0x20 /* __GFP_HIGH set */
#define ALLOC_CPUSET 0x40 /* check for correct cpuset */
#define ALLOC_CMA 0x80 /* allow allocations from CMA areas */
源码路径 : linux-4.12mminternal.h#483
三、判定内存节点的脏页数量
调用者 假如 设置了 __GFP_WRITE
标志位 , 表明 文件系统 写文件 需要 申请一个页缓存 , 需要检查 " 内存节点 “ 中的 ” 脏页数量 " 是否超出了限制 ,
假如 超出了限制 , 也是 不能从 该 内存区域 分配内存 , continue
中断本次遍历 , 继续执行下一次遍历 ;
static struct page *
get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
const struct alloc_context *ac)
{
/*
* Scan zonelist, looking for a zone with enough free.
* See also __cpuset_node_allowed() comment in kernel/cpuset.c.
*/
for_next_zone_zonelist_nodemask(zone, z, ac->zonelist, ac->high_zoneidx,
ac->nodemask){
/*
* When allocating a page cache page for writing, we
* want to get it from a node that is within its dirty
* limit, such that no single node holds more than its
* proportional share of globally allowed dirty pages.
* The dirty limits take into account the node's
* lowmem reserves and high watermark so that kswapd
* should be able to balance it without having to
* write pages from its LRU list.
*
* XXX: For now, allow allocations to potentially
* exceed the per-node dirty limit in the slowpath
* (spread_dirty_pages unset) before going into reclaim,
* which is important when on a NUMA setup the allowed
* nodes are together not big enough to reach the
* global limit. The proper fix for these situations
* will require awareness of nodes in the
* dirty-throttling and the flusher threads.
*/
if (ac->spread_dirty_pages) {
if (last_pgdat_dirty_limit == zone->zone_pgdat)
continue;
if (!node_dirty_ok(zone->zone_pgdat)) {
last_pgdat_dirty_limit = zone->zone_pgdat;
continue;
}
}
}
}
源码路径 : linux-4.12mminternal.h#3057