0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux内存管理之CPU本地页帧缓存

jf_0tjVfeJz 来源:嵌入式ARM和Linux 2024-02-20 09:23 次阅读

0 CPU页帧缓存概念

在前一节中,我们学习了buddy伙伴关系系统,它适用于申请连续的大块物理内存;而有些时候,经常需要申请和释放单个页帧。但是,如果使用伙伴关系系统,需要查表、进行合并等操作,效率不高。为了提高性能,每个内存ZONE区都提供了一个per-CPU变量,CPU页帧缓存。每个CPU页帧缓存都包含一些预分配好的页帧,满足本地CPU发起的单个页帧请求。

实际上,每个内存ZONE区和每个CPU都有2个缓存:一个是热缓存,它存储页帧,其内容可能包含在CPU的硬件缓存中;另一个是冷缓存。

如果内核或用户进程在分配后立即写入页帧,那么从热缓存中获取页帧将有利于系统性能。实际上,每次访问页帧的某个内存位置,都会导致硬件Cache中替换其它页帧的某一行(Cache-line),当然,除非硬件Cache已经包含刚刚访问的“热”页帧中内存位置的一行。

相反,如果要用DMA操作填充页帧,则从冷缓存中取页帧是很方便的。在这种情况下,不涉及CPU,也不会修改硬件Cache的任何行。从冷缓存中取页帧可以为其他类型的内存分配请求保留热页帧。

CPU页帧缓存的数据结构是per_cpu_pageset类型的数组,其存储在内存ZONE描述符中的pageset成员中,如下面的代码所示:

structzone{
/*...*/
structper_cpu_pagesetpageset[NR_CPUS];
/*...*/
}

数组个数与CPU个数相关,其中的每个数组元素又包含2个per_cpu_pages描述符成员:一个是热缓存;另一个是冷缓存。而per_cpu_pages数据类型的成员如下表所示:

structper_cpu_pages{
intcount;/*缓存中的页帧数量*/
intlow;/*阈值下限,用于缓存补充*/
inthigh;/*阈值上限,需要清空缓存*/
intbatch;/*需从缓存中添加或减少的页帧数*/
structlist_headlist;/*缓存中页帧描述符列表,即内存页列表*/
};

内核使用两个阈值(low和high)监控冷/热缓存的大小:如果页帧数量低于阈值,则内核使用伙伴系统分配一定数量的单个页帧(batch);否则,页帧数量超过阈值上限,内核将缓存中的页帧释放到伙伴系统中(batch)。batch、low和high的值,具体依赖于内存ZONE区的页帧数量。

1 通过CPU页帧缓存分配页帧

buffered_rmqueue()函数在给定的内存ZONE区中分配页帧。它利用CPU页帧缓存来处理单个页帧请求。

Linux v2.6.11内核源码实现如下所示(文件位置:/mm/page_alloc.c):

staticstructpage*
buffered_rmqueue(structzone*zone,intorder,intgfp_flags)
{
unsignedlongflags;
structpage*page=NULL;
intcold=!!(gfp_flags&__GFP_COLD);

if(order==0){
structper_cpu_pages*pcp;

pcp=&zone->pageset[get_cpu()].pcp[cold];
local_irq_save(flags);
if(pcp->count<= pcp->low)
pcp->count+=rmqueue_bulk(zone,0,
pcp->batch,&pcp->list);
if(pcp->count){
page=list_entry(pcp->list.next,structpage,lru);
list_del(&page->lru);
pcp->count--;
}
local_irq_restore(flags);
put_cpu();
}

if(page==NULL){
spin_lock_irqsave(&zone->lock,flags);
page=__rmqueue(zone,order);
spin_unlock_irqrestore(&zone->lock,flags);
}

if(page!=NULL){
BUG_ON(bad_range(zone,page));
mod_page_state_zone(zone,pgalloc,1<< order);
        prep_new_page(page, order);

        if (gfp_flags & __GFP_ZERO)
            prep_zero_page(page, order, gfp_flags);

        if (order && (gfp_flags & __GFP_COMP))
            prep_compound_page(page, order);
    }
    return page;
}

输入参数分别是内存ZONE区的描述符的地址(zone)、内存分配请求大小(2^order)和分配标志gfp_flags。如果在gfp_flags中设置了__GFP_COLD标志,则应从冷缓存中获取页帧,否则应从热缓存中获取页帧(此标志仅对单个页帧请求有意义)。该函数基本上执行以下操作:

如果order不等于0,则页帧缓存不能使用,函数直接跳转到第4步。

