物理内存相关的三个数据结构

news/2024/7/20 13:42:29 标签: 数据结构与算法, 内存管理

物理内存相关的三个数据结构

基于linux-4.9介绍linux内存管理中跟物理内存相关的三个数据结构pglist_data、zone、page。

pg_data_t

typedef struct pglist_data {
    /* 跟zone相关的成员 */
    struct zone node_zones[MAX_NR_ZONES];
    struct zonelist node_zonelists[MAX_ZONELISTS];
    int nr_zones;

    /* 内存节点的信息 */
    unsigned long node_start_pfn;     //内存节点起始的页帧号
    unsigned long node_present_pages; //总共的物理页面数
    unsigned long node_spanned_pages; //总共的物理地址所占的页面数,包括内存空洞

    /* 内存回收相关的数据结构 */
    wait_queue_head_t kswapd_wait;  
    wait_queue_head_t pfmemalloc_wait;
    struct task_struct *kswapd;       //内存回收专门的线程kswapd
    int kswapd_order; 
    enum zone_type kswapd_classzone_idx; 

}pg_data_t;

zone

[include/linux/mmzone.h]

struct zone {

    /* zone的水位,用来内存回收时使用 */
    unsigned long watermark[NR_WMARK];

    /* 记录紧急内存的大小 */
    unsigned long nr_reserved_highatomic;

    /* 每一个zone的最少保留内存,比如来自high zone
     * 的内存分配时,如果high zone空闲内存不够用,
     * 这个时候就会到normal zone取内存,
     * 而这个lowmem_reserve的意义就是为了,
     * 让high zone在normal zone取内存时不能无限制的取,
     * 最少要保留一些给normal zone。
     */
    long lowmem_reserve[MAX_NR_ZONES]; 

    /* 其所在的pglist_data */
    struct pglist_data      *zone_pgdat;

    /* 每一个cpu都维护一个page列表用来提高速度 */
    struct per_cpu_pageset __percpu *pageset;

    /* 第一个页的索引 */
    unsigned long           zone_start_pfn;

    /* spanned_pages 是这个zone跨越的所有物理内存的page数目
     * 包括内存空洞,它的计算方式:
     *  spanned_pages = zone_end_pfn - zone_start_pfn
     */
    unsigned long           spanned_pages;

    /* present_pages 是这个zone在物理内存真实存在的所有page数目,
     * 它的计算方式:
     *  present_pages = spanned_pages - absent_pages
     *  其中absent_pages指的是内存空洞的page数目
     */
    unsigned long           present_pages;

    /* managed_pages 是这个zone被Buddy管理的所有的page数目,
     * 计算的方式:
     *  managed_pages = present_pages - reserved_pages
     * 其中reserved_pages包括被Bootmem分配走的内存
     */
    unsigned long           managed_pages;

    /* 这个zone的名字 */
    const char              *name;

    /* ZONE_PADDING是为了cache line对齐,用空间换时间。
     * 防止cache line 中有多个不同的数据结构而同时影响多个
     * cpu
     */
    ZONE_PADDING(_pad1_)

    /* 包含所有的空闲页面的列表 */
    struct free_area        free_area[MAX_ORDER];

    /* zone flags, see below */
    unsigned long           flags;

    /* 用来保护free_area的锁 */
    spinlock_t              lock;

    ZONE_PADDING(_pad2_)

    /* 内存规整和CMA相关的成员 */
    #if defined CONFIG_COMPACTION || defined CONFIG_CMA
            unsigned long           compact_cached_free_pfn;
            unsigned long           compact_cached_migrate_pfn[2];
    #endif
    #ifdef CONFIG_COMPACTION
            unsigned int            compact_considered;
            unsigned int            compact_defer_shift;
            int                     compact_order_failed;
    #endif
    #if defined CONFIG_COMPACTION || defined CONFIG_CMA
            bool                    compact_blockskip_flush;
    #endif

    ZONE_PADDING(_pad3_)

    /* zone统计信息 */
    atomic_long_t           vm_stat[NR_VM_ZONE_STAT_ITEMS];

}____cacheline_internodealigned_in_smp;

zone中的一些成员的作用

包含所有空闲页面的free_area数组

[include/linux/mmzone.h]

struct free_area {
    struct list_head free_list[MIGRATE_TYPES];
    unsigned long    nr_free;
};

MIGRATE_TYPES的定义如下:

enum migratetype {
        MIGRATE_UNMOVABLE,   //不可迁移
        MIGRATE_MOVABLE,     //可迁移
        MIGRATE_RECLAIMABLE, //可被回收
        MIGRATE_PCPTYPES,    //跟per_cpu_pageset有关的迁移类型的个数
        MIGRATE_HIGHATOMIC = MIGRATE_PCPTYPES,
#ifdef CONFIG_CMA
        MIGRATE_CMA,
#endif
#ifdef CONFIG_MEMORY_ISOLATION
        MIGRATE_ISOLATE,
#endif
        MIGRATE_TYPES
};

migratetype定义了一个page的迁移类型。free_area为每一个迁移类型都单独设一个链表,以方便维护。

所以free_area这个数组包含了这个zone的所有空闲页面,并按照不通的迁移类型把这些页面放在不通的链表中方便管理。

每一个core维护的本地页链表per_cpu_pageset

