内存管理-5-物理内存管理-1-相关结构体

基于msm-5.4

概述:

物理内存组织关系如下图。非服务器的设备,一般只有一个内存node节点,使用 pglst_data 结构描述;一个内存节点下有较多分区,使用 zone 结构描述;每个分区中有不同大小的空闲内存块,通过 free_area 结构进行描述;物理内存管理的最小单位使用 page 结构进行描述。

 物理内存管理主要分两部分,一是权限管理,另外就是将其分成大小相同的内存块,叫做物理页(Frame Page),使用 struct page 表示。分区 struct zone 表示。

 

一、struct pglist_data

1. 简介

表示一个node里面的内存资源。在 NUMA 机器上,每个 NUMA 节点都会有一个 pg_data_t 来描述其内存布局。在 UMA 机器上,只有一个 pglist_data 来描述整个内存。 内存统计信息和页面替换数据结构按区域(zone)进行维护。

初始化函数:zone_sizes_init() 其调用路径:

start_kernel //main.c
    setup_arch //setup.c
        bootmem_init //init.c
            zone_sizes_init
            memblock_dump_all //dump所有的memory和reserved内存区间

2. 成员介绍

//include/linux/mmzone.h
typedef struct pglist_data {
    struct zone node_zones[MAX_NR_ZONES];
    struct zonelist node_zonelists[MAX_ZONELISTS];
    int nr_zones;
    spinlock_t node_size_lock;
    unsigned long node_start_pfn;
    unsigned long node_present_pages;
    unsigned long node_spanned_pages;
    int node_id;
    wait_queue_head_t kswapd_wait;
    wait_queue_head_t pfmemalloc_wait;
    struct task_struct *kswapd;
    struct task_struct *mkswapd[MAX_KSWAPD_THREADS]; //16
    int kswapd_order;
    enum zone_type kswapd_classzone_idx;
    int kswapd_failures;

    int kcompactd_max_order;
    enum zone_type kcompactd_classzone_idx;
    wait_queue_head_t kcompactd_wait;
    struct task_struct *kcompactd;

    unsigned long        totalreserve_pages;
    ZONE_PADDING(_pad1_) //cache line aligned,size=0
    spinlock_t        lru_lock;
    struct deferred_split deferred_split_queue;
    struct lruvec        lruvec;
    unsigned long        flags;

    ZONE_PADDING(_pad2_) //cache line aligned,size=0
    struct per_cpu_nodestat __percpu *per_cpu_nodestats;
    atomic_long_t        vm_stat[NR_VM_NODE_STAT_ITEMS];
} pg_data_t;

node_present_pages: 物理页的总个数。

node_spanned_pages: 物理页面范围的总大小,包括空洞。

mkswapd[]: 显示最大可以有16个kswapd线程,被 mem_hotplug_begin/end() 保护。

kswapd_failures: 'reclaimed == 0'运行次数。

totalreserve_pages: 每个节点保留的页面数,不可用于用户空间分配。

_pad1_: 页面回收使用的写密集型字段

lruvec: 页面回收扫描器经常访问的字段

per_cpu_nodestats: 每node的vmstats

vm_stat: 统计信息

 

二、struct zone

1. 简介

描述一个内存区。内存可以按照不同的用途与类型划分为不同的区域,zone结构表示内存区域,系统存在的zone见 enum zone_type 成员。

ZONE_DMA: 表示这片区域用于DMA,一般是给x86使用的,64位上将其取消掉了,arm系统上也没有使用这个zone,因为划分了这个区域若不使能的话它就会一直空着,造成内存浪费。arm使用另外一机制,叫做cma,它能充分利用不浪费一片内存。

使用哪些zone区域是可以配置的。

水位是每内存区的。

2. 成员介绍

//include/linux/mmzone.h
struct zone {
    /* Read-mostly fields */
    unsigned long _watermark[NR_WMARK];
    unsigned long watermark_boost;

    unsigned long nr_reserved_highatomic;
    long lowmem_reserve[MAX_NR_ZONES]; //2
    struct pglist_data    *zone_pgdat;
    struct per_cpu_pageset __percpu *pageset;
    bool            cma_alloc; //CONFIG_CMA
    unsigned long        *pageblock_flags; //CONFIG_SPARSEMEM
    unsigned long        zone_start_pfn;

    atomic_long_t        managed_pages;
    unsigned long        spanned_pages;
    unsigned long        present_pages;

