内存管理之内存节点Node、Zone、Page

注:本文分析基于linux-4.18.0-193.14.2.el8_2内核版本,即CentOS 8.2

1、Node

Node是内存管理最顶层的结构,在NUMA架构下,CPU平均划分为多个Node,每个Node有自己的内存控制器及内存插槽。CPU访问自己Node上的内存时速度快,而访问其他CPU所关联Node的内存的速度比较慢。而UMA则被当做只有一个Node的NUMA系统。

内核中使用struct pglist_data来描述一个Node节点,我们看下主要的一些变量,

/*
 * On NUMA machines, each NUMA node would have a pg_data_t to describe
 * it's memory layout. On UMA machines there is a single pglist_data which
 * describes the whole memory.
 */
typedef struct pglist_data {
    //当前节点中包含的zone数组,如ZONE_DMA,ZONE_DMA32,ZONE_NORMAL
	struct zone node_zones[MAX_NR_ZONES];
	struct zonelist node_zonelists[MAX_ZONELISTS];
    //当前节点中不同内存域zone的数量
	int nr_zones;
	...
	unsigned long node_start_pfn; //当前节点的页帧的起始值
	unsigned long node_present_pages; //当前节点可使用的page数量
	unsigned long node_spanned_pages; //包含空洞内存的内存总长度
	int node_id; //节点编号
	wait_queue_head_t kswapd_wait; //kswapd进程的等待队列
	wait_queue_head_t pfmemalloc_wait; //直接内存回收过程中的进程等待队列
	struct task_struct *kswapd;	//指向该结点的kswapd进程的task_struct
	int kswapd_order; //kswap回收页面大小
    enum zone_type kswapd_classzone_idx; //kswap扫描的内存域范围
	...
	//每个节点的保留内存
	unsigned long		totalreserve_pages;

	//zone reclaim becomes active if more unmapped pages exist.
	unsigned long		min_unmapped_pages;
    //当前node中可回收slab页面阈值,超过该值才会回收该node内存
	unsigned long		min_slab_pages;
    ...
	/* Fields commonly accessed by the page reclaim scanner */
    //LRU链表管理结构
	struct lruvec		lruvec;
    ...
} pg_data_t;
  • 第一部分主要是内存管理域,比如包括的zone类型,以及该Node管理的内存范围等。

  • 第二部分主要是和kswap进程相关,我们后续会专门对kswap进行梳理,看看它是如何工作的。从这里我们也能看出,kswap是和Node挂钩,也就是每个Node对应一个kswap进程,更多的我们后续再说。

  • 第三部分是LRU链表相关,这个成员原本是在zone结构体中,后来移到了Node上,我们后面也会对页面的LRU管理进行梳理。

2、Zone

在x86_64的机器上,主要有以下几种Zone类型,

[root@my-test proc]# cat /proc/zoneinfo | grep -w zone
Node 0, zone      DMA
Node 0, zone    DMA32
Node 0, zone   Normal
Node 0, zone  Movable
Node 0, zone   Device

ZONE_DMA:内存首部16MB,即低端范围的物理内存,某些工业标准体系结构(ISA)设备需要用到ZONE_DMA;

    ZONE_DMA32:标记了使用32位地址字可寻址, 适合DMA的内存域。

    ZONE_NORMAL:16MB~896MB,该部分的内存由内核直接映射到线性地址空间的较高部分;

    ZONE_HIGHMEM:896MB~末尾,将保留给系统使用,是系统中预留的可用内存空间,不能被内核直接映射。

    ZONE_MOVABLE:内核定义了一个伪内存域ZONE_MOVABLE, 在防止物理内存碎片的机制memory migration中需要使用该 内存域. 供防止物理内存碎片的极致使用。

    ZONE_DEVICE:为支持热插拔设备而分配的Non Volatile Memory非易失性内存。

内存域Zone用struct zone描述,

struct zone {
	/* Read-mostly fields */

