首页 > 系统相关 >【Linux内核架构】【(二)内存管理】(N)UMA模型中的内存组织(下)

【Linux内核架构】【(二)内存管理】(N)UMA模型中的内存组织(下)

时间:2024-12-20 20:01:37浏览次数:7  
标签:zone min page UMA 内存 Linux pages 内核

2.3.2 结点管理

2.3.2.4 内存域水印(WaterMark)

内存域水印(WaterMark)是一种用于内存管理的机制,它帮助内核监控和调节物理内存的使用情况,以确保系统的稳定性和性能。

内存域水印是指每个内存区域(zone)中设定的三个关键水位线,分别是最低水线(WMARK_MIN)、低水线(WMARK_LOW)和高水线(WMARK_HIGH)。这些水位线以页(page)为单位,用于衡量内存区域的空闲页数,从而指导内存分配和回收策略。

2.3.2.4.1 内存域水印的作用
  1. 监控内存使用情况:通过比较内存区域的空闲页数与设定的水位线,内核可以判断内存的使用状态,是充足、轻微不足还是严重不足。

  2. 指导内存分配:当内存分配器(如buddy allocator)发现当前内存区域的空闲页数小于低水线,但大于最低水线时,会触发异步的内存回收操作(由kswapd守护进程执行),以释放足够的内存来满足分配请求。

    如果空闲页数低于最低水线,则可能需要进行同步的内存回收(direct reclaim),这可能会阻塞内存分配请求直到回收足够的内存。

  3. 保障系统稳定性:通过设定最低水线,内核可以确保即使在内存压力较大的情况下,也能为关键任务保留足够的内存,从而避免系统崩溃或不稳定。

2.3.2.4.2 水印类型
  1. 最低水线(WMARK_MIN):是内存区域必须保留的最低空闲页数,是为关键性分配保留的内存空间,以确保系统的基本稳定运行。其值通常通过系统参数(全局变量)min_free_kbytes计算得出,该参数指定了系统保留的空闲内存的最低限(以KB为单位),位于/proc/sys/vm/min_free_kbytes,用户层可以读取和修改。
  2. 低水线(WMARK_LOW):当内存区域的空闲页数低于此水位线时,表明内存开始面临压力,此时会唤醒kswapd守护进程进行内存回收。低水线的值通常设置为最低水线的一定比例(如增加1/4)加上额外的预留空间。
  3. 高水线(WMARK_HIGH):当内存区域的空闲页数高于此水位线时,表明内存充足,不需要进行内存回收。高水线的值通常设置为最低水线的一定比例(如增加1/2)加上额外的预留空间。
2.3.2.4.3 计算水印

在计算各种水印之前,内核首先确定最低水线。下图为各水线(纵轴)与主内存大小(横轴)之间的关系,普通内存域,不涉及高端内存域。例如,pages_min为按页计算的min_free_kbytes。

在这里插入图片描述

  • init_per_zone_pages_min():初始化struct zone中的pages_min、pages_low、pages_high水印值,该函数在内核启动期间调用(不止,每次通过proc文件系统修改某个控制参数时也会调用),无需显式调用。

在这里插入图片描述

  • setup_per_zone_pages_min():设置struct zone的pages_min、pages_low、pages_high成员。在计算出高端内存域之外页面的总数(lowmem_pages)之后,内核遍历系统中的所有内存域并执行下列计算。
mm/page_alloc.c 
void setup_per_zone_pages_min(void) 
{ 
    unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT -10); 
    unsigned long lowmem_pages = 0; 
    struct zone *zone; 
    unsigned long flags; 
... 
    for_each_zone(zone) { //遍历所有zone
        u64 tmp; 
        tmp = (u64)pages_min * zone->present_pages; 
        do_div(tmp,lowmem_pages); 
        if (is_highmem(zone)) { 
            int min_pages; 
            min_pages = zone->present_pages / 1024; 
            if (min_pages < SWAP_CLUSTER_MAX) 
                 min_pages = SWAP_CLUSTER_MAX; 
            if (min_pages > 128) 
                 min_pages = 128; 
            zone->pages_min = min_pages; 
        } else { 
            zone->pages_min = tmp; 
        }
        zone->pages_low = zone->pages_min + (tmp >> 2); 
        zone->pages_high = zone->pages_min + (tmp >> 1); 
	} 
}

