首页 > 系统相关 >内存管理中的关键数据结构

内存管理中的关键数据结构

时间:2023-10-08 11:35:09浏览次数:36  
标签:Node node struct ZONE 内存 数据结构 pages 关键

前言

在谈Linux内存管理框架之前需要了解NUMA,NUMA是非一致性内存访问(Uon-Uniform Memory Access)的缩写,与之相反的是一致性内存访问UMA。在多核的UMA架构的机器上,CPU视角下所有的内存都是均匀的,不同CPU访问同一块内存的延迟是相同;而在NUMA架构的机器上内存被划分为不同的区域,对CPU来说内存是不均匀的,存在着远端内存和本地内存的区别,访问远端内存的代价更高。

UMA和NUMA

UMA与NUMA的区别

内核对NUMA的支持

为了支持NUMA内核使用Node这个概念表示一块均匀的内存,这样NUMA系统的多个内存区域就可以用多个Node来表示,这样做有两个优点:

  1. 对于UMA架构可以只用一个Node表示就可以了,架构上完全兼容。
  2. 对于物理内存区域存在空洞的情况(一种不均匀内存的特殊情况,内存在物理上不连续),也可以使用Node的概念进行抽象。

在内核中使用pg_data_t结构描述一个Node,在内核中使用链表串联所有的Node用于遍历访问。每个Node中的内存又被划分为多个Zone,被称为内存域。

内存域(Zone)

在讲Zone之前需要先描述一下虚拟地址空间的划分,在32位系统上可寻址的地址空间为4GB,一般会按照3:1的比例划分为用户地址和内核地址。内核地址空间的前896MB空间被称为直接映射区,对于物理内存地址不超过该范围的页帧可以通过虚拟内存减去内核地址偏移的方式得到物理地址,这种地址映射方式被称为直接映射,对于超过内核地址空间范围的物理内存则需要通过高端内存映射,其映射关系是动态的。

Zone的类型与其物理地址范围有关,从低地址到高地址划分为ZONE_DMAZONE_DMA32ZONE_NORMALZONE_HIGHMEM

  • ZONE_DMA: DMA寻址需要,一些外设设备能够访问的地址空间有限,因此需要预留一部分物理内存地址空间给DMA使用。
  • ZONE_DMA32: 在64位系统上与ZONE_DMA有区别,在32位系统该内存区域为0MB。
  • ZONE_NORMAL: 能够由内核段直接映射的地址空间,但是该段地址空间不一定有实际的物理内存对应。比如64位系统中只有2GB的物理内存,这两GB物理内存都属于了ZONE_DMA32范围。
  • ZONE_HIGHMEM: 物理地址空间范围超过了内核段长度的部分。

这里的Zone指的是物理内存的地址范围,并不一定存在对应的物理内存。

除此之外,内核还提供了一个伪内存区域ZONE_MOVEABLE,是一种减少内存碎片的机制。

每个Zone提供了一个数组来管理该内存区域内的所有页帧,每个页帧通过Page结构体来描述。

为了满足性能需要,内存分配会优先从当前CPU所属的Node中分配内存,但是该操作不一定保证成功,因此每个Node提供了备选列表存放可选的Node以及相关的Zone,列表中越靠后的Node和Zone越不适合进行分配。该备用列表在内核中使用zonelist表示。

Node和Zone的数据结构

Node的数据结构

typedef struct pglist_data {
    struct zone node_zones[MAX_NR_ZONES];        
    struct zonelist node_zonelists[MAX_ZONELISTS]; 
    int nr_zones;
#ifdef CONFIG_FLAT_NODE_MEM_MAP
    struct page *node_mem_map;
#endif
    struct bootmem_data *bdata;

    unsigned long node_start_pfn;
    unsigned long node_present_pages; /* total number of physical pages */
    unsigned long node_spanned_pages; /* total size of physical page
                         range, including holes */
    int node_id;
    wait_queue_head_t kswapd_wait;
    struct task_struct *kswapd;
    int kswapd_max_order;
} pg_data_t;

  • node_zones: 内存区域数组
  • node_zonelists: 备选Node和Zone列表
  • nr_zones: 内存区域个数
  • node_mem_map: Node中的所有页帧
  • bdata: struct bootmem_data是内核启动时使用的自举内存分配器boot memory allocator,bdata指向该实例。
  • node_start_pfn: Node中第一个page的逻辑编号,page的逻辑编号是全局唯一
  • node_present_pages: Node中的page数目。
  • node_spanned_pages: Node以地址长度和page大小计算的page数目(包括空洞),和node_present_pages不一定相同。
  • node_id: 全局的Node ID
  • kswapd_wait: kswapd的等待队列
  • kswapd: 负责该Node的kswapd进程的task_struct
  • kswapd_max_order: 页交换子系统实现相关。

