成员类型 | 成员名 | 功能说明(内核选项) |
struct maple_tree | mm_mt | Oracle 开发的 Maple Tree 数据结构,作为虚拟内存地址的基于范围的重叠树,可以提供一些效率改进。 |
unsigned long (*get_unmapped_area) (struct file *filp,unsigned long addr, unsigned long len,unsigned long pgoff, unsigned long flags) | 获取进程用户空间中未被映射的内存区域 (CONFIG_MMU) | |
unsigned long | mmap_base | 新式布局下,内存映射区(mmap)的起始地址 |
unsigned long | mmap_legacy_base | 经典布局下,内存映射区(mmap)的起始地址(task_size/3,参考从内核世界透视 mmap 内存映射的本质(源码实现篇)-鸿蒙开发者社区-51CTO.COM) |
unsigned long | mmap_compat_base | 新式布局下,兼容性内存映射(mmap)的起始地址( CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES,64位应用调用32位mmap系统调用,或者32位应用调用64位mmap系统调用) |
unsigned long | mmap_compat_legacy_base | 经典布局下,兼容性内存映射(mmap)的起始地址( CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES) |
unsigned long | task_size | 进程虚拟地址空间总大小(task_struct->mm_struct) |
pgd_t * | pgd | 页全局目录(page global directory),mm_init过程中分配 |
atomic_t | membarrier_state | 控制内存屏障行为的标志(CONFIG_MEMBARRIER) |
atomic_t | mm_users | 共享该内存描述符的用户计数(LWP) |
atomic_t | mm_count | 正在使用该内存描述符的进程数量,为零时,内存描述符会被释放。 |
atomic_long_t | pgtables_bytes | 页表(占用的内存)大小(CONFIG_MMU) |
int | map_count | 内存映射数(VMA) |
spinlock_t | page_table_lock | 保护页表的自旋锁 |
struct rw_semaphore | mmap_lock | 保护内存映射的读写信号量 |
struct list_head | mmlist | 可能会被交换的内存链(的挂接点),自旋锁mmlist_lock保护该 |
unsigned long | hiwater_rss | 进程的最大页面数(RSS,Resident Set Size的缩写,指的是一个进程所占用的物理内存的大小;即RSS表示了一个进程正在使用的实际内存量) |
unsigned long | hiwater_vm | 进程的线性区最大页数 |
unsigned long | total_vm | 进程映射的页面总数 |
unsigned long | locked_vm | 被锁定的页面数(不能换出) |
atomic64_t | pinned_vm | 被钉住的页面数(不能换出也不能移动) |
unsigned long | data_vm | 数据段映射的页面数 |
unsigned long | exec_vm | 存放可执行文件的页面数 |
unsigned long | stack_vm | 栈映射的页面数 |
unsigned long | def_flags | 进程默认的vm_flags标志 |
seqcount_t | write_protect_seq | 页表项的写保护计数 |
spinlock_t | arg_lock | 保护启动相关信息的自旋锁: start_code、end_code、start_data、end_data, |
unsigned long | start_code | 代码段起始地址 |
unsigned long | end_code | 代码段结束地址 |
unsigned long | start_data | 数据段起始地址 |
unsigned long | end_data | 数据段结束地址 |
unsigned long | start_brk | 堆的起始地址 |
unsigned long | brk | 堆的结束地址,在分配和释放堆内存时会重新定位。 |
unsigned long | start_stack | 栈的起始地址。 |
unsigned long | arg_start | 命令行参数链表的起始地址 |
unsigned long | arg_end | 命令行参数链表的结束地址 |
unsigned long | env_start | 环境变量的起始地址 |
unsigned long | env_end | 环境变量的结束地址 |
unsigned long | saved_auxv[AT_VECTOR_SIZE]; | 保存进程镜像文件的一些信息(待确认) |
struct mm_rss_stat | rss_stat | 特殊计数,包括文件映射页面数、匿名映射页面数、匿名交换页面数和共享内存页面数 |
struct linux_binfmt * | binfmt | 镜像(二进制)文件格式信息,比如elf |
mm_context_t | context | 架构相关的内存管理上下文。以arm64架构为例: 引入ASID版本号,避免每次进程切换都需要清空页表缓存,只需要在硬件ASID回绕时把处理器的页表缓存清空。 |
unsigned long | flags | 内存标志,参考<ched/coredump.h> |
spinlock_t | ioctx_lock | 保护IO上下文表的自旋锁(CONFIG_AIO:POSIX 异步IO) |
struct kioctx_table __rcu* | ioctx_table | IO上下文表(CONFIG_AIO) |
struct task_struct __rcu * | owner | 该内存描述符的所有者(CONFIG_MEMCG,控制组内任务(task)内存控制) |
struct user_namespace * | user_ns | 用户户命名空间,保存限制资源使用的信息 |
struct file __rcu * | exe_file | /proc/<pid>/exe 符号链接文件描述符 |
struct mmu_notifier_subscriptions * | notifier_subscriptions | 内存管理单元通知链订阅(CONFIG_MMU_NOTIFIER) |
pgtable_t | pmd_huge_pte | 大页面相关的字段,具体用途待确认。由page_table_lock 锁保护(CONFIG_TRANSPARENT_HUGEPAGE && !USE_SPLIT_PMD_PTLOCKS) |
unsigned long | numa_next_scan | 下次扫描地址空间的时间(CONFIG_NUMA_BALANCING,非一致性内存访问平衡) |
unsigned long | numa_scan_offset | 下次扫描的起始位置,采用顺序循环的方式扫描(CONFIG_NUMA_BALANCING) |
int | numa_scan_seq | 扫描序列号,防止相同进程下的多个线程同时扫描地址空间(CONFIG_NUMA_BALANCING) |
atomic_t | tlb_flush_pending | 只要移动处理器内存,都需要flush TLB。在批量flush TLB前,增加计数 |
atomic_t | tlb_flush_batched | 调用flush_tlb_batched_pending函数,刷新由于并行回收竞争留下的TLB条目( CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH) |
struct uprobes_state | uprobes_state | uprobe是linux内核提供的一种trace用户态函数的机制。可以在不对二进制重新编译的情况下进行trace特定函数 |
struct rcu_head | delayed_drop | 延迟释放内存(CONFIG_PREEMPT_RT) |
atomic_long_t | hugetlb_usage | 大内存页使用计数(CONFIG_HUGETLB_PAGE) |
struct work_struct | async_put_work | 异步内存释放工作队列(mmput,mm_users减1) |
u32 | pasid | 进程地址空间标识符(CONFIG_IOMMU_SVA) |
unsigned long | ksm_merging_pages | KSM合并的页面数量(CONFIG_KSM,kernel same-page merging,内核同页合并) |
unsigned long | ksm_rmap_items | KSM虚拟映射的数量(CONFIG_KSM) |
struct list_head | list | (CONFIG_LRU_GEN,MULTI-GEN LRU(Least Recently Used),内存回收算法) |
unsigned long | bitmap | CONFIG_LRU_GEN |
struct mem_cgroup * | memcg | CONFIG_LRU_GEN && CONFIG_MEMCG |
unsigned long | cpu_bitmap[] | 必须放在结构体最后,因为其大小会随着nr_cpu_ids变化(即动态数组),记录当前内存节点下的处理器位图 |
1. maple_tree
maple tree属于 B树类型,就是说每个节点可以包含两个及以上的元素,叶子节点最多包含 16 个元素,中间节点最多包含 10个元素。每个 节点最多需要 256 字节,基本是处理器缓存行的整数倍。节点中元素数量的增加,和节点大小缓存行对齐,可以减少遍历过程中的cache-miss。
maple tree 是一个遵循read-copy-update (RCU) 方式的通用结构,可以用在内核的不同子系统中。第一个内核用户是内存子系统,替换 VMA 管理中用到的红黑树和双向链表。
2. MMU notifier
MMU notifier是linux内核内存管理的一个机制,用于处理MMU相关的事件通知。通常被用来实现页表刷新和更新等操作,确保进程能够正确地访问内存。该机制在内核态和用户态之间建立了一个通信的机制,保证不同的进程能够共享内存,内核能够及时通知进程内存访问权限的变化,提高系统的稳定性和安全性。
1. 注册通知回调函数
进程可以注册一个通知回调函数,以便监听内存访问权限的变化。该回调函数通常会在页表刷新或更新时调用。通过该回调函数,进程能够及时响应内核态的事件变化。
2. 事件通知的触发
当内核发生与内存管理相关的事件时,会调用进程注册的回调函数。
3. 处理事件通知
一旦回调函数被调用,进程就可以开始处理事件。根据事件类型,进程可以做出相应的处理,
确保内存的一致性和正确性,防止出现内存访问错误。
3. numa balance
周期性扫描进程地址空间,并且修改页表项为PAGE_NONE,从而触发page fault。在page fault处理过程中,重新修改页表为正确的权限使得后面能够继续执行。在page fault处理过程总,重点检查page被哪个节点和任务访问过,任务在各个节点上发生的缺页情况,并据此决定是否迁移页面。