首页 > 其他分享 >冷热页机制

冷热页机制

时间:2023-10-07 19:34:31浏览次数:25  
标签:struct zone 冷热 list page pages 机制 pcp

前言

在进行内存访问时的大概流程如下:

  1. 由CPU发出访存指令
  2. 地址转化,MMU根据页表转换或者通过TLB得到物理地址
  3. 访问cache
  4. 如果cache miss,访问物理内存读入cache

因此,访问一个内容在cache中的物理内存能大幅度提高访问速度,基于这个原理,在Linux中将内容仍在cache中page称为hot page,采取了一种叫做冷热页的优化机制。

percpu_pageset_t

由于每个CPU都有自己的独占cache(L1和L2),而在Linux中内存分配最终会落在内存域Zone上,因此在zone中有一个per-cpu的struct per_cpu_pageset,在per-zone下的per-cpu的struct per_cpu_pageset就负责管理在CPU在该zone上进行分配时的冷热页。

struct zone {
    struct per_cpu_pageset pageset[NR_CPUS];
}

相关数据结构如下,per_cpu_pageset中有一个per_cpu_pages成员,冷热页的实现就在per_cpu_pages中。冷热页通过一个链表表示,热页放在链表首部,冷页放在链表尾部。其中成员变量的含义如下:

  • count: 冷热链表中的page个数。
  • high: 当count超过high时说明缓存的冷热页过多,此时会批量返回页帧给伙伴系统。
  • batch: 批量移除或添加page的个数
  • list: 冷热页帧链表
struct per_cpu_pages {
    int count;		/* number of pages in the list */
    int high;		/* high watermark, emptying needed */
    int batch;		/* chunk size for buddy add/remove */
    struct list_head list;	/* the list of pages */
};


struct per_cpu_pageset {
    struct per_cpu_pages pcp;
#ifdef CONFIG_NUMA
    s8 expire;
#endif
#ifdef CONFIG_SMP
    s8 stat_threshold;
    s8 vm_stat_diff[NR_VM_ZONE_STAT_ITEMS];
#endif
} ____cacheline_aligned_in_smp;

单页的分配

当某个zone符合分配条件时,buffered_rmqueue就会尝试从该zone中分配内存。

首先buffered_rmqueue会检查gfp_flags是否标记了__GFP_COLD!!(gfp_flags & __GFP_COLD)中两个!!的作用是将cold的值限定为0或者1。根据gfp_flags确定迁移类型migratetype,迁移类型与内存碎片管理相关。

冷热页主要优化的是单个页面的分配和释放,也就是当分配阶为0的情况。首先找到当前zone上的当前cpu的冷热链表pcp,检查pcp是否有缓存的page,如果没有则调用rmqueue_bulk从当前zone上分配pcp->batch个pages加入到冷热链表中。如果存在pages并且cold为1说明优先分配冷页,从后往前遍历,否则从前往后遍历,找到第一个满足迁移类型的page返回,可以看到冷热的概念只表示被返还的顺序,后进入的链表的页帧相对热一些,分配并不能真的保证分配的“hot page”在cache中,只是可能性更大。

&page->lru == &pcp->list这个判断表示在冷热链表中未找到满足迁移类型的page。此时会调用rmqueue_bulk从对应迁移类型的自由链表中分配一批的pages到冷热链表中后再尝试分配。