在内核中,Node的状态通过位图来表示,nodemask_t是一个Node的位图,每一位对应一个Node,node_states是一个nodemask_t数组,每一个子项就是处于某状态的Node集合。

typedef struct { DECLARE_BITMAP(bits, MAX_NUMNODES); } nodemask_t;
nodemask_t node_states[NR_NODE_STATES];

在内核中支持的Node状态有以下几种(注意区分node_states,在这里是枚举类型,在上面是变量名):

enum node_states {
    N_POSSIBLE,		/* The node could become online at some point */
    N_ONLINE,		/* The node is online */
    N_NORMAL_MEMORY,	/* The node has regular memory */
#ifdef CONFIG_HIGHMEM
    N_HIGH_MEMORY,		/* The node has regular or high memory */
#else
    N_HIGH_MEMORY = N_NORMAL_MEMORY,
#endif
    N_CPU,		/* The node has one or more cpus */
    NR_NODE_STATES
};
  • N_POSSIBLE: 某个时刻后能够变成ONLINE状态
  • N_ONLINE: 在线状态
  • N_NORMAL_MEMORY: Node包含普通内存域
  • N_HIGH_MEMORY: Node包含高端内存或者普通内存域
  • N_CPU: Node包含一个或者多个cpus

N_POSSIBLEN_ONLINEN_CPU会用于内存、cpu的热插拔。N_NORMAL_MEMORYN_HIGH_MEMORY与内存管理相关。

Zone的数据结构

struct zone {
    /* Fields commonly accessed by the page allocator */
    unsigned long		pages_min, pages_low, pages_high;
    unsigned long		lowmem_reserve[MAX_NR_ZONES];

#ifdef CONFIG_NUMA
    int node;
    unsigned long		min_unmapped_pages;
    unsigned long		min_slab_pages;
    struct per_cpu_pageset	*pageset[NR_CPUS];
#else
    struct per_cpu_pageset	pageset[NR_CPUS];
#endif

    struct free_area	free_area[MAX_ORDER];

    spinlock_t		lock;
    ZONE_PADDING(_pad1_)

    /* Fields commonly accessed by the page reclaim scanner */
    spinlock_t		lru_lock;	
    struct list_head	active_list;
    struct list_head	inactive_list;
    unsigned long		nr_scan_active;
    unsigned long		nr_scan_inactive;
    unsigned long		pages_scanned;	   /* since last reclaim */
    unsigned long		flags;		   /* zone flags, see below */

    /* Zone statistics */
    atomic_long_t		vm_stat[NR_VM_ZONE_STAT_ITEMS];

    
    int prev_priority;


    ZONE_PADDING(_pad2_)
    /* Rarely used or read-mostly fields */

    wait_queue_head_t	* wait_table;
    unsigned long		wait_table_hash_nr_entries;
    unsigned long		wait_table_bits;

    /*
     * Discontig memory support fields.
     */
    struct pglist_data	*zone_pgdat;
    /* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */
    unsigned long		zone_start_pfn;

    unsigned long		spanned_pages;	/* total size, including holes */
    unsigned long		present_pages;	/* amount of memory (excluding holes) */

    /*
     * rarely used fields:
     */
    const char		*name;
} ____cacheline_internodealigned_in_smp;

在zone中使用了两个自旋锁,locklru_lock,这两个锁的使用频率很高,为了降低锁的并发压力,结构体中通过ZONE_PADDING填充直接让两个锁进入不同的cache line,降低对cache line的冲突,____cacheline_internodealigned_in_smp是一个编译器关键字,提示编译器进行最优化的cache line对齐。

  • pages_min, pages_low, pages_high: 内存水线,用于触发不同操作。高于pages_high内存域状态理想。低于pages_low触发swap。低于pages_min时页回收压力较大。
  • lowmem_reserve: 每个内存域的保留内存页,用于完成一些不能失败的分配,每个内存域的保留内存数量不同。
  • pageset: 一个per-cpu数组,struct per_cpu_pageset存储了per-cpu的冷热页信息,热页指page内容仍在cache中,可以快速访问,冷页即内容不在cache中。
  • free_area: 数组,每个free_area存放了固定分配阶的内存块,和伙伴系统相关。
  • active_list, inactive_list: 访问频繁的page看作active。访问不频繁的page为inactive的。在回收时应该优先回收inactive状态的page。active_listinactive_list对应两种状态page的列表。
  • nr_scan_active, nr_scan_inactive: 指定在回收时需要扫描的活跃和非活跃page数
  • pages_scanned: 指定了上次换出一页以来有多少页未成功扫描。
  • flags: 内存域状态。ZONE_ALL_UNRECLAIMABLE表示所有的pages都被钉住不可回收,出现在页面回收时。ZONE_RECLAIM_LOCKED避免SMP系统多个CPU同时回收。ZONE_OOM_LOCKED在处理OOM时设置,避免多CPU进行该操作。
