前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >快车道-分配页

快车道-分配页

作者头像
DragonKingZhu
发布于 2020-04-24 02:39:07
发布于 2020-04-24 02:39:07
63000
代码可运行
举报
运行总次数:0
代码可运行

内核提供如下函数用于分配页:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
alloc_pages(gfp_mask, order)        //用于分配一个order阶数的页
alloc_page(gfp_mask)               //用于分配一页,最终调用的是alloc_pages(gfp_mask, 0)

那我们就分析alloc_pages的具体实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static inline struct page *alloc_pages_node(int nid, gfp_t gfp_mask,
                        unsigned int order)
{
    if (nid == NUMA_NO_NODE)
        nid = numa_mem_id();
 
    return __alloc_pages_node(nid, gfp_mask, order);
}
  • 确定当前是否是NUMA还是UMA,然后根据node去调用__alloc_pages_node
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static inline struct page *__alloc_pages_node(int nid, gfp_t gfp_mask, unsigned int order)
{
    VM_BUG_ON(nid < 0 || nid >= MAX_NUMNODES);
    VM_WARN_ON((gfp_mask & __GFP_THISNODE) && !node_online(nid));
 
    return __alloc_pages(gfp_mask, order, nid);
}
 
 
static inline struct page *
__alloc_pages(gfp_t gfp_mask, unsigned int order, int preferred_nid)
{
    return __alloc_pages_nodemask(gfp_mask, order, preferred_nid, NULL);
}
  • 确保node是在范围之类,而且此node是online的,因为node是可以热插拔的
  • 确保分配的gfp不是__GFP_THISNODE
  • 最终调用到__alloc_pages_nodemask
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct page * __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, int preferred_nid, nodemask_t *nodemask)
{
    struct page *page;
    unsigned int alloc_flags = ALLOC_WMARK_LOW;   //先尝试从LOW水位分配
    gfp_t alloc_mask; /* The gfp_t that was actually used for allocation */
    struct alloc_context ac = { };
 
    gfp_mask &= gfp_allowed_mask;
    alloc_mask = gfp_mask;
    if (!prepare_alloc_pages(gfp_mask, order, preferred_nid, nodemask, &ac, &alloc_mask, &alloc_flags))
        return NULL;
 
    /* First allocation attempt */
    page = get_page_from_freelist(alloc_mask, order, alloc_flags, &ac);
 
    page = __alloc_pages_slowpath(alloc_mask, order, &ac);
 
    return page;
}
  • 此函数是所谓分配页的heart函数,精简下次函数,主要分为三大步骤
  • 通过prepare_alloc_pages函数初始化alloc_context,确认下分配的zone,zonelist,mask,以及迁移类型
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ac->high_zoneidx = gfp_zone(gfp_mask);
ac->zonelist = node_zonelist(preferred_nid, gfp_mask);
ac->nodemask = nodemask;
ac->migratetype = gfpflags_to_migratetype(gfp_mask);
  • 调用get_page_from_freelist函数从LOW水位进行第一次尝试分配,如果分配成功则返回。
  • 如果分配失败则进入慢车道尝试分配
  • 我们将第一次尝试分配,称为快速快车道分配,本节重点看下快车道的分配
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static struct page *
get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags, const struct alloc_context *ac)
{
    struct zoneref *z;
    struct zone *zone;
    struct pglist_data *last_pgdat_dirty_limit = NULL;
    bool no_fallback;
 
retry:
    /*
     * Scan zonelist, looking for a zone with enough free.
     * See also __cpuset_node_allowed() comment in kernel/cpuset.c.
     */
    no_fallback = alloc_flags & ALLOC_NOFRAGMENT;
    z = ac->preferred_zoneref;
    for_next_zone_zonelist_nodemask(zone, z, ac->zonelist, ac->high_zoneidx,
                                ac->nodemask) {
        struct page *page;
        unsigned long mark;
 
        if (cpusets_enabled() &&
            (alloc_flags & ALLOC_CPUSET) &&
            !__cpuset_zone_allowed(zone, gfp_mask))      //cpuset判断
                continue;
     
        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;
            }
        }
 
        mark = wmark_pages(zone, alloc_flags & ALLOC_WMARK_MASK);  //水位判断
        if (!zone_watermark_fast(zone, order, mark,
                       ac_classzone_idx(ac), alloc_flags)) {
            int ret;
 
            /* Checked here to keep the fast path fast */
            BUILD_BUG_ON(ALLOC_NO_WATERMARKS < NR_WMARK);
            if (alloc_flags & ALLOC_NO_WATERMARKS)               //不检查水位
                goto try_this_zone;
 
            if (node_reclaim_mode == 0 ||
                !zone_allows_reclaim(ac->preferred_zoneref->zone, zone))  //不支持回收功能
                continue;
 
            ret = node_reclaim(zone->zone_pgdat, gfp_mask, order);  //zone回收
            switch (ret) {
            case NODE_RECLAIM_NOSCAN:
                /* did not scan */
                continue;
            case NODE_RECLAIM_FULL:
                /* scanned but unreclaimable */
                continue;
            default:
                /* did we reclaim enough */
                if (zone_watermark_ok(zone, order, mark,
                        ac_classzone_idx(ac), alloc_flags))
                    goto try_this_zone;
 
                continue;
            }
        }
 
