内存管理-5-物理内存数据结构-3-struct page

基于msm-5.4


一、struct page简介

物理页帧,系统中的每个物理页面都有一个与之关联的 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


1.1 struct page 在内存管理中扮演的角色:

struct page 是物理内存页的元数据载体,其核心作用是为内核提供统一管理物理内存页的基础结构。每个物理页帧(Page Frame)对应一个 struct page 实例,通过它内核可以追踪页的状态、属性及关联信息。

struct page 记录页的物理属性(如是否空闲、脏页、锁定状态等); 系统所有 struct page 组成全局数组 mem_map, 通过页帧号(PFN)可直接索引到对应页的元数据。它在内存回收、LRU链表、反向映射、页缓存与文件映射。


二、成员介绍

//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));

__init_single_page() 中在初始化一个page结构之前,先将其清0.

成员介绍:

flags:

set_page_zone()中表明bit63表示此page属于哪个zone,由 set_page_links() 可知,当前配置下,node_id 和 section_id 都没保存在这个flags中。

mapping

利用指针总是4的整数倍这个特性,成员 mapping 的最低两位用来作为页映射标志,最低位 PAGE_MAPPING_ANON 表示匿名页。如果物理页是匿名页,page.mapping = (struct anon_vma 的地址 | PAGE_MAPPING ANON)。如果物理页是文件页 page.mapping 指向结构体 address_space。

index:

页面的pageblock的迁移类型的缓存值,在将页面放入 pcplist 时使用。在大多数情况下,用于在从 pcplist 释放时避免页面块迁移类型查找. 使用见 get_pcppage_migratetype();

它是在映射里面的偏移,单位是页。如果是匿名映射,那么 index 是物理页对应的虚拟页在虚拟内存区域中的页偏移,如果是文件映射那么 index 是物理页存储的数据在文件中的页偏移。

_mapcount:

是映射计数,反映物理页被映射到多少个虚拟内存区域。初始值是 -1, 加上1以后才是真实的映射计数,建议使用内联函数 page_mapcount() 获取页的映射计数。

 

三、辅助函数

1. 获取page对应的zone、node、section

//include/linux/mm.h

static inline struct zone *page_zone(const struct page *page)
{
    return &NODE_DATA(page_to_nid(page))->node_zones[page_zonenum(page)];
}
static inline pg_data_t *page_pgdat(const struct page *page)
{
    return NODE_DATA(page_to_nid(page));
}
static inline unsigned long page_to_section(const struct page *page)
{
    return (page->flags >> SECTIONS_PGSHIFT) & SECTIONS_MASK; //64 0
}


四、其它相关

1. __mm_zero_struct_page() 中有对 struct page 的大小进行检查,必须是 56, 64, 72, 80 这几个大小,否则在编译时报错。

2. page有一组debug trace

trace_page_ref_{set/mod/mod_and_test/mod_and_return/mod_unless/freeze/unfreeze}, 使用见 debug_page_ref.c

 

posted on 2026-04-02 10:41  Hello-World3  阅读(4)  评论(0)    收藏  举报

导航