struct per_cpu_pageset {
        struct per_cpu_pages pcp;
#ifdef CONFIG_NUMA
        s8 expire;
        u16 vm_numa_stat_diff[NR_VM_NUMA_STAT_ITEMS];
#endif
#ifdef CONFIG_SMP
        s8 stat_threshold;
        s8 vm_stat_diff[NR_VM_ZONE_STAT_ITEMS];
#endif
};

struct per_cpu_pages {
        int count;
        int high;
        int batch;

        /* 3个不同的迁移类型的链表 */
        struct list_head lists[MIGRATE_PCPTYPES];
};

每个cpu core都维护一个per_cpu_pageset,用来加速只分配一个page的情况。当只申请一个页的时候,可以直接从per_cpu_pageset中获取,per_cpu_pages->high记录了这个cpu core维护的per_cpu_pageset的最度的个数,如果超过了这个个数,就把其中的per_cpu_pages->batch个还给buddy系统。如果发现per_cpu_pageset中没有可分配的页,就从buddy中获取batch个。

MMU管理内存的最小单位–页page

struct page {

    /* 描述这个页面的状态:脏页、被上锁、正在写回?等等 */
    unsigned long flags;

    union {
        /* 如果是有文件背景的page,指向这个文件相关的信息,低bit为0(为0时也可能是NULL);
         * 如果是匿名页,指向一个anon_vma结构体,低Bit为1。*/
        struct address_space *mapping;
        void *s_mem;
        atomic_t compound_mapcount;
    };

    union {
        struct {
            union {
                atomic_t _mapcount; //这个页面被映射的次数 - 1
            };
            atomic_t _refcount;        //这个页面被引用的次数
        };
    };

    union {
        struct list_head lru;        //lru算法页面回收时使用
    };

    struct rcu_head rcu_head;

    union {
        unsigned long private;
    };

#ifdef CONFIG_MEMCG
        struct mem_cgroup *mem_cgroup;
#endif
}

存储所有page的数组mem_map

mem_map的定义在:

[mm/memory.c]

struct page *mem_map;

有两个宏就是利用mem_map来将page和pfn进行转换的:

[include/asm-generic/memory_model.h]

#define __pfn_to_page(pfn)  (mem_map + ((pfn) - ARCH_PFN_OFFSET))
#define __page_to_pfn(page) ((unsigned long)((page) - mem_map) + ARCH_PFN_OFFSET)

转载于:https://www.cnblogs.com/muahao/p/10299725.html


http://www.niftyadmin.cn/n/1819265.html

相关文章

ORA-12162: TNS:net service name is incorrectly specified

ORA-12162 Table of Contents 1. 错误信息2. 分析及解决 2.1. 原因2.2. 解决1 错误信息 $ sqlplus / as sysdbaSQL*Plus: Release 11.2.0.1.0 Production on Wed Aug 21 11:23:01 2019Copyright (c) 1982, 2009, Oracle. All rights reserved.ERROR: ORA-12162: TNS:net servi…

工业以太网EtherNet/IP协议安全分析整理

1、 EtherNet/IP : 设备可以用户数据报协议(UDP)的隐式报文传送基于IO的资料 ,用户传输控制协议(TCP)显示报文上传和下参数,设定值,程式 ,用户主站的轮询 从站周期性的更新或是改…

记录一次openMv色块查找

参加了一个比赛,最近在弄openMv识别目标色块部分,主要特定颜色的矩形,而矩形里有一个特定颜色的圆或者三角形。经过调试,圆是最好识别出来的,但问题出在矩形,我想查找红色的矩形,但识别蓝色的矩…

Java并发编程的艺术,解读并发编程的优缺点

并发编程的优缺点 使用并发的原因 多核的CPU的背景下,催生了并发编程的趋势,通过并发编程的形式可以将多核CPU的计算能力发挥到极致,性能得到提升。 在特殊的业务场景下先天的就适合于并发编程。 比如在图像处理领域,一张1024X76…

大白话科普:旨在解决用户交易隐私问题的 CoinJoin

(图片来自网络)相信即使不懂区块链的小伙伴也知道,匿名性是区块链(本文主要指公有链)的基本特点。比如在区块浏览器上可以查询到那么的地址以及交易信息,但却不能知道背后的用户是谁,这就是匿名…

centos6 安装node.js

1.源码安装 到nodejs官网下载源码https://nodejs.org/en/download/ 解压缩 进入源码目录 编译 安装 2.二进制安装 到nodejs官网下载二进制包https://nodejs.org/en/download/ 解压缩到指定安装目录 配置环境变量 vim .bash_profile(vim /etc/profile 全局变量) export NODE_HOM…

IDE 插件新版本发布,开发效率 “biu” 起来了

2019独角兽企业重金招聘Python工程师标准>>> 近日,Cloud Toolkit正式推出了面向 IntelliJ 和 Eclipse 两个平台的新款插件,本文挑选了其中三个重大特性进行解读,点击文末官网跳转链接,可查看详细的版本说明。 本地应用…

【Java】多线程学习——Callable和Future接口

Callable是一个接口。它是逻辑上的概念Task的一个具体存在(说成实现Implement可能会造成混淆,因为它是一个接口),它有一个带返回值的run()方法。 Future也是一个接口,它的作用是: 1、获取Callable中run方法…