检查由__GFP_COLD标志标识的内存ZONE区域的CPU缓存是否必须被补充(per_cpu_pages的count ≤ low)。在本例中,它执行以下子步骤:

重复调用__rmqueue()函数,从伙伴系统中分配batch个页帧。

将分配的页帧描述符插入到缓存的列表中。

更新count变量(将新分配的页帧数量加上)。

如果count > 0,从缓存列表中取一个页帧,然后跳转到第5步。(CPU页帧缓存可能是空的,在第2步的__rmqueue()没有申请到页帧时就会发生)

到这儿,如果内存请求没有被满足,调用__rmqueue()申请从伙伴系统中分配所请求页帧。

如果内存请求被满足,初始化该页帧(第1个)的页描述符:清除某些标志、设置private为0,设置页帧引用计数器为1。另外,如果设置了__GPF_ZERO,将申请的内存清零。

返回页帧(第1个)的描述符,失败返回NULL。

2 通过CPU页帧缓存释放页帧

从CPU页帧缓存中释放页帧,使用free_hot_page()和free_cold_page()函数。它们都是free_hot_cold_page()的封装函数,如下所示(文件位置:/mm/page_alloc.c):

staticvoidfastcallfree_hot_cold_page(structpage*page,intcold)
{
structzone*zone=page_zone(page);
structper_cpu_pages*pcp;
unsignedlongflags;

arch_free_page(page,0);

kernel_map_pages(page,1,0);
inc_page_state(pgfree);
if(PageAnon(page))
page->mapping=NULL;
free_pages_check(__FUNCTION__,page);
pcp=&zone->pageset[get_cpu()].pcp[cold];
local_irq_save(flags);
if(pcp->count>=pcp->high)
pcp->count-=free_pages_bulk(zone,pcp->batch,&pcp->list,0);
list_add(&page->lru,&pcp->list);
pcp->count++;
local_irq_restore(flags);
put_cpu();
}

free_hot_cold_page()接受的参数是待释放页帧的描述符地址page,表示热缓存还是冷缓存的标志cold。

执行的步骤如下:

根据页帧,获取page->flags标志。

根据cold标志获取对应页帧缓存的描述符per_cpu_pages地址。

检查缓存是否不足:如果count ≥ high,调用free_pages_bulk()函数。该函数会重复调用__free_pages_bulk()函数释放指定的页帧到伙伴系统中。

将该页帧添加到缓存列表中,增加count计数。

应该注意的是,在Linux v2.6内核中,没有任何页帧被释放到冷缓存中:内核总是假设释放的页帧相对于硬件缓存来说是热的。当然,这并不意味着冷缓存是空的:当达到低阈值时,缓存由buffered_rmqueue()补充。

3 移除__GFP_COLD

虽然我们前边分析了基于冷热缓存的CPU页帧缓存,但是,从v4.14版本以后的内核中已经移除,参考patch。Patches 1-4是与移除冷缓存最相关的部分;Patches 5-8是可选的,因为它们都是删除无用但也不影响性能的代码。

free_hot_cold_page的大多数调用者用户都声称被释放的页是热缓存的。唯一的例外是页回收代码,因为在不久的将来可能会释放足够多的页,因此CPU的本地页帧缓存列表将被回收,热缓存信息将丢失。由于没有人真正关心被释放到分配器的页的热信息,所以省略该参数即可。




审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 计数器
    +关注

    关注

    32

    文章

    2253

    浏览量

    94351
  • Cache
    +关注

    关注

    0

    文章

    129

    浏览量

    28297
  • LINUX内核
    +关注

    关注

    1

    文章

    316

    浏览量

    21617

原文标题:Linux内核8.5-内存管理之CPU本地页帧缓存

