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

基于msm-5.4

一、struct mm_struct 简介

struct mm_struct { //mm_types.h
    struct { //这里还有一个struct,用于随机存储的
        struct vm_area_struct *mmap;
        struct rb_root mm_rb;
        u64 vmacache_seqnum;
#ifdef CONFIG_SPECULATIVE_PAGE_FAULT //默认不使能
        rwlock_t mm_rb_lock;
#endif
#ifdef CONFIG_MMU
        unsigned long (*get_unmapped_area) (struct file *filp, unsigned long addr,
            unsigned long len, unsigned long pgoff, unsigned long flags);
#endif
        /* mmap映射区起始地址 */
        unsigned long mmap_base;
        unsigned long mmap_legacy_base;
#ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES //默认不使能
        unsigned long mmap_compat_base;
        unsigned long mmap_compat_legacy_base;
#endif
        unsigned long task_size;
        unsigned long highest_vm_end;
        pgd_t * pgd;

#ifdef CONFIG_MEMBARRIER //默认使能
        atomic_t membarrier_state;
#endif
        atomic_t mm_users;
        atomic_t mm_count;

#ifdef CONFIG_MMU
        atomic_long_t pgtables_bytes;
#endif
        int map_count;
        spinlock_t page_table_lock;
        struct rw_semaphore mmap_sem;
        struct list_head mmlist;
        unsigned long hiwater_rss; /* High-watermark of RSS usage */
        unsigned long hiwater_vm;  /* High-water virtual memory usage */
        unsigned long total_vm;       /* Total pages mapped */
        unsigned long locked_vm;   /* Pages that have PG_mlocked set */
        atomic64_t    pinned_vm;   /* Refcount permanently increased */
        unsigned long data_vm;       /* VM_WRITE & ~VM_SHARED & ~VM_STACK */
        unsigned long exec_vm;       /* VM_EXEC & ~VM_WRITE & ~VM_STACK */
        unsigned long stack_vm;       /* VM_STACK */
        unsigned long def_flags;
        spinlock_t arg_lock; /* protect the below fields */
        unsigned long start_code, end_code, start_data, end_data;
        unsigned long start_brk, brk, start_stack;
        unsigned long arg_start, arg_end, env_start, env_end;
        unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
        struct mm_rss_stat rss_stat;
        struct linux_binfmt *binfmt;
        mm_context_t context;
        unsigned long flags; /* Must use atomic bitops to access */
        struct core_state *core_state; /* coredumping support */
#ifdef CONFIG_AIO //默认使能
        spinlock_t            ioctx_lock;
        struct kioctx_table __rcu    *ioctx_table;
#endif
#ifdef CONFIG_MEMCG //默认使能
        struct task_struct __rcu *owner;
#endif
        struct user_namespace *user_ns;
        struct file __rcu *exe_file;
#ifdef CONFIG_MMU_NOTIFIER //默认不使能
        struct mmu_notifier_mm *mmu_notifier_mm;
#endif
#if (defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_GKI_OPT_FEATURES)) && !USE_SPLIT_PMD_PTLOCKS //默认不成立
        pgtable_t pmd_huge_pte; /* protected by page_table_lock */
#endif
#ifdef CONFIG_NUMA_BALANCING //默认不使能
        unsigned long numa_next_scan;
        unsigned long numa_scan_offset;
        int numa_scan_seq;
#endif
        atomic_t tlb_flush_pending;
#ifdef CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH //默认不使能
        bool tlb_flush_batched;
#endif
        struct uprobes_state uprobes_state;
#ifdef CONFIG_HUGETLB_PAGE //默认不使能
        atomic_long_t hugetlb_usage;
#endif
        struct work_struct async_put_work;
        ANDROID_VENDOR_DATA(1);
    } __randomize_layout;
    unsigned long cpu_bitmap[];
};

mm_struct 是一个进程地址空间对象,同一进程内多个线程通常共享同一个 mm_struct。

1. 成员介绍

第一部分,地址空间核心索引与布局:

mmap
该进程所有 VMA 的双向链表头,按虚拟地址排序。典型使用场景是 缺页、mmap、munmap、brk 等路径遍历 VMA。


mm_rb
同一批 VMA 的红黑树的根,提供更快查找。典型使用场景是按地址快速定位 VMA,复杂度优于纯链表遍历。


vmacache_seqnum
每线程 VMA cache 的序列号,用于失效判断。意义是,VMA 拓扑变化时,线程本地 vmacache 需要失效重建。


get_unmapped_area
查找可用虚拟地址区间的函数指针。在文件映射、匿名映射时选址策略可由体系结构或文件系统定制。