SWAP_CLUSTER_MAX为高端内存域的下界(页面回收)。页面回收子系统经常对页进行分组式批处理操作,而SWAP_CLUSTER_MAX定义了分组的大小。

  • setup_per_zone_lowmem_reserve():计算lowmem_reserve。

    lowmem_reserve是Linux内核中保留的一部分物理内存,这部分内存不被系统常规使用,而是作为备用资源,在系统内存压力极大时分配给关键任务。

    内核在分配内存页时会检查各个内存区域(zone)中的可用内存量。如果某个zone的可用内存<预设的水线(watermark)+该zone的lowmem_reserve值,内核则认为其内存不足,会跳过这个zone。

    lowmem_reserve的配置可以通过内核参数或系统启动后的proc接口文件进行调整,例如,可以通过内核参数/proc/sys/vm/lowmem_reserve_ratio调整各个zone的lowmem_reserve值。

    lowmem_reserve_ratio是一个数组,每个元素对应一个内存区域(zone)的预留内存比例。内核会根据这个比例计算出每个zone需要预留的内存量,并在内存分配时考虑这些预留内存。这样,即使在高端内存紧张的情况下,低端内存也能得到一定的保护,不会被过度使用。

    内核遍历系统的所有结点,对每个结点的各个内存域分别计算预留内存最小值,具体是将内存域中页帧的总数除以sysctl_lowmem_reserve_ratio[zone]。除数的默认设置对低端内存域是256,对高端内存域是32。

2.3.2.5 冷热页管理pageset[NR_CPUS](cpu高速缓存)

struct zone的pageset[NR_CPUS]成员用于实现冷热分配器(hot-n-cold allocator),NR_CPUS是内核能够支持CPU数量的最大值。

热页:页已经加载到CPU高速缓存,与在内存中的页相比,其数据能够更快地访问。

冷页:页不在CPU高速缓存中。

在多处理器系统上每个CPU都有一个或多个高速缓存,各个CPU的管理必须是独立的。

NR_CPUS是可以在编译时间配置的宏常数。在单处理器系统上是1,针对SMP系统编译的内核中,在2和32(在64位系统上是64)之间,该值并不是系统中实际存在的CPU数目,而是内核支持的CPU的最大数目。

pageset[NR_CPUS]数组元素的类型为per_cpu_pageset。

<mmzone.h> 
struct per_cpu_pageset { 
	struct per_cpu_pages pcp[2]; //索引0为热页,索引1为冷页
} ____cacheline_aligned_in_smp;

更新的版本已将管理冷页和热页的两个列表合并为一个列表。热页放置在列表头部,而冷页置于列表尾部。

<mmzone.h> 
struct per_cpu_pages { 
    int count; //列表中的页数
    int high; //页数上限水印,在需要的情况下清空列表
    int batch; //添加/删除多页块的时候,块的大小(页数)
    struct list_head list; //页的链表
};
  • count:与该列表相关的页的数目。
  • high:水印。如果count的值超出了high,则表明列表中的页太多了。对容量过低的状态没有显式使用水印。如果列表中没有成员,则重新填充列表。
  • batch:CPU的高速缓存不是用单个页为单位来填充的,而是用多个页组成的块为单位。batch是每次添加页数的一个参考值,也就是每块的页数。
  • list:是一个双链表,保存了当前CPU的冷页或热页。

下图展示了在双处理器系统上per-CPU缓存的数据结构是怎么填充的。
在这里插入图片描述
high的计算以及per_cpu_pageset的初始化后续讨论。

2.3.2.6 页帧page

页帧是系统内存的最小单位,对内存中的每个页都会创建struct page的一个实例,所以struct page结构要尽可能小,会有大量的页存在。例如,IA-32系统的标准页长度为4KB,当主内存大小为384MB时就会有100000页。

所以对page结构的小改动,也可能导致保存所有page实例所需的物理内存大幅增加。

2.3.2.6.1 page中特殊的union定义

内存管理的许多部分都使用页,有着广泛用途,所以内核的一个部分可能完全依赖于struct page提供的特定信息,而该信息对内核的另一部分可能完全无用。所以保持page的大小很难。

C语言的联合union类型很适合这种问题。