文章出处:【微信号:嵌入式ARM和Linux,微信公众号:嵌入式ARM和Linux】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Linux内存管理是什么,Linux内存管理详解

    Linux内存管理 Linux内存管理是一个非常复杂的过程,主要分成两个大的部分:内核的
    的头像 发表于 05-11 17:54 5976次阅读
    <b class='flag-5'>Linux</b>的<b class='flag-5'>内存</b><b class='flag-5'>管理</b>是什么,<b class='flag-5'>Linux</b>的<b class='flag-5'>内存</b><b class='flag-5'>管理</b>详解

    Linux内存管理页面回收

    请求调机制,只要用户态进程继续执行,他们就能获得框,然而,请求调没有办法强制进程释放不再使用的框。因此,迟早所有空闲内存将被分配给进
    发表于 05-19 14:09 1063次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内存</b><b class='flag-5'>管理</b><b class='flag-5'>之</b>页面回收

    关于Linux内存管理的详细介绍

    Linux内存管理是指对系统内存的分配、释放、映射、管理、交换、压缩等一系列操作的管理。在
    发表于 03-06 09:28 1060次阅读

    Linux内核内存管理ZONE内存分配器

    内核中使用ZONE分配器满足内存分配请求。该分配器必须具有足够的空闲,以便满足各种内存大小请求。
    的头像 发表于 02-21 09:29 863次阅读

    Linux内核内存管理之内核非连续物理内存分配

    我们已经知道,最好将虚拟地址映射到连续,从而更好地利用缓存并实现更低的平均内存访问时间。然而,如果对内存区域的请求并不频繁,那么考虑基于
    的头像 发表于 02-23 09:44 870次阅读
    <b class='flag-5'>Linux</b>内核<b class='flag-5'>内存</b><b class='flag-5'>管理</b>之内核非连续物理<b class='flag-5'>内存</b>分配

    HVM的缓存控制与内存管理

    控制的虚拟指令 下图列出了HVM缓存控制的虚拟指令: 内存管理概述 HVM映射虚拟地址0x0000_0000至0xfeff_ffff置于一个32位的逻辑地址空间,既可以使用一系列的转换或者1或2级别
    发表于 09-20 10:19

    Linux内存管理导读

    Linux 内存管理导读 :1. 存储层次结构和 x86存储管理硬件(MMU) 1.1 存储层次 高速缓存(cache) 主存(main m
    发表于 11-03 22:32 39次下载

    渲染中的缓存和深度缓存

    渲染涉及大量的缓存,这里缓存只是一个简单的存有像素数据的矩形内存块,最重要缓存缓存和深度
    的头像 发表于 05-14 11:44 6304次阅读
    渲染中的<b class='flag-5'>帧</b><b class='flag-5'>缓存</b>和深度<b class='flag-5'>缓存</b>

    Linux性能及调优指南:内存架构

    Linux管理swap空间也非常有效率。swap空间被使用时并不意味着出现内存的瓶颈,它恰恰证明了Linux管理系统资源如何的有效。详见“
    发表于 04-02 14:32 340次阅读

    CPU缓存的作用及原理有哪些

    CPU缓存是位于CPU内存之间的临时存储器,它的容量比内存小很多,但交换速度比内存要快很多。
    的头像 发表于 08-27 15:58 1.1w次阅读

    关于CPU缓存的作用

    CPU没有存储功能,那么缓存到底是干什么的?CPU缓存是用于减少处理器访问内存所需平均时间的部件,作用类似于
    的头像 发表于 03-30 10:58 4452次阅读
    关于<b class='flag-5'>CPU</b><b class='flag-5'>缓存</b>的作用

    如何在 Linux 上查看本地 DNS 缓存

      刷新本地 DNS 缓存可以解决 HTTP 错误并保护您免受 DNS 欺骗。以下是在 Linux 上执行此操作的方法。 当您使用域名访问网站时,您的系统会向 DNS 服务器发送请求以获取该域
    的头像 发表于 06-26 10:52 3300次阅读
    如何在 <b class='flag-5'>Linux</b> 上查看<b class='flag-5'>本地</b> DNS <b class='flag-5'>缓存</b>

    Linux 内存管理总结

    缓存、交换分区等。Linux内存管理的目标是最大限度地利用可用内存,同时保证系统的稳定和可靠性。 1.1 什么是
    的头像 发表于 11-10 14:58 497次阅读
    <b class='flag-5'>Linux</b> <b class='flag-5'>内存</b><b class='flag-5'>管理</b>总结

    什么是CPU缓存?它有哪些作用?

    CPU缓存(Cache Memory)是计算机系统中一个至关重要的组成部分,它位于CPU内存之间,作为两者之间的临时存储器。CPU
    的头像 发表于 08-22 14:54 2355次阅读

    缓存美——如何选择合适的本地缓存

    Guava cache是Google开发的Guava工具包中一套完善的JVM本地缓存框架,底层实现的数据结构类似于ConcurrentHashMap,但是进行了更多的能力拓展,包括缓存过期时间设置、
    的头像 发表于 11-17 14:24 154次阅读
    <b class='flag-5'>缓存</b><b class='flag-5'>之</b>美——如何选择合适的<b class='flag-5'>本地</b><b class='flag-5'>缓存</b>?