mmap_base
mmap 区域基址(通常用于 top-down 分配)。


mmap_legacy_base
兼容旧式 bottom-up mmap 分配时的基址。


mmap_compat_base
依赖 CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES, 默认不使能,用于兼容位宽进程的 mmap 基址。


mmap_compat_legacy_base:
依赖 CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES, 默认不使能,用于兼容位宽进程 bottom-up 基址。


task_size
该 mm 的用户态地址空间上限。


highest_vm_end
当前 VMA 里最高结束地址缓存,帮助快速边界判断。


pgd
该地址空间顶级页表指针(内核虚拟地址)。


第二部分、同步与生命周期引用计数

membarrier_state
membarrier 相关状态位。它被放在接近 pgd 的位置,考虑切换 mm 的缓存局部性。


mm_users
用户引用计数。通常代表正在“使用该地址空间”的用户数量;降到 0 会触发释放流程的一部分。在常用接口 mmget()/mmput()。


mm_count
结构体本体引用计数(包含来自 mm_users 的基准引用)。降到 0 才真正释放 mm_struct。常用接口 mmgrab()/mmdrop()。


pgtables_bytes
该 mm 的页表页内存占用统计(字节)。


map_count
当前 VMA 数量计数。


page_table_lock
页表及部分计数的自旋锁。主要用于保护页表修改、部分 RSS/统计字段(视配置而定)。


mmap_sem
地址空间级读写信号量。读锁场景:查 VMA、缺页大多路径。写锁场景:mmap/munmap/mprotect/brk 改布局路径。


mmlist
全局 mm 链表节点。所有候选可换出 mm 串在全局 init_mm.mmlist 上,由 mmlist_lock 保护;swapoff 等全局扫描会用到。


第三部分、内存规模与高水位统计

hiwater_rss
RSS 历史峰值(高水位)。

hiwater_vm
虚拟内存总量历史峰值。

total_vm
当前映射总页数统计。

locked_vm
被 mlock 等机制锁住的页数统计(PG_mlocked)。

pinned_vm
长期 pin 住的页统计(如长期 GUP pin,引用永久抬高语义)。

data_vm
数据段类可写私有映射页统计。 注释上说明 VM_WRITE & ~VM_SHARED & ~VM_STACK

exec_vm
可执行文本类映射页统计。注释上说明 VM_EXEC & ~VM_WRITE & ~VM_STACK

stack_vm
栈相关映射页统计。

def_flags
新建 VMA 的默认标志模板。


第四部分、进程镜像地址边界与参数区信息

arg_lock
保护它下面这批地址边界字段的锁。


start_code
代码段起始地址。


end_code
代码段结束地址。


start_data
数据段起始地址。


end_data
数据段结束地址。


start_brk
堆初始起点。


brk
当前堆顶。


start_stack
用户栈起始位置(初始化时的栈顶语义点)。


arg_start
argv 区起始地址。


arg_end
argv 区结束地址。


env_start
环境变量区起始地址。


env_end
环境变量区结束地址。


saved_auxv[]

保存 ELF auxiliary vector,默认是46个元素,供 /proc/PID/auxv 等导出。xxd /proc/1/auxv 同时16进制和字符显示此文件中的内容。

ELF auxiliary vector(辅助向量)是内核在启动用户态进程时,通过栈传给进程的一批键值对信息,用来告诉进程关于运行环境的底层细节。它本质上是一个 {type, value} 数组,放在初始栈上,位于 argv 和 envp 之后:

栈底 (高地址)
  ...
  [env 字符串]
  [arg 字符串]
  [envp[] 指针数组]     ← envp
  [argv[] 指针数组]     ← argv
  [argc]
  [auxiliary vector]    ← 一系列 {AT_TYPE, value} 对
  [AT_NULL, 0]          ← 结束标记
栈顶 (低地址)

每一项的 C 表示是 Elf64_auxv_t:

typedef struct {
    uint64_t a_type;     //类型编号,如 AT_PAGESIZE = 6
    union {
        uint64_t a_val;  //对应的值
    } a_un;
} Elf64_auxv_t;

常见类型及含义:

--------------------------------------------------------------------
类型常量         值  含义
--------------------------------------------------------------------
AT_PHDR          3   程序头表(Program Headers)在内存中的地址
AT_PHNUM         5   程序头表条目数
AT_PAGESZ        6   系统页大小(通常 4096)
AT_BASE          7   动态链接器加载基址
AT_ENTRY         9   程序入口地址(ELF entry point)
AT_UID           11  真实用户 ID
AT_GID           13  真实组 ID
AT_HWCAP         16  CPU 硬件能力位(ARM 上有 NEON/CRC32 等标志)
AT_CLKTCK        17  sysconf(_SC_CLK_TCK) 时钟频率
AT_RANDOM        25  内核提供的 16 字节随机数据地址(用于stack canary等)
AT_HWCAP2        26  扩展硬件能力位
AT_EXECFN 31 程序的文件名 AT_SYSINFO_EHDR
33 vDSO 的 ELF 头地址(glibc 用来加速系统调用) --------------------------------------------------------------------