例如,一个物理内存页能够通过多个地方的不同页表映射到虚拟地址空间,内核想要跟踪有多少地方映射了该页。所以,page中有一个计数器用于计算映射的数目。如果某页用于slub分配器(slub将整页细分为更小部分),那么可以确保只有内核会使用该页,而其他地方不使用,所以映射计数信息就是多余的。这样内核可以重新解释该字段,用来表示该页被细分为多少个小的内存对象使用。

<mm_types.h> 
struct page { 
... 
    union { //双重解释
        atomic_t _mapcount; //内存管理子系统中映射的页表项计数,用于表示页是否已经映射,还用于限制逆向映射搜索
        unsigned int inuse; //用于slub分配器,为对象的数目
    }; 
... 
}

atomic_t允许以原子方式修改其值,是32bit,不受并发访问的影响。

unsigned int是普通整数。在Linux支持的每种体系结构上普通整数也是32bit。

假如将整个union换为atomic_t counter就不合理,因为slub代码在访问对象计数器时不需要原子性,应该反映在数据类型中。这样的定义会影响两个子系统中代码的可读性。_mapcount和inuse描述对应成员就很清晰,counter的含义则过于广泛。

2.3.2.6.2 struct page

与体系结构无关。

<mm.h> 
struct page { 
    unsigned long flags; //原子标志,有些情况下会异步更新
    atomic_t _count; //使用计数
    union {
        atomic_t _mapcount; //内存管理子系统中映射的页表项计数,用于表示页是否已经映射,还用于限制逆向映射搜索
        unsigned int inuse; //用于slub分配器,对象的数目
	}; 
	union { 
		struct { 
            unsigned long private; /* 由映射私有,不透明数据:
            * 如果设置了PagePrivate,通常用于buffer_heads;
            * 如果设置了PageSwapCache,则用于swp_entry_t;
            * 如果设置了PG_buddy,则用于表示伙伴系统中的阶。
            */ 
            struct address_space *mapping; /* 如果最低位为0,则指向inode 
            * address_space,或为NULL。
            * 如果页映射为匿名内存,最低位置位0,
            * 而且该指针指向anon_vma对象:
            * 参见下文的PAGE_MAPPING_ANON。
             */ 
        }; 
... 
        struct kmem_cache *slab; //用于SLUB分配器,指向slab的指针
        struct page *first_page; //用于复合页的尾页,指向首页
    }; 
    union { 
        pgoff_t index; //在映射内的偏移量
        void *freelist; //SLUB: freelist req. slab lock
    }; 
	struct list_head lru; //换出页列表,例如由zone->lru_lock保护的active_list

#if defined(WANT_PAGE_VIRTUAL) 
	void *virtual; //内核虚拟地址(如果没有映射则为NULL,即高端内存)
#endif /* WANT_PAGE_VIRTUAL */ 
};
  • flags:存储了体系结构无关的标志,用于描述页的属性。

  • _count:使用的计数,表示内核中引用该页的次数。==0时,说明page实例当前不使用,可以删除。如果>0,该实例则不会从内存删除。

  • _mapcount:在页表中有多少项指向该页。

  • inuse:用于slub分配器,对象的数目。

  • private:指向私有数据的指针,这样虚拟内存管理会忽略此数据。根据页的用途,用不同的方式使用该指针。多数情况下它用于将页与数据缓冲区关联起来,后续讨论。

  • mapping:指定了页帧所在的地址空间。pgoff_t类型的index是页帧在映射的地址空间内部的偏移量。由内存区的地址空间映射到物理内存(文件的内容所在)。

    mapping不仅能够保存一个address_space指针,还能包含一些额外的信息,用于判断页是否属于未关联到地址空间的某个匿名内存区。

    如果将mapping置为1,则说明该指针并不指向address_space的实例,而是指向anon_vma结构体,该结构对实现匿名页的逆向映射很重要(进程虚拟内存的用户空间缺页异常的校正)。对该指针也可以双重使用,因为address_space实例总是对齐到sizeof(long)。所以在Linux支持的所有计算机上,指向该实例的指针最低位总是0。