static struct page *buffered_rmqueue(struct zone *preferred_zone,
            struct zone *zone, int order, gfp_t gfp_flags)
{
    unsigned long flags;
    struct page *page;
    int cold = !!(gfp_flags & __GFP_COLD);
    int cpu;
    int migratetype = allocflags_to_migratetype(gfp_flags);

again:
    cpu  = get_cpu();
    if (likely(order == 0)) {
        struct per_cpu_pages *pcp;

        pcp = &zone_pcp(zone, cpu)->pcp;
        local_irq_save(flags);
        // 如果冷热链表为空 从zone中分配负责迁移类型的pages加入冷热链表
        if (!pcp->count) {
            pcp->count = rmqueue_bulk(zone, 0,
                    pcp->batch, &pcp->list, migratetype);
            if (unlikely(!pcp->count))
                goto failed;
        }

        // 如果是要求分配冷页则从后往前查找,否则从前往后查找,找到第一个满足迁移类型的page
        /* Find a page of the appropriate migrate type */
        if (cold) {
            list_for_each_entry_reverse(page, &pcp->list, lru)
                if (page_private(page) == migratetype)
                    break;
        } else {
            list_for_each_entry(page, &pcp->list, lru)
                if (page_private(page) == migratetype)
                    break;
        }

        // 如果没有合适的page 此时回到了开头 尝试再次分配符合迁移类型的pages加入列表后从链表中分配page
        /* Allocate more to the pcp list if necessary */
        if (unlikely(&page->lru == &pcp->list)) {
            pcp->count += rmqueue_bulk(zone, 0,
                    pcp->batch, &pcp->list, migratetype);
            page = list_entry(pcp->list.next, struct page, lru);
        }

        list_del(&page->lru);
        pcp->count--;
    } 
...
}

单页的释放

在内核中释放page的基础API是__free_pages__free_pages会检查释放的页的分配阶是否为0,如果为0则调用free_hot_pagefree_hot_page会调用free_hot_cold_page携带参数表示释放的是hot-page。

void __free_pages(struct page *page, unsigned int order)
{
    if (put_page_testzero(page)) {
        if (order == 0)
            free_hot_page(page);
        else
            __free_pages_ok(page, order);
    }
}

void free_hot_page(struct page *page)
{
    free_hot_cold_page(page, 0);
}
    
void free_cold_page(struct page *page)
{
    free_hot_cold_page(page, 1);
}

free_hot_cold_page中和冷热链表相关的代码如下,cold参数决定了是将page链入链表的首部还是尾部,然后设置page的private字段为page的迁移类型。

此时如果冷热链表的页总数count超过了high,调用free_pages_bulk返回batch个pages给伙伴系统。这种批量释放的方式被称为惰性合并,能够避免大量不必要的合并操作(合并了又被分配的情况)。

static void free_hot_cold_page(struct page *page, int cold)
{
    struct zone *zone = page_zone(page);
    struct per_cpu_pages *pcp;
    unsigned long flags;

    ...

    pcp = &zone_pcp(zone, get_cpu())->pcp;
    if (cold)
        list_add_tail(&page->lru, &pcp->list);
    else
        list_add(&page->lru, &pcp->list);
    set_page_private(page, get_pageblock_migratetype(page));
    pcp->count++;
    if (pcp->count >= pcp->high) {
        free_pages_bulk(zone, pcp->batch, &pcp->list, 0);
        pcp->count -= pcp->batch;
    }
    ...
}

总结

冷热页机制有以下优点:

  • free的page加入热页后,一段时间内如果再次访问大概率只需要重新建立映射,而不需要将内容读取到cache中,访问速度更快。
  • 通过从伙伴系统批量分配和释放pages,减少了使用伙伴系统的次数,因此降低了伙伴系统的分裂合并的次数,同时分配速度更快。
  • 采取per-cpu的设计,区分了不同CPU的cache,同时避免了CPU同时分配单页内存时的竞争。

标签:struct,zone,冷热,list,page,pages,机制,pcp
From: https://www.cnblogs.com/wodemia/p/17747271.html