    const char        *name;
    unsigned long        nr_isolate_pageblock; //CONFIG_MEMORY_ISOLATION
    seqlock_t        span_seqlock; //CONFIG_MEMORY_HOTPLUG
    int initialized;
    ZONE_PADDING(_pad1_) //cache aligned, size=0, Write-intensive
    struct free_area    free_area[MAX_ORDER];
    unsigned long        flags;
    spinlock_t        lock;
    ZONE_PADDING(_pad2_) //cache aligned, size=0, Write-intensive
    unsigned long percpu_drift_mark;
#if defined CONFIG_COMPACTION || defined CONFIG_CMA //both-enabled
    unsigned long        compact_cached_free_pfn;
    unsigned long        compact_cached_migrate_pfn[2];
    unsigned long        compact_init_migrate_pfn;
    unsigned long        compact_init_free_pfn;
#endif
#ifdef CONFIG_COMPACTION //enabled
    unsigned int        compact_considered;
    unsigned int        compact_defer_shift;
    int                    compact_order_failed;
#endif
#if defined CONFIG_COMPACTION || defined CONFIG_CMA //both-enabled
    bool            compact_blockskip_flush;
#endif
    bool            contiguous;
    ZONE_PADDING(_pad3_) //cache aligned, size=0, Write-intensive
    /* Zone statistics */
    atomic_long_t        vm_stat[NR_VM_ZONE_STAT_ITEMS];
    atomic_long_t        vm_numa_stat[NR_VM_NUMA_STAT_ITEMS];
} ____cacheline_internodealigned_in_smp;

_watermark[]: 当前zone的水位,使用 min/low/high_wmark_pages(zone) 宏访问,此宏会考虑boost水位。超出水位后会触发内存回收等机制。

lowmem_reserve[]: 我们不知道要分配的内存是否可释放或/以及最终是否会被释放,因此为了避免完全浪费几GB的RAM,我们必须保留一些较低区域内存(否则,尽管较高区域有大量可释放的 RAM,我们仍有在较低区域运行 OOM 的风险)。如果 sysctl_lowmem_reserve_ratio sysctl 发生变化,则在运行时重新计算此数组。它相当于一个备胎,分配内存的时候可以指定参数来决定去哪个zone里面去分,若目标zone中已经没有内存了,它会去其它zone里面去找,若其它zone中也没有内存了,就会启用 lowmem_reserve[] 中预留的内存。

pageblock_flags: pageblock_nr_pages 块的标志。请参阅 pageblock-flags.h。在 SPARSEMEM 中,此映射存储在 struct mem_section 中。

zone_start_pfn:  此zone管理的物理内存的起始页帧号,zone_start_pfn == zone_start_paddr >> PAGE_SHIFT

spanned_pa​​ges: 是区域所跨越的总页面数,包括空洞(holes),其计算方式为:spanned_pa​​ges = zone_end_pfn - zone_start_pfn;

present_pages: 是区域内现有的物理页面,其计算方式为:present_pages = spanned_pa​​ges - absent_pages(空洞中的页面);

managed_pa​​ges: 此zone实际管理的页面数,其计算方式为:managed_pa​​ges = present_pages - reserved_pa​​ges;(reserved_pa​​ges 包括由 bootmem 分配器分配的页面)因此,内存热插拔或内存电源管理逻辑可以使用 present_pages 通过检查(present_pages - managed_pa​​ges)找出非托管页面。并且,页面分配器和 vm 扫描器应使用 managed_pa​​ges 来计算各种水位和阈值。

nr_isolate_pageblock: 孤立页块的数量。它用于解决由于检索页块的迁移类型不当而导致的空闲页计数不正确的问题。受 zone->lock 保护。

free_area[]: 不同大小的可用区域,MAX_ORDER定义为11,即0-10,一次能从buddy系统中分配出来的最大连续内存块的大小是4MB. 下标 i 中存放的是页数为2^i个页面的分组。每个数组元素里面的每一个迁移类型都有一个链表。

flags: zone的标志位。

lock: 主要保护free_area[]

percpu_drift_mark: 当可用页面低于此值时,读取可用页面数量时会采取额外步骤,以避免每个 CPU 计数器漂移导致水位被突破。

compact_cached_free_pfn: 指示pfn无压缩扫描仪器应从何处开始。

compact_cached_migrate_pfn[]: pfn异步和同步压缩迁移扫描器应开始的位置。

compact_defer_shift/compact_considered: 如果压缩失败,则在重试之前会跳过 1<<compact_defer_shift 进行压缩。自上次失败以来尝试的次数会通过 compact_considered 进行跟踪。

compact_blockskip_flush: 当应清除 PG_migrate_skip 位时,设置为 true

vm_stat/vm_numa_stat: zone的统计信息。

free_area[]: 当前zone空闲可用的物理页帧。free_area.free_list: 随着物理页的申请与释放,page在这些链表上动态移动。

 

三、struct free_area

1. 简介

描述固定大小的空余内存块,zone中不同大小的空闲内存块使用数组进行描述。

2. 成员介绍

