内存管理-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) 收藏 举报
浙公网安备 33010602011771号