总结下来:

  1. mapping为0,表示该页面属于交换缓存(swapcache)。
  2. mapping不为0,但最低位为0,表示该页为匿名页,此时mapping指向一个anon_vma结构体。
  3. mapping不为0,且最低位不为0,表示该页与文件映射相关,此时mapping指向一个address_space结构体。
  • slab:指向slab的指针。
  • first_page:用于复合页的尾页,指向首页。
  • freelist:
  • lru:是一个表头,用于在各种链表上维护该页,以便将页按不同类别分组,最重要的类别是活动和不活动页(页面回收和页交换)。
  • virtual:用于高端内存区域中的页,即无法直接映射到内核内存中的页。virtual用于存储该页的虚拟地址。按照预处理器语句#if defined(WANT_PAGE_VIRTUAL),只有定义了对应的宏,virtual才能成为struct page的一部分,仅部分体系结构是这样。其他体系结构都用别的方式来寻址虚拟内存页,主要是用来查找所有高端内存页帧的散列表(内核映射)。
2.3.2.6.3 page的flags及其宏(体系结构无关)

页的不同属性通过一系列页标志描述,是struct page的flags成员中的各个比特位。在page-flags.h中的宏定义,还有一些宏,用于标志的设置、删除、查询。有一种命名方案:

例如,PG_locked常数定义了标志中用于指定页是否锁定的比特位。对应的宏可以用来操作该比特位:

  • PageLocked:查询比特位是否置位;

  • SetPageLocked:设置PG_locked位,不考虑先前的状态;

  • TestSetPageLocked:设置比特位,而且返回原值;

  • ClearPageLocked:清除比特位,不考虑先前的状态;

  • TestClearPageLocked:清除比特位,返回原值。

其他的页标志,同样有这样的一组宏用来操作对应的比特位。这些宏是原子的,即这些语句无法中断,否则会导致竞态条件(锁)。

列出一些重要的标志:

  • PG_locked:指定页是否锁定。如果置位,则内核的其他部分不允许访问该页。防止了内存管理出现竞态条件,例如,从硬盘读取数据到页帧时。

  • PG_error:涉及该页的I/O操作期间是否发生错误。

  • PG_referenced、PG_active:表明系统使用该页的活跃程度。在页交换子系统选择换出页时有作用(页回收和页交换)。

  • PG_uptodate:表示页的数据已经从块设备读取,并且没有出错。

  • PG_dirty(脏页):与硬盘上的数据相比,页的内容是否已经改变(内存中的数据与外存储器介质如硬盘上的

    数据)。出于性能考虑,页并不在每次改变后立即回写。所以内核使用该标志注明页已经改变,可以在稍后刷新。

  • PG_lru:有助于实现页面回收和切换。内核使用2个最近最少使用(least recently used,lru)链表来区别活动和不活动页(频繁使用的页排到链表最靠前的位置,而不活动的页向链表末尾方向移动)。如果页在其中一个链表中,则设置该位。此外,如果页在活动页链表中,也设置PG_active。(页回收和页交换)

  • PG_highmem:表示页在高端内存中,无法持久映射到内核内存中。

  • PG_private:如果page结构的private成员非空,则必须设置该位。用于I/O的页,可使用该字段将页细分为多个缓冲区(页缓存和块缓存)。

  • PG_writeback:如果页的内容处于向块设备回写的过程中,则设置该位。

  • PG_slab:如果页是slab分配器的一部分,则设置该位。

  • PG_swapcache:如果页处于交换缓存,则设置该位。在这种情况下,private包含一个类型为swap_entry_t的项。(页面回收和页交换)

  • PG_reclaim:在可用内存的数量变少时,内核尝试周期性地回收页,即剔除不活动、未用的页。(页面回收和页交换)在内核决定回收某个特定的页之后,则设置该位。

  • PG_buddy:如果页空闲且包含在伙伴系统的列表中,则设置该位,伙伴系统是页分配机制的核心。

  • PG_compound:表示该页属于一个更大的复合页,复合页由多个相连的普通页组成。

很多情况下,需要等待页的状态改变,才能恢复工作。内核有两个辅助函数来等待页:内核等待期间进入睡眠,解锁之后唤醒。

<pagemap.h> 
void wait_on_page_locked(struct page *page); //等待锁定页,直至解锁。
void wait_on_page_writeback(struct page *page); //等待到与页面相关的所有待决回写操作结束,将页面包含的数据同步到块设备(例如,硬盘)为止

标签:zone,min,page,UMA,内存,Linux,pages,内核
From: https://blog.csdn.net/m0_45372381/article/details/144597438