相关文章

  • 【Cpp】RTTI 机制原理解析
    ReferencesBaiduWikiC++中的RTTI机制详解RTTI推荐阅读:RTTI原理推荐阅读:C++中的RTTI机制什么是RTTI机制?RTTI是“RuntimeTypeInformation”的缩写,意思是:运行时类型信息。它提供了运行时确定对象类型的方法。RTTI通过运行时类型信息程序能够使用基类的指针或引用......
  • 在安全数字包裹机制下,汽车制造业如何安全可控地实现上下游协作?
    随着互联网的发展,现在越来越多的企业通过传递电子文件的形式实现网上办公,提高便捷性的同时,也带了文件泄露的风险。尤其是一些机密文档,万一不小心外泄出去,对企业的造成的影响将是不可估量的。2023年1月,小米官方发布“小米汽车保险杠设计图外泄”事件的处理结果,小米二级供应商北京......
  • 小程序底层技术机制解读 - 小程序的性能优化
    小程序的性能优化是确保应用程序流畅运行、快速加载和高效使用资源的关键。了解小程序的性能优化技术和机制可以帮助开发者提高小程序的性能,提供更好的用户体验。本文将深入解读小程序的性能优化的底层技术机制,包括数据缓存、异步编程、页面渲染优化等,并提供一个简单的代码演示,以帮......
  • 网络规划设计师真题解析--TCP慢启动拥塞避免机制
    TCP使用慢启动拥塞避免机制进行拥塞控制。当拥塞窗口大小为16时,发送节点出现超时未收到确认现象时,将采取的措施是(26)。再经过5轮后的拥塞窗口大小为(27)。26、A.将慢启动阈值设为16,将拥塞窗口设为8,并进入拥塞避免阶段B.将慢启动阈值设为16,将拥塞窗口设为1,并进入慢开始阶段C.将慢启动......
  • 网络规划设计师真题解析--TCP慢启动拥塞避免机制
    TCP使用慢启动拥塞避免机制进行拥塞控制。当拥塞窗口大小为16时,发送节点出现超时未收到确认现象时,将采取的措施是(26)。再经过5轮后的拥塞窗口大小为(27)。26、A.将慢启动阈值设为16,将拥塞窗口设为8,并进入拥塞避免阶段B.将慢启动阈值设为16,将拥塞窗口设为1,并进入慢开始阶段C.将慢启动阈......
  • sv的LSB 使用+SV的protect类型+RAL模型的lock原因+C语言结构体中的冒号用法+uvm版本在
    sv的LSB使用https://blog.csdn.net/gsjthxy/article/details/90722378等价关系[LSB+:STEP]=[LSB+STEP:LSB]伪代码:bit[1023:0]mem;bit[7:0]data;j=0..100mem[j*8+:8]=data;//[7:0],[15:8],[23:16]SV的protect类型https://blog.csdn.net/qq_37573794/ar......
  • 嵌入式裸机设计思想——时间片轮裸机开发架构+状态机+定时器调度机制
    前言(1)(2)在MCU开发的时候,很多入门者会固执的认为,做项目一定要上实时操作系统。但是真的是这样的吗?(3)我曾经阅读过一位10年嵌入式开发经验的大佬分享的公众号,这位大佬感叹到,其实对于绝大多数时候,MCU开发不需要上操作系统。只要任务分配的合理,百分之九十的项目不上操作系统都是能够跑......
  • 小程序底层技术机制解读 - 小程序的社交能力
    小程序的社交能力是其成功的关键之一,它允许用户在应用内与其他用户互动、分享内容和建立社交关系。了解小程序的社交能力技术和机制对于开发者来说非常重要,因为它可以帮助他们更好地利用社交功能来增加用户粘性和扩大用户群体。本文将深入解读小程序的社交能力的底层技术机制,包括用......
  • Docker搭建Mysql主从机制
    Mysql主从复制1基础准备由于家境贫寒没有那么多的云资源供我操作,只能使用docker进行模拟了。拉取镜像简单得很就先不谈了。直接开整。以下操作基于mysql:5.7进行一主二从配置。2主库配置运行容器dockerrun-p3306:3306--namemysql-slaver-2-eMYSQL_ROOT_PASSWOR......
  • 手动开发-实现SpringMVC底层机制--小试牛刀
    在这里说的底层机制的实现主要是指:前端控制器、Controller、Service注入容器、对象自动装配、控制器方法获取参数、视图解析、返回json数据。前端控制器前端控制器就是核心控制器。在这里我们可以设计一个Servlet来充当核心控制器:LingDispatcherServlet.java.这个控制器的作用......