zoukankan      html  css  js  c++  java
  • 物理内存相关的三个数据结构

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

    基于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)
    
  • 相关阅读:
    说说那些低级错误
    vue双向数据绑定
    妙用$.extend
    写一个限制执行次数的函数
    强类型语言不好的地方,比如这样:
    form表单里的坑
    Java Day 09
    Java Day 08
    Java Day 07
    Java Day 06
  • 原文地址:https://www.cnblogs.com/muahao/p/10299725.html
Copyright © 2011-2022 走看看