//include/linux/mmzone.h
struct free_area {
    struct list_head    free_list[MIGRATE_TYPES];
    unsigned long        nr_free;
};

free_list[]: 一个链表数组,同一个zone中的同一大小同一迁移属性的内存块挂在同一个链表上。

nr_free: 链表中元素的个数。

 

四、struct page

1. 简介

物理页帧,系统中的每个物理页面都有一个与之关联的 struct page,用于跟踪我们当前正在使用该页面的用途。请注意,我们无法跟踪哪些任务正在使用页面,但如果它是页面缓存页面,rmap 结构可以告诉我们谁在映射它。

如果您使用 alloc_pages() 分配页面,则可以将 struct page 中的部分空间用于自己的目的。主联合中的五个字(40B)可用,但第一个字的位 0 除外,必须保持清0。许多用户使用此字来存储指向保证对齐的对象的指针。如果您使用与 page->mapping
相同的存储,则必须在释放页面之前将其恢复为 NULL。

如果您的页面不会映射到用户空间,您也可以使用 mapcount 联合中的四个字节,但必须在释放它之前调用 page_mapcount_reset()。

如果要使用 refcount 字段,则必须以其他 CPU 临时增加然后减少 refcount 的方式使用它,而不会导致问题。从 alloc_pages() 接收页面时,refcount 将为正数。

如果分配 order > 0 的页面,则可以使用每个子页面中的某些字段,但之后可能需要恢复它们的值。

SLUB 使用 cmpxchg_double() 原子更新其空闲列表和计数器。这要求空闲列表和计数器相邻且双字8字节对齐。 我们将所有结构页面对齐到双字边界,
并确保“空闲列表”在结构内对齐。

page结构里面有很多union类型,因为page可以用来表示不同类型的物理内存
page cache: 页缓存,磁盘在去读写的时候在内存中会有一个缓存,叫页缓存。平时申请的匿名页,也会使用page cache类型表示。
page_pool:
slab:
Tail pages:
Second tail page:
Page table:

系统在初始化的时候,对于每一个物理页帧,都会定义一个 struct page 结构去表示它。比如有一千个页帧,就要定义一千个page结构体,这些page结构体也会存放在内存中,使用 vmemmap 指针指向这个区域。此时物理页帧号pfn和page结构之间就存在了一一对应的关系,因为两者都是线性排列的。

#define __pfn_to_page(pfn)    (vmemmap + (pfn))                  //返回指针
#define __page_to_pfn(page)    (unsigned long)((page) - vmemmap)  //参数page是指针

//注:pfn = paddr >> PAGE_SHIFT

 

2. 成员介绍

//include/linux/mm_types.h
struct page {
    unsigned long flags;
    /* 此union结构是40B */
    union {

        /* Page cache and anonymous pages 40B */
        struct {
            struct list_head lru;
            struct address_space *mapping;
            pgoff_t index;
            unsigned long private;
        };

        /* page_pool used by netstack 8B */
        struct {
            dma_addr_t dma_addr;
        };

        /* slab, slob and slub 40B */
        struct {
            union {
                struct list_head slab_list;
                /* Partial pages */
                struct {    
                    struct page *next;
                    int pages;
                    int pobjects;
                };
            };
            struct kmem_cache *slab_cache;
            void *freelist;
            union {
                void *s_mem;
                unsigned long counters;
                struct {            
                    unsigned inuse:16;
                    unsigned objects:15;
                    unsigned frozen:1;
                };
            };
        };

        /* Tail pages of compound page 16B */
        struct {
            unsigned long compound_head;

            /* First tail page only */
            unsigned char compound_dtor;
            unsigned char compound_order;
            atomic_t compound_mapcount;
        };

        /* Second tail page of compound page 32B */
        struct {
            unsigned long _compound_pad_1;
            unsigned long _compound_pad_2;
            struct list_head deferred_list;
        };

        /* Page table pages 40B */
        struct {
            unsigned long _pt_pad_1;
            pgtable_t pmd_huge_pte;
            unsigned long _pt_pad_2;
            union {
                struct mm_struct *pt_mm;
                atomic_t pt_frag_refcount;
            };
            spinlock_t *ptl;
        };

        /* ZONE_DEVICE pages 16B */
        struct {
            struct dev_pagemap *pgmap;
            void *zone_device_data;
        };

        /* 16B */
        struct rcu_head rcu_head;
    };

    /* 4B */
    union {        
        atomic_t _mapcount;
        unsigned int page_type;
        unsigned int active;
        int units;
    };

    atomic_t _refcount;
    struct mem_cgroup *mem_cgroup; //CONFIG_MEMCG
    int _last_cpupid;
} __aligned(2 * sizeof(unsigned long));

 

posted on 2024-06-18 21:41  Hello-World3  阅读(6)  评论(0编辑  收藏  举报

导航