相关文章

  • Linux搭建NAS服务器结合海康录像存储
    NAS是NetworkAttachedStorage的缩写,也就是网络附属存储,是一种专门用于提供文件级数据存储服务的设备或系统。以下是NAS的主要特点:独立设备:NAS通常是连接到网络的独立硬件设备,内置硬盘驱动器,可以方便地扩展存储容量。文件共享:它通过网络协议(如SMB、NFS等)为多个客户端提供......
  • linux cgroup统一的层次结构文档
    cgroup统一的层次结构本文档描述了统一层次结构所做的更改及其基本原理。它最终将被合并到主cgroup文档中。目录背景基本操作底座cgroup.subtree_controlcgroup.controllers结构约束自上而下的没有内部任务。其他改动[Un]populatedNotification其他核心变化......
  • 《 C++ 点滴漫谈: 十 》揭秘 C++ struct 的潜力:内存布局、继承、优化,你都掌握了吗?
    摘要本文全面解析了C++中的struct关键字,从其基本概念到高级应用,涵盖了struct的成员与访问控制、构造函数与析构函数、继承与多态,以及内存布局和现代C++的特性扩展。此外,文章详细探讨了struct与class的异同、与union的对比,并剖析了常见的误区与陷阱。结合丰富......
  • Linux服务器网卡接口聚合Bond技术及原理
    什么是BondBond技术,也被称为网卡绑定或网卡捆绑,是将两个或更多的物理网卡绑定成一个虚拟的网卡。Bond的工作模式bond的模式有7种,mode=0,mode=1,mode=2,mode=3,mode=4,mode=5,mode=6bond常用的模式有两种1)mode=0,表示平衡负载round-robin,轮询的方式,第一个包走eth0,第二个包走eth......
  • 初识C/C++内存结构
    希望本文有助于学习C++的同学们理解C++的内存结构路漫漫,道阻且长。文章目录一、C++的内存结构是什么?二、代码区二、常量存储区三、全局/静态存储区四、栈(Stack)区五、堆区六、各个内存区域之间的联系总结一、C++的内存结构是什么?C/C++不同于其他的语言的其中一......
  • Ubuntu 部署饥荒联机版服务器 Linux DST_Dedicate_Server
    0.文件夹-~|-~/steamcmd#装的是steamcmd_linux.tar.gz以及其解压出来的东西|-~/DST#装的是DST服务器可执行文件、世界存档、世界模板|-~/DST/server#这个是steam下载的dedicated_DST_Server|-~/DST/server/mods#这个是要下载的mod,以及mod的保存位置,因为steam......
  • Linux安装MongDB
    1、准备安装包2、解压到指定目录tar-zxvfmongodb-linux-x86_64-4.0.10.tgz-C/opt/3、改名cd/opt/mvmongodb-linux-x86_64-4.0.10/mongodb-4.0.10/4、新建目录分别用来存储数据和日志cdmongodb-4.0.10/#数据存储目录mkdir-pdata/db#日志存储目录mkdir-p......
  • Linux SSH远程连接工具FinalShell下载及基本使用
     1.FinalShell下载安装官网:FinalShell官网1.1.FinalShell软件下载1.点击官网带有系统及版本的信息行到下载地址列表页面。2.选择自我系统匹配的版本,点击下方链接进行下载。1.2.FinalShell软件安装1.下载文件为exe(finalshell_windows_x64.exe)Windows可执行文件,直接......
  • Linux复习1——导论
    世界三大操作系统:Windows、UNIX、LinuxUNIX简洁、开放、可移植、价格高昂、闭源Linux继承UNIX的优点、免费、开源Linux的诞生:1991年,芬兰的一名大学生LinusTorvalds开发了linux内核#开源的优点:·低风险:开源社区维护开源代码,而开源社区几乎不可能倒闭·低成本:社区免费维护......
  • Linux常用命令之sosreport命令详解
    sosreport是一个广泛应用于Linux系统中用于收集系统配置和诊断信息的命令行工具,尤其在RedHatEnterpriseLinux(RHEL)及其衍生发行版(如CentOS和Fedora)中非常流行。它能够生成详细的系统报告,帮助技术支持人员或管理员进行故障排除、性能分析以及安全审计等工作。以......