try_this_zone:
        page = rmqueue(ac->preferred_zoneref->zone, zone, order,  //分配一页
                gfp_mask, alloc_flags, ac->migratetype);
        if (page) {
            prep_new_page(page, order, gfp_mask, alloc_flags);   //初始化一页
 
            /*
             * If this is a high-order atomic allocation then check
             * if the pageblock should be reserved for the future
             */
            if (unlikely(order && (alloc_flags & ALLOC_HARDER)))
                reserve_highatomic_pageblock(page, zone, order);
 
            return page;
        } else {
        }
    }
 
    return NULL;
}
  • 遍历所有的zonelist,在其中的一个zone中找到空闲的free page
  • 如果使能了cpuset,而且分配标志位页设置了cpuset,但是cpuset不允许从此zone分配,则跳过此zone
  • 如果脏页的数量超过此zone的限制,则跳过此zone
  • 获取LOW水位的门限值mark,如果当前zone的空闲页数小于水位的值,则会做如下的操作,具体见zone_watermark_fast函数
    • 如果设置了不检查水位(ALLOC_NO_WATERMARKS)则尝试从此zone分配页
    • 如果此node没开启回收页功能或者此zone不允许回收页,则跳过
    • 在此node进行回收页,然后判断检查空闲页是否大于水位,大于的话则尝试从此zone分配
  • 当走到try_this_zone进行page分配的话就会调用到我们之前说的buddy分配一页的流程了
  • 如果分配到一页的话,则调用prep_new_page初始化页