mm_struct.saved_auxv[] 就是把这批键值对保存在进程的 mm 里,供:/proc/PID/auxv 导出(可以用 xxd /proc/self/auxv 查看), 和进程重新 exec 后恢复。

用户态可以通过 page_size = getauxval(AT_PAGESZ); 或解析 /proc/PID/auxv 文件读取。

Android libc库中提供了相关函数,用户空间可直接使用 pagesize = sysconf(_SC_PAGE_SIZE); 其调用路径:

pagesize = sysconf(_SC_PAGE_SIZE)
    long sysconf(int name) //bionic/libc/bionic/sysconf.cpp
        case _SC_PAGESIZE:
        case _SC_PAGE_SIZE:
            return static_cast<long>(getauxval(AT_PAGESZ)); //sys/auxv.h

 

第五部分、RSS、二进制格式、架构上下文

rss_stat
RSS 分类统计结构(匿名页、文件页、swap 等分项)。它只有一个 count[] 成员,每种页类型占用一个元素。


binfmt
当前进程的镜像使用的二进制格式处理器(例如 ELF)。

context
体系结构相关的 mm 上下文(ASID、硬件上下文等)。

flags
mm 级标志位集合。备注要求用原子位操作接口访问该成员。

core_state
coredump 协调状态,多线程转储时用于同步。


第六部分、异步 IO、memcg、命名空间与可执行文件

ioctx_lock
保护 AIO 上下文表的锁。


ioctx_table
AIO 上下文表(RCU 保护访问)。


owner
该 mm 的“规范拥有者”任务指针(RCU 指针)。memcg 记账、归属迁移场景需要严格条件下更新。


user_ns
该地址空间关联的用户命名空间。


exe_file
指向可执行文件对象的 RCU 指针,对应 /proc/PID/exe。xxd /proc/1/exe 查看可执行文件的二进制数据。

只编译一个打印helloworld程序实现,一个很小的测试程序这个dump出来的数据量还挺大,942KB, 其中所有字符串是被放在一起的。


mmu_notifier_mm
依赖 CONFIG_MMU_NOTIFIER, 默认不使能。用于 MMU notifier 订阅集合,给 KVM/设备驱动同步页表变更用。


第七部分、THP/NUMA/TLB 批处理与探针

pmd_huge_pte
依赖 (CONFIG_TRANSPARENT_HUGEPAGE 且 !USE_SPLIT_PMD_PTLOCKS)。THP 某些路径下的 PMD 级 huge pte 辅助指针。注释指明了由 page_table_lock 保护。


numa_next_scan
依赖 CONFIG_NUMA_BALANCING, 默认不使能。用于下一次 NUMA hint 扫描时间点。


numa_scan_offset
依赖 CONFIG_NUMA_BALANCING, 默认不使能。用于 NUMA 扫描游标偏移。


numa_scan_seq
依赖 CONFIG_NUMA_BALANCING, 默认不使能。用于扫描序列号,避免并发线程重复做同一轮标记。


tlb_flush_pending
当前 mm 是否有待完成的 TLB flush 批处理计数。页表修改与延后 flush 协同时的关键并发状态。


tlb_flush_batched
依赖 CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH, 默认不使能。用于架构支持下的批量 TLB flush 状态标记。


uprobes_state
用户态探针 uprobes 在该 mm 上的状态管理。


hugetlb_usage
依赖 CONFIG_HUGETLB_PAGE, 默认不使能,hugetlb 页使用量统计。


async_put_work
mm 异步释放相关工作项,避免在不合适上下文直接做重释放路径。


第八部分、动态尾部位图

cpu_bitmap[]
柔性数组,表示该 mm 关联 CPU 掩码(mm_cpumask)。必须放在结构体尾部,大小按 nr_cpu_ids 动态决定。典型用途:TLB shootdown、跨 CPU 地址空间活动跟踪。

 

2. 注意事项

(1) 查 VMA 常走 mmap_sem 读锁,改布局走写锁。页表细粒度改动常配合 page_table_lock 与 TLB flush 协议。生命周期一定区分 mm_users 和 mm_count:一个是使用者计数,一个是对象生存计数。

 

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

导航