typedef enum {
    ZONE_ALL_UNRECLAIMABLE,		/* all pages pinned */
    ZONE_RECLAIM_LOCKED,		/* prevents concurrent reclaim */
    ZONE_OOM_LOCKED,		/* zone is in OOM killer zonelist */
} zone_flags_t;
  • prev_priority: 存储了上一次扫描该内存区域时的优先级,扫描操作由try_to_free_pages进行,该值与页的换出有关。
  • wait_tablewait_table_hash_nr_entrieswait_table_bits: 等待队列,用于进程等待某个页成为可用,当事件发生时会唤醒进程恢复工作。
  • zone_pgdat: 指向所属的Node实例。
  • zone_start_pfn: 第一个页帧的编号。
  • spanned_pagespresent_pagesname: 不重要,spanned_pagespresent_pages含义与Node中相同。

标签:Node,node,struct,ZONE,内存,数据结构,pages,关键
From: https://www.cnblogs.com/wodemia/p/17748483.html

相关文章

  • 迁移类型与内存碎片
    前言在伙伴系统中长时间的内存分配之后很容易造成内存碎片,即物理内存总量不少但是无法合并为大的连续内存块。而在现代CPU中提供了hugepage的可能,可以分配超大块的page,在TLB中使用更少级的地址转换操作。一个page覆盖了更大的地址范围,大幅度的提高了TLB的命中概率。对于内存密集......
  • SMT贴片加工关键贴片材料有哪些?
    众所周知,不论是PCBA加工或SMT贴片加工,都离不开电子元器件,市面上一般的贴片材料各种各样,但是它们的主要材质都无非是两种:金属基贴片(有机贴片材料)、陶瓷轴包铜贴片(无机贴片材料),这两种材质被广泛应用在PCBA加工行业的原因肯定离不开,材料本身的优点。下面就由贴片加工厂_安徽英特丽小编......
  • 基于 Linux、C++实现的高性能内存池
    1.引入内存池的意义  内存池(MemoryPool)是一种内存分配方式,又被称为固定大小区块规划(fixed-size-blocksallocation)。通常我们习惯直接使用new、malloc等API申请分配内存,但是这种方式非常容易产生内存碎片,早晚都会申请内存失败。并且在比较复杂的代码或者继承的屎山......
  • 数据结构的思维导图(帮助梳理脉络)
    编辑......
  • oracle 开发的列名不可以是level等关键字
    因为开发中将column(name="LEVEL")出错,原因:LEVEL是关键字查某个属性列是不是关键字可以用下列SQLselect*fromv$reserved_wordsvwherev.KEYWORD='LEVEL'其中LEVEL必须用大写   原文出处一、    命名约定 1.是指数据库、数据库对象如TABLE、SEQUENCE、PROCEDURE、COL......
  • 数据结构——C语言知识补充
    学数据结构发现代码看不懂:(参考资料:C语言--指针详解-tongye-博客园C语言结构体详解,C语言struct用法详解指针C语言的一大难点,如何理解指针和运用指针。从一个简单的应用说起:inta=1;这是一个简单的变量声明和初始化,定义了一个整型变量a,并赋值为1。而对于计算机内部,在栈......
  • openGauss学习笔记-92 openGauss 数据库管理-内存优化表MOT管理-内存表特性-使用MOT-M
    openGauss学习笔记-92openGauss数据库管理-内存优化表MOT管理-内存表特性-使用MOT-MOT使用MOTSQL覆盖和限制MOT设计几乎能够覆盖SQL和未来特性集。例如,大多数支持标准的PostgresSQL,也支持常见的数据库特性,如存储过程、自定义函数等。下面介绍各种SQL覆盖和限制。92.1不支持......
  • volatile关键字
    volatile是一个类型修饰符(typespecifier)。它是被设计用来修饰被不同线程访问和修改的变量。如果没有volatile,基本上会导致这样的结果:要么无法编写多线程程序,要么编译器失去大量优化的机会。1.原理作用Volatile意思是“易变的”,应该解释为“直接存取原始内存地址”比较合适。“......
  • 内存碎片化清理
    echo3>/proc/sys/vm/drop_caches具体来说,数字3代表同时清空页缓存(pagecache)、目录项缓存(dentries)和inode缓存(inodes)。这意味着执行echo3>/proc/sys/vm/drop_caches命令后,会清空这三种类型的缓存。这些缓存是用来加速文件系统的访问和提高性能的。通过清空这些缓存,可以释放一......
  • 从GPU的内存访问视角对比NHWC和NCHW
    NHWC和NCHW是卷积神经网络(cnn)中广泛使用的数据格式。它们决定了多维数据,如图像、点云或特征图如何存储在内存中。NHWC(样本数,高度,宽度,通道):这种格式存储数据通道在最后,是TensorFlow的默认格式。NCHW(样本数,通道,高度,宽度):通道位于高度和宽度尺寸之前,经常与PyTorch一起使用。......