	//每个zone的min、low、high水位值
	unsigned long watermark[NR_WMARK];

	unsigned long nr_reserved_highatomic;

    //每个zone的预留内存,有些进程只能使用某些低地址的zone空间,但是在极端情况下
    //有些进程本来是可以使用高地址zone,但是可能此时系统内存刚好被占用,就会去
    //使用这些低地址zone的内存,导致后续一些进程即使高地址zone还有空闲内存,但还是
    //无法获得内存。因此需要为每个zone预留一部分内存,保证跨zone内存分配有上限
    //预留的值可通过/proc/sys/vm/lowmem_reserve_ratio设置
	long lowmem_reserve[MAX_NR_ZONES];

	int node;
	struct pglist_data	*zone_pgdat; //指向所属node节点
	struct per_cpu_pageset __percpu *pageset; //每CPU保留一些页面,避免自旋锁冲突

	unsigned long		zone_start_pfn; //该内存域起始页帧地址
	unsigned long		managed_pages;  //该zone中被伙伴系统管理的页面数量
	unsigned long		spanned_pages;  //该zone包含的总页数,包含空洞
	unsigned long		present_pages;  //该zone包含的总页数,不包含空洞
    //内存域名字,如"DMA", "NROMAL"
	const char		*name;
    ...
    //页面使用状态的信息,用于伙伴系统的,每个元素对应不同阶page大小
    //目前系统上有11种不同大小的页面,从2^0 - 2^10,即4k-4M大小的页面
	struct free_area	free_area[MAX_ORDER];

	/* zone flags, see below */
	unsigned long		flags;
    ...
} ____cacheline_internodealigned_in_smp;
  • 第一部分为zone的水位值watermark,包括MIN、LOW、HIGH三个水位,这三个值根据/proc/sys/vm/min_free_kbytes来设置;以及zone的保留内存lowmem_reserve,则是由/proc/sys/vm/lowmem_reserve_ratio控制,关于这两部分可以参考之前的文章《vm内核参数之内存水位min_free_kbytes和保留内存lowmem_reserve_ratio》。
  • 第二部分就是内存域的描述,比如管理的内存范围以及page数量
  • 第三部分就是free_area,用于管理伙伴系统上11个不同大小的page页面

3、Page

页是内存管理的最小单位,页面中的内存其物理地址是连续的,每个物理页面由struct page描述。为了节约内存,struct page是个联合体,我们在之前梳理slab的时候知道,slab管理结构也是用struct page描述。

struct page {
    /* 在lru算法中主要用到的标志
     * PG_active: 表示此页当前是否活跃,当放到或者准备放到活动lru链表时,被置位
     * PG_referenced: 表示此页最近是否被访问,每次页面访问都会被置位
     * PG_lru: 表示此页处于lru链表
     * PG_mlocked: 表示此页被mlock锁在内存中,禁止换出和释放
     * PG_swapbacked: 表示此页依靠swap,可能是进程的匿名页(堆、栈、数据段),匿名mmap共享内存映射,shmem共享内存映射
     */
	unsigned long flags;		/* Atomic flags, some possibly
					 * updated asynchronously */
    struct {	/* Page cache and anonymous pages */
        //通过该结构挂到node的对应LRU链表
        struct list_head lru;
        //以下三个变量都是pagecache相关
        struct address_space *mapping;
        pgoff_t index;		/* Our offset within mapping. */
        /**
         * @private: Mapping-private opaque data.
         * Usually used for buffer_heads if PagePrivate.
         * Used for swp_entry_t if PageSwapCache.
         * Indicates order in the buddy system if PageBuddy.
         */
        unsigned long private;
    };
    ...
	struct mem_cgroup *mem_cgroup; //cgroup相关

	void *virtual;			//页面内核虚拟地址
    ...
} _struct_page_alignment;
  • 第一部分是LRU链表相关,包括用于LRU的标志位,和LRU表头
  • 第二部分是pagecache相关,我们之后也会梳理
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值