总结如下就是快车道的页分配

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
一文搞定伙伴分配器
当系统内核初始化完毕后,使用页分配器管理物理页,当使用的页分配器是伙伴分配器,伙伴分配器的特点是算法简单且高效,支持内存节点和区域,为了预防内存碎片,把物理内存根据可移动性分组,针对分配单页做了性能优化,为了减少处理器的锁竞争,在内存区域增加1个每处理器页集合。
刘盼
2023/01/05
1.2K0
一文搞定伙伴分配器
【Linux 内核 内存管理】物理分配页 ④ ( __alloc_pages_nodemask 函数源码分析 | 快速路径 | 慢速路径 | get_page_from_freelist 源码 )
在 【Linux 内核 内存管理】物理分配页 ② ( __alloc_pages_nodemask 函数参数分析 | __alloc_pages_nodemask 函数分配物理页流程 ) 博客中 , 分析了 __alloc_pages_nodemask 函数分配物理页流程如下 :
韩曙亮
2023/03/30
9930
【Linux 内核 内存管理】物理分配页 ④ ( __alloc_pages_nodemask 函数源码分析 | 快速路径 | 慢速路径 | get_page_from_freelist 源码 )
深入理解 Linux 物理内存分配全链路实现
在上篇文章 《深入理解 Linux 物理内存管理》中,笔者详细的为大家介绍了 Linux 内核如何对物理内存进行管理以及相关的一些内核数据结构。
bin的技术小屋
2023/10/30
8940
深入理解 Linux 物理内存分配全链路实现
Buddy 内存管理机制(下)
这里体现了 Buddy 的核心思想:在内存释放时判断其 buddy 兄弟 page 是不是 order 大小相等的 free page,如果是则合并成更高一阶 order。这样的目的是最大可能的减少内存碎片化。
刘盼
2023/01/05
1.9K0
Buddy 内存管理机制(下)
alloc_page分配内存空间--Linux内存管理(十七)
在内核初始化完成之后, 内存管理的责任就由伙伴系统来承担. 伙伴系统基于一种相对简单然而令人吃惊的强大算法.
233333
2018/12/28
3.2K0
伙伴系统分配内存
内核中常用的分配物理内存页面的接口函数是alloc_pages(),用于分配一个或者多个连续的物理页面,分配页面个数只能是2个整数次幂。相比于多次分配离散的物理页面,分配连续的物理页面有利于提高系统内存的碎片化,内存碎片化是一个很让人头疼的问题。alloc_pages()函数有两个,一个是分配gfp_mask,另一个是分配阶数order。
233333
2020/05/25
1.7K0
Linux分区页框分配器之水位
我们讲页框分配器的时候讲到了快速分配和慢速分配,其中伙伴算法是在快速分配里做的,忘记的小伙伴我们再看下:
刘盼
2020/07/22
1.3K0
Linux-3.14.12内存管理笔记【伙伴管理算法(5)】-核心算法实现
伙伴管理算法内存申请和释放的入口一样,其实并没有很清楚的界限表示这个函数是入口,而那个不是,所以例行从稍微偏上一点的地方作为入口分析。于是选择了alloc_pages()宏定义作为分析切入口:
233333
2020/03/24
1.1K0
【Linux 内核 内存管理】物理分配页 ⑤ ( get_page_from_freelist 快速路径调用函数源码分析 | 遍历备用区域列表 | 启用 cpuset 检查判定 | 判定脏页数量 )
在 【Linux 内核 内存管理】物理分配页 ② ( __alloc_pages_nodemask 函数参数分析 | __alloc_pages_nodemask 函数分配物理页流程 ) 博客中 , 分析了 __alloc_pages_nodemask 函数分配物理页流程如下 :
韩曙亮
2023/03/30
1K0
【Linux 内核 内存管理】物理分配页 ⑥ ( get_page_from_freelist 快速路径调用函数源码分析 | 检查内存区域水线 | 判定节点回收 | 判定回收距离 | 回收分配页 )
在 【Linux 内核 内存管理】物理分配页 ② ( __alloc_pages_nodemask 函数参数分析 | __alloc_pages_nodemask 函数分配物理页流程 ) 博客中 , 分析了 __alloc_pages_nodemask 函数分配物理页流程如下 :
韩曙亮
2023/03/30
9090
【Linux 内核 内存管理】物理分配页 ⑦ ( __alloc_pages_slowpath 慢速路径调用函数源码分析 | 判断页阶数 | 读取 mems_allowed | 分配标志位转换 )
在 【Linux 内核 内存管理】物理分配页 ② ( __alloc_pages_nodemask 函数参数分析 | __alloc_pages_nodemask 函数分配物理页流程 ) 博客中 , 分析了 __alloc_pages_nodemask 函数分配物理页流程如下 :
韩曙亮
2023/03/30
1.3K0
【Linux 内核 内存管理】物理分配页 ⑦ ( __alloc_pages_slowpath 慢速路径调用函数源码分析 | 判断页阶数 | 读取 mems_allowed | 分配标志位转换 )
Linux内存管理之伙伴算法
上文我们讲到快速分配和慢速分配,接下来会详细讲解这两种分配情况,我们先来看下快速分配:
刘盼
2020/07/14
2.3K0
伙伴系统之伙伴系统概述--Linux内存管理(十五)
在内核初始化完成之后, 内存管理的责任就由伙伴系统来承担. 伙伴系统基于一种相对简单然而令人吃惊的强大算法.
233333
2018/12/27
3.1K0
【Linux 内核 内存管理】物理分配页 ⑨ ( __alloc_pages_slowpath 慢速路径调用函数源码分析 | retry 标号代码分析 )
在 【Linux 内核 内存管理】物理分配页 ② ( __alloc_pages_nodemask 函数参数分析 | __alloc_pages_nodemask 函数分配物理页流程 ) 博客中 , 分析了 __alloc_pages_nodemask 函数分配物理页流程如下 :
韩曙亮
2023/03/30
7810
【Linux 内核 内存管理】物理分配页 ⑧ ( __alloc_pages_slowpath 慢速路径调用函数源码分析 | 获取首选内存区域 | 异步回收内存页 | 最低水线也分配 | 直接分配 )
在 【Linux 内核 内存管理】物理分配页 ② ( __alloc_pages_nodemask 函数参数分析 | __alloc_pages_nodemask 函数分配物理页流程 ) 博客中 , 分析了 __alloc_pages_nodemask 函数分配物理页流程如下 :
韩曙亮
2023/03/30
1.5K0
【Linux 内核 内存管理】物理分配页 ① ( 分区伙伴分配器物理分配页核心函数 __alloc_pages_nodemask | __alloc_pages_nodemask 函数完整源码 )
Linux 内核中 , " 分区伙伴分配器 " 有多种 物理页分配函数 , 所有的 函数 都会调用 __alloc_pages_nodemask 函数 , 该函数是 物理页分配 的 核心函数 ;
韩曙亮
2023/03/30
3970
【Linux 内核 内存管理】物理分配页 ① ( 分区伙伴分配器物理分配页核心函数 __alloc_pages_nodemask | __alloc_pages_nodemask 函数完整源码 )
Linux分区页框分配器
页框分配在内核里的机制我们叫做分区页框分配器(zoned page frame allocator),在linux系统中,分区页框分配器管理着所有物理内存,无论你是内核还是进程,都需要请求分区页框分配器,这时才会分配给你应该获得的物理内存页框。当你所拥有的页框不再使用时,你必须释放这些页框,让这些页框回到管理区页框分配器当中。
刘盼
2020/07/14
1.5K0
深度剖析 Linux 伙伴系统的设计与实现
在上篇文章 《深入理解 Linux 物理内存分配全链路实现》 中,笔者为大家详细介绍了 Linux 内存分配在内核中的整个链路实现:
bin的技术小屋
2023/10/30
7880
深度剖析 Linux 伙伴系统的设计与实现
万字整理,肝翻Linux内存管理所有知识点
Linux的内存管理可谓是学好Linux的必经之路,也是Linux的关键知识点,有人说打通了内存管理的知识,也就打通了Linux的任督二脉,这一点不夸张。有人问网上有很多Linux内存管理的内容,为什么还要看你这一篇,这正是我写此文的原因,网上碎片化的相关知识点大都是东拼西凑,先不说正确性与否,就连基本的逻辑都没有搞清楚,我可以负责任的说Linux内存管理只需要看此文一篇就可以让你入Linux内核的大门,省去你东找西找的时间,让你形成内存管理知识的闭环。
用户6280468
2022/03/21
1.8K0
万字整理,肝翻Linux内存管理所有知识点
直接内存回收中的等待队列
在直接内存回收过程中,有可能会造成当前需要分配内存的进程被加入一个等待队列,当整个node的空闲页数量满足要求时,由kswapd唤醒它重新获取内存。这个等待队列头就是node结点描述符pgdat中的pfmemalloc_wait。如果当前进程加入到了pgdat->pfmemalloc_wait这个等待队列中,那么进程就不会进行直接内存回收,而是由kswapd唤醒后直接进行内存分配。
233333
2020/04/13
1.6K0
推荐阅读
一文搞定伙伴分配器
1.2K0
【Linux 内核 内存管理】物理分配页 ④ ( __alloc_pages_nodemask 函数源码分析 | 快速路径 | 慢速路径 | get_page_from_freelist 源码 )
9930
深入理解 Linux 物理内存分配全链路实现
8940
Buddy 内存管理机制(下)
1.9K0
alloc_page分配内存空间--Linux内存管理(十七)
3.2K0
伙伴系统分配内存
1.7K0
Linux分区页框分配器之水位
1.3K0
Linux-3.14.12内存管理笔记【伙伴管理算法(5)】-核心算法实现
1.1K0
【Linux 内核 内存管理】物理分配页 ⑤ ( get_page_from_freelist 快速路径调用函数源码分析 | 遍历备用区域列表 | 启用 cpuset 检查判定 | 判定脏页数量 )
1K0
【Linux 内核 内存管理】物理分配页 ⑥ ( get_page_from_freelist 快速路径调用函数源码分析 | 检查内存区域水线 | 判定节点回收 | 判定回收距离 | 回收分配页 )
9090
【Linux 内核 内存管理】物理分配页 ⑦ ( __alloc_pages_slowpath 慢速路径调用函数源码分析 | 判断页阶数 | 读取 mems_allowed | 分配标志位转换 )
1.3K0
Linux内存管理之伙伴算法
2.3K0
伙伴系统之伙伴系统概述--Linux内存管理(十五)
3.1K0
【Linux 内核 内存管理】物理分配页 ⑨ ( __alloc_pages_slowpath 慢速路径调用函数源码分析 | retry 标号代码分析 )
7810
【Linux 内核 内存管理】物理分配页 ⑧ ( __alloc_pages_slowpath 慢速路径调用函数源码分析 | 获取首选内存区域 | 异步回收内存页 | 最低水线也分配 | 直接分配 )
1.5K0
【Linux 内核 内存管理】物理分配页 ① ( 分区伙伴分配器物理分配页核心函数 __alloc_pages_nodemask | __alloc_pages_nodemask 函数完整源码 )
3970
Linux分区页框分配器
1.5K0
深度剖析 Linux 伙伴系统的设计与实现
7880
万字整理,肝翻Linux内存管理所有知识点
1.8K0
直接内存回收中的等待队列
1.6K0
相关推荐
一文搞定伙伴分配器
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文