首页 > 系统相关 >Linux内核中NUMA内存节点和内存zone

Linux内核中NUMA内存节点和内存zone

时间:2023-11-20 23:16:00浏览次数:38  
标签:node struct zone zones 内存 节点 NUMA

    在现代大型服务器中多个内存节点机器一般都采用NUMA架构,而NUMA架构中不同的内存节点在Linux内核中使用pg_data_t类型(实际是struct pglist_data)来表示表示。

    Linux又为每个内存节点根据内存地址的高低划分了不同的区域类型如ZONE_DMA、ZONE_DMA32、ZONE_NORMAL,一个区域在Linux中使用数据结构struct zone表示。

    一个内存节点中的所有zone组织在pg_data_t的struct zone node_zones[MAX_NR_ZONES]成员中,这个node_zones有MAX_NR_ZONES个struct zone元素,但是这其中并非所有的zone都是有效的,这依赖于架构以及系统的差异;比如,有些架构中没有ZONE_HIGH。

    这样一来,我们就可以了解在NUMA架构的系统中各个节点和zone的组织情况,以4个节点为例如下所示:

  node0: node_zones[DMA]、node_zones[DMA32]、node_zones[ZONE_NORMAL]、node_zones[ZONE_MOVABLE]
  node1:  node_zones[DMA]、node_zones[DMA32]、node_zones[ZONE_NORMAL]、node_zones[ZONE_MOVABLE]
  node2: node_zones[DMA]、node_zones[DMA32]、node_zones[ZONE_NORMAL]、node_zones[ZONE_MOVABLE]
  node3: node_zones[DMA]、node_zones[DMA32]、node_zones[ZONE_NORMAL]、node_zones[ZONE_MOVABLE]

    有了这个组织结构,我们可以顺利的在NUMA系统中分配内存了吗?No!

    思考一下这些问题:当一个进程要分配内存时,系统中多个NUMA内存节点它首选从哪个节点分配内存?如果首选的节点内存不足了,第二选择的内存节点是哪个呢?内存节点选好了,从哪个内存zone分配内存呢?

    首先,Linux会先计算好每个节点与其他节点之间的距离:

Node Fallback list
------------------
0       0  1  2  3
1       1  0  3  2
2       2  3  0  1
3       3  2  1  0

    上面node0距离其他节点由近到远分别为node0,node1, node2, node3。每个节点到其他的节点的距离决定了在当前节点分配内存时的选择顺序,因此node0分配内存的先后顺序就是node0 > node1 > node2 > node3,这种内存的分配策略在Linux中叫ZONELIST_FALLBACK

    当然还有一种特殊情况,有些场景中内存分配时只能够在当前NUMA节点分配,在Linux中使用__GFP_THISNODE标志分配内存时就是这种情况,此时的分配策略是ZONELIST_NOFALLBACK。

    Linux为每个NUMA节点创建了一个struct zonelist node_zonelists[MAX_ZONELISTS]数组来实现上面的ZONELIST_FALLBACK和ZONELIST_NOFALLBACK策略;如果分配内存时有__GFP_THISNODE标志则选用node_zonelists[ZONELIST_NOFALLBACK],否则选用node_zonelists[ZONELIST_FALLBACK]。

    内存分配时优先选择哪个内存节点的问题解决了,那么如何选择具体的zone呢?答案还是在这个node_zonelists[]上。

    首先node_zonelists[]是struct zonelist结构的数组,而struct zonelist结构的定义如下:

struct zonelist {
        struct zoneref _zonerefs[MAX_ZONES_PER_ZONELIST + 1];    /* MAX_ZONES_PER_ZONELIST = (MAX_NUMNODES * MAX_NR_ZONES) */
}; 

    可以看到struct zonelist实际上是一个 MAX_NUMNODES * MAX_NR_ZONES + 1大小的 struct zoneref数组;其实可以看做是两层MAX_NUMNODES,表示一个个的NUMA node;而MAX_NR_ZONES则是每个节点上的zones。而一个NUMA node中的node_zonelists[ZONELIST_NOFALLBACK]就是将系统中所有节点的zones按照距离依次铺开:

node0                    |  node1                   |  node2                    |  node3
MOVABLE,NORNMAL,DMA32,DMA| MOVABLE,NORNMAL,DMA32,DMA|  MOVABLE,NORNMAL,DMA32,DMA|  MOVABLE,NORNMAL,DMA32,DMA

    上面是node0的node_zonelists[]的组织情况,首先从节点维度来看数组元素先后顺序是按离node0距离近远来分布的;其次,从一个node的zone分布情况来看是从高到低的zone分布来的。

    因此当前上下文为node0分配内存时,优先选择距离最近的node0;然后再优先从最高的zone去分配(其实这里描述并不准确,zone的起点需要根据gfp来确定,然后从高到低优先级依次降低)。

    还有一点需要补充,node_zonelist中存放的其实是struct zoneref。而struct zoneref的定义非常简单,代表着pg_data_t.node_zones[]中的一个zone:

struct zoneref {
        struct zone *zone;      /* Pointer to actual zone */
    /* 这里的zone_idx指的是当前这个zoneref所代表的zone在对应的NUMA节点上node_zones[]中的index */
        int zone_idx;           /* zone_idx(zoneref->zone) */
};

    咋一看struct zoneref和struct zone其实就差了一个zone_idx,这个有什么作用呢,为什么要这样设计呢?

    首先,node_zonelist中的zoneref.zone是指向node_zones[]中具体某个zone的指针,这是为了避免对大数据结构的查找引用带来的cache miss。

    其次,zone_idx代表着zoneref.zone在node_zones[]数组中的索引,注意如果node_zones[]中的某个zone是一个”空”zone,即没有管理任何一个page,则node_zonelist[]中的zoneref则不会有对应的元素。也就是说node_zonlist[]中代表一个node的zoneref元素并不是和node_zones[]一一对应的,它是node_zones[]的一个子集;为此需要有zone_idx来表示它在node_zones[]中的位置。 

    最后,用一张图来做总结:

图1 双NUMA节点的ZONELIST_FALLBACK示意图

 

标签:node,struct,zone,zones,内存,节点,NUMA
From: https://www.cnblogs.com/liuhailong0112/p/17844676.html

相关文章

  • Linux - 内存间通信
    进程间通信 Linux下进程通信的方式有管道:管道用于有亲缘关系的进程间通信有名管道:除了管道特性外还能在独立进程间进行通信信号:用于通知进程有某种事件发生消息队列:用于进程间较多数据的通信,有读写权限的进程可以向队列中添加消息。只有读权限则只能读取队列中消......
  • Proj4:改进LiteOS中物理内存分配算法
    记录一下,操作系统课上老师讲的proj4做法给的参考资料LiteOS中的物理内存分配采用了TLSF算法,该算法较好地解决了最坏情况执行时间不确定(notbounded)或者复杂度过高(boundedwithatooimportantbound"),以及碎片化问题(fragmentation)两个问题。TLSF算法仍存在优化空间,Best-......
  • 【无为原创】万字图文详解java的堆内存及OOM的解决方案,看完还不懂,从此绝笔不写了!
      目录如下:什么是JVM的堆是不是所有的Java对象都放在堆上?线程和堆的关系堆的内部结构面试题新生代与老年代如何设置堆的大小?新生代与老年代的比例设置Eden、幸存者的比例常用参数对象分配金句:分配过程内存......
  • 一次Java内存占用高的排查案例,解释了我对内存问题的所有疑问
      问题现象7月25号,我们一服务的内存占用较高,约13G,容器总内存16G,占用约85%,触发了内存报警(阈值85%),而我们是按容器内存60%(9.6G)的比例配置的JVM堆内存。看了下其它服务,同样的堆内存配置,它们内存占用约70%~79%,此服务比其它服务内存占用稍大。那为什么此服务内存占用稍大呢,它......
  • 内存加载.NET程序集&Bypass ETW
    内存加载.NET程序集&BypassETW在内存中加载可以使你的文件变得非常隐蔽,不需要落地文件。下面我们一起来学习一下,如何在内存中加载.net程序集,并且绕过ETW的检测。0x01Assembly.Load官方文档如下Assembly.Load方法(System.Reflection)|MicrosoftLearn,我在这里也简单介绍一......
  • 新生代内存需要有两个Survivor区 S0、S1
     在我的上一篇博客中,介绍了JVM堆内存的结构以及在堆中进行的GC机制,链接是浅谈JAVAGC机制与性能优化那么,在JVM的新生代内存中,为什么除了Eden区,还要设置两个Survivor区?1为什么要有Survivor区先不去想为什么有两个Survivor区,第一个问题是,设置Survivor区的意义在哪里? 如果......
  • win11笔记本换内存后,报错,及解决:0x00007FF8011F6693指令引用了0x0000000000000000内存
    笔记本原装内存为一对镁光8GDDR54800MHz换单条镁光32GDDR55600MHz内存后,重启电脑出现如下报错:0x00007FF8011F6693指令引用了0x0000000000000000内存。该内存不能为read。要终止程序,请单击”确定” 联系内存的卖家客服提供的解决步骤虽然我没看到滚屏,但是重启后问题一样......
  • 进程间通信的方式之消息队列和共享内存
    消息队列消息队列就是保存在内核中的消息链表,包括Posix消息队列和SystemV消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。共享内存共享内存的机制,......
  • 内存安全问题之 use-after-free 漏洞的介绍
    计算机安全领域中的"use-after-free"漏洞是一种常见的内存安全问题。该漏洞类型源自于程序错误,通常发生在应用程序或操作系统中。"Use-after-free"漏洞指的是在释放(free)了某块内存后,程序继续使用了已释放的内存区域,可能导致严重的安全问题。这种漏洞对计算机系统和用户数据构成严......
  • 关于内存芯片的电流消耗机制的介绍
    内存芯片的电能消耗机制是一个复杂而精密的系统,受到多种因素的影响。在理解内存芯片的电能消耗机制之前,我们需要了解内存芯片的基本结构和工作原理。内存芯片的基本结构:内存芯片通常由存储单元组成,每个存储单元用于存储一个数据位。存储单元的排列方式可以是行和列的矩阵结构,其......