内存管理-6-虚拟内存相关结构体

基于msm-5.4

一、struct mm_struct

1. 简介

内嵌在 task_struct 结构中,表示一个进程虚拟地址空间。

2. 成员介绍

//include/linux/mm_types.h
struct mm_struct {
    struct {
        struct vm_area_struct *mmap; /* list of VMAs */
        struct rb_root mm_rb;
        u64 vmacache_seqnum; /* per-thread vmacache */
        unsigned long (*get_unmapped_area) (struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags);
        unsigned long mmap_base;    /* base of mmap area */
        unsigned long mmap_legacy_base;    /* base of mmap area in bottom-up allocations */
        unsigned long task_size;    /* size of task vm space */
        unsigned long highest_vm_end;    /* highest vma end address */
        pgd_t * pgd;
        atomic_t membarrier_state; //CONFIG_MEMBARRIER
        atomic_t mm_users;
        atomic_t mm_count;
        atomic_long_t pgtables_bytes; /* PTE page table pages */
        int map_count; /* number of VMAs */
        spinlock_t page_table_lock; /* Protects page tables and some counters */
        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]; /* 46 for /proc/PID/auxv */
        struct mm_rss_stat rss_stat;
        struct linux_binfmt *binfmt;
        mm_context_t context; /* Architecture-specific MM 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
        struct task_struct __rcu *owner; //CONFIG_MEMCG
        struct user_namespace *user_ns;
        struct file __rcu *exe_file; /* store ref to file /proc/<pid>/exe symlink points to */
        atomic_t tlb_flush_pending;
        struct uprobes_state uprobes_state;
        struct work_struct async_put_work;

    } __randomize_layout;
    unsigned long cpu_bitmap[];
};

get_unmapped_area: 用于在此进程虚拟地址空间中查找一块未映射的虚拟内存的回调。

mmap: 执向任务VMA链表的链表头。

mm_rb: 执向任务VMA红黑树的根节点。

map_count: 任务VMA的总个数。

total_vm: 进程地址空间总大小. TODO: 内存管理上是如何区分进程和线程的?

start_stack: 指向栈顶的虚拟地址,栈是向下生长的。

mmap_base: mmap段起始虚拟地址,向下生长。

start_brk/brk: 指向堆的起始和结束虚拟地址,堆是向上生长的。

start_code/end_code: 代码段的起始和结束虚拟地址。

start_data/end_data: 数据段的起始和结束地址。其中 end_data-->start_brk之前是BSS段的空间。

membarrier_state: 控制 membarrier 行为的标志。此字段接近 @pgd,希望适合相同的缓存行,这需要由 switch_mm() 触及。

mm_users: 用户的数量,包括用户空间。使用 mmget()/mmget_not_zero()/mmput() 进行修改。当该值降至 0 时(即当任务退出且没有其他临时引用持有者时),我们还会释放对@mm_count 的引用(如果 @mm_count 也降至 0,则可能会释放 &struct mm_struct)。

mm_count:对 &struct mm_struct 的引用数(@mm_users 计为 1)。使用 mmgrab()/mmdrop() 进行修改。当此值降至 0 时,&struct mm_struct 将被释放。

mm_list: 可能交换(swapped )的 mm 列表。这些列表全局地串联在 init_mm.mmlist 上,并受 mmlist_lock 保护。

rss_stat: 特殊计数器在某些配置中受 page_table_lock 保护,在其他配置中则具有原子性。

owner: 指向被视为此 mm 的规范用户/所有者的任务。必须满足以下所有条件才能更改它:current == mm->owner; current->mm != mm; new_owner->mm == mm; new_owner->alloc_lock is held;

tlb_flush_pending: 正在进行批量 TLB 刷新操作。任何可以移动进程内存的操作在移动 PROT_NONE 或 PROT_NUMA 映射页面时都需要刷新 TLB

cpu_bitmap[]: mm_cpumask 需要位于 mm_struct 的末尾,因为它是根据 nr_cpu_ids 动态调整大小的。

 

二、struct vm_area_struct

1. 简介

此结构用来描述一个 VMM 内存区域。每个 VM 区域/任务都有一个这样的区域。VM 区域是进程虚拟内存空间的一部分,它对page-fault处理程序有特殊规则(即共享库、可执行区域等)。

无论是加载一个动态链接库,还是通过mmap创建映射,都需要在进程地址空间中增加一个新的vma结构。具体过程是首先通过 get_unmapped_area()找到虚拟地址空间中一块空闲且大小满足要求的区域,分配给新vma并设置其flag属性,返回该vma起始处的虚拟地址。注意分配的vma只是这段虚拟地址的使用权,而不是物理地址的使用权。

2. 成员介绍

//include/linux/mm_types.h
struct vm_area_struct {
    /* The first cache line has the info for VMA tree walking. */
    unsigned long vm_start; /* Our start address within vm_mm. */
    unsigned long vm_end;   /* The first byte after our end address within vm_mm. */
    struct vm_area_struct *vm_next, *vm_prev;
    struct rb_node vm_rb;
    unsigned long rb_subtree_gap;

    /* Second cache line starts here. after 64B */
    struct mm_struct *vm_mm; /* The address space we belong to. */
    pgprot_t vm_page_prot;   /* Access permissions of this VMA. */
    unsigned long vm_flags;     /* Flags, see mm.h. */

    union {
        struct {
            struct rb_node rb;
            unsigned long rb_subtree_last;
        } shared;
        const char __user *anon_name;
    };

    struct list_head anon_vma_chain; /* Serialized by mmap_sem & page_table_lock */
    struct anon_vma *anon_vma; /* Serialized by page_table_lock */
    const struct vm_operations_struct *vm_ops;
    unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE units */
    struct file * vm_file;  /* File we map to (can be NULL). */
    void * vm_private_data;    /* was vm_pte (shared mem) */
    atomic_long_t swap_readahead_info; //CONFIG_SWAP
    struct vm_region *vm_region; //CONFIG_MMU
    struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
} __randomize_layout;

vm_start: 指向此区域起始虚拟地址

vm_end: 指向此区域结束虚拟地址

vm_page_prot: 描述此区域所有页面的读写权限

vm_flags: 描述此区域是私有的还是共享的等,见mm.h, VM_IO: 此区域映射的是设备IO地址;VM_LOCKED: 此页被锁住了不能交换出去;

vm_next/vm_prev: 任务的所有 VM 区域构成的链表,按地址排序。

rb_subtree_gap: 此 VMA 左侧的最大可用内存间隙(以字节为单位)。此 VMA 与 vma->vm_prev 之间,或 VMA rbtree 中我们下方的 VMA 之一与其 ->vm_prev 之间。这有助于get_unmapped_area 找到合适大小的可用区域。

shared.rb/shared.rb_subtree_last: 对于具有地址空间和后备存储的区域,链接到 address_space->i_mmap 间隔树。
anon_name: 对于私有匿名映射,指向用户进程中以空字符结尾的字符串的指针,该字符串包含赋予 vma 的名称,如果未命名,则为 NULL。

anon_vma_chain: 在文件页面之一的 COW 之后,文件的 MAP_PRIVATE vma 可以同时位于 i_mmap 树和 anon_vma list 中。MAP_SHARED vma 只能位于 i_mmap 树中。匿名 MAP_PRIVATE、堆栈或 brk vma(带有 NULL 文件)只能位于 anon_vma 列表中。

vm_ops: 用于处理此结构的函数指针。

vm_pgoff: 有关后备存储的信息


三、struct vm_operations_struct

1. 简介

VMA的操作函数集。这些是虚拟 MM 函数 - 打开、关闭和取消映射一个区域(需要使磁盘上的文件保持最新等)、指向发生 no-page 或 wp-page 异常时调用的函数。

//include/linux/mm.h
struct vm_operations_struct {
    void (*open)(struct vm_area_struct * area);
    void (*close)(struct vm_area_struct * area);
    int (*split)(struct vm_area_struct * area, unsigned long addr);
    int (*mremap)(struct vm_area_struct * area);
    vm_fault_t (*fault)(struct vm_fault *vmf);
    vm_fault_t (*huge_fault)(struct vm_fault *vmf, enum page_entry_size pe_size);
    void (*map_pages)(struct vm_fault *vmf, pgoff_t start_pgoff, pgoff_t end_pgoff);
    unsigned long (*pagesize)(struct vm_area_struct * area);
    vm_fault_t (*page_mkwrite)(struct vm_fault *vmf);
    vm_fault_t (*pfn_mkwrite)(struct vm_fault *vmf);
    int (*access)(struct vm_area_struct *vma, unsigned long addr, void *buf, int len, int write);
    const char *(*name)(struct vm_area_struct *vma);
    struct page *(*find_special_page)(struct vm_area_struct *vma, unsigned long addr);
};

2. 成员介绍

page_mkwrite: 通知以前只读的页面即将变为可写,如果返回错误,则会导致 SIGBUS.

pfn_mkwrite: 当使用 VM_PFNMAP|VM_MIXEDMAP 时与 page_mkwrite 相同

access: 当 get_user_pages() 失败时由 access_process_vm 调用,通常由可以在内存和硬件之间切换的特殊 VMA 使用.

name: 由 /proc/PID/maps 代码调用,询问 vma 是否具有特殊名称。返回非 NULL 也将导致此 vma 被无条件dumped。

find_special_page: 由 vm_normal_page() 调用,用于特殊 PTE 查找 @addr 的页面。如果默认行为(使用 pte_page())找不到正确的页面,则此功能很有用。


四、struct vmacache

1. 简介

内嵌在 task_struct 结构中。保存了上一次找到的vma,根据局部性原理,下一次要用到的vma正好是上次使用的vma的可能性是比较大的,因此使用 find_vma() 函数查找vma时,会首先从 mmap_cache 中找,找到了就直接返回。

参考:Linux中的VMA cache: https://www.zhihu.com/tardis/bd/art/99124666?source_id=1001

2. 成员介绍

//include/linux/mm_types_task.h
struct vmacache {
    u64 seqnum;
    struct vm_area_struct *vmas[VMACACHE_SIZE]; //4
};

vma[]: 当一个地址最近被访问到,就会抽取该地址的PTE中的最低2个bits作为此数组的下标,然后将该地址所在的VMA的 vm_area_struct 指针填入这个下标对应的数组元素中。见 vmacache_update()


五、struct task_rss_stat

1. 简介

内嵌在 task_struct 结构中。记录每线程缓存的信息。

2. 成员介绍

//include/linux/mm_types_task.h
struct task_rss_stat {
    int events;    /* for synchronization threshold */
    int count[NR_MM_COUNTERS]; //5
};

count[]: 有5个元素,分别记录文件页、匿名页、交换页、共享内存页、不可回收页的数量。


六、struct reclaim_state

1. 简介

内嵌在 task_struct 结构中。当任务正在运行内存回收时,current->reclaim_state 指向其中一个.

2. 成员介绍

//include/linux/swap.h 
struct reclaim_state {
    unsigned long reclaimed_slab;
};


七、struct vm_struct

1. 简介

此结构内嵌在 task_struct 结构中。管理着虚拟地址和物理页之间的映射关系。

2. 成员介绍

//include/linux/vmalloc.h
struct vm_struct {
    struct vm_struct    *next;
    void            *addr;
    unsigned long    size;
    unsigned long    flags;
    struct page        **pages;
    unsigned int    nr_pages;
    phys_addr_t        phys_addr;
    const void        *caller;
};

next: 指向下一个 vm_struct 结构体.

addr: 当前vmalloc区域的虚拟地址的起始地址.

size: 当前vmalloc区域的虚拟地址的大小.

pages: vamlloc分配获取的各个物理页面并是不连续的,pages是一个数组,保存此结构管理的所有物理页。

nr_pages: vmalloc映射的page数目。

phys_addr: 用来映射硬件设备的IO共享内存,其他情况下为0。

caller: 调用vmalloc函数的函数的地址


八、struct vmap_area

1. 简介

vmap_area 描述一段虚拟地址的区域,可以将struct vm_struct构成一个链表,维护多段映射。

2. 成员介绍

//include/linux/vmalloc.h
struct vmap_area {
    unsigned long va_start;
    unsigned long va_end;

    struct rb_node rb_node; /* address sorted rbtree */
    struct list_head list;  /* address sorted list */

    union {
        unsigned long subtree_max_size; /* in "free" tree */
        struct vm_struct *vm;           /* in "busy" tree */
        struct llist_node purge_list;   /* in purge list */
    };
};

va_start: vmalloc申请虚拟地址返回的起始地址.

va_end: vmalloc申请申请虚拟地址返回的结束地址.

rb_node: 挂接到 vmap_area_root 红黑树.

list: 挂接到 vmap_area_list 链表.

union: 这三个变量可以打包,因为 vmap_area 对象始终处于以下三种状态之一:1) 在“空闲”树中(根是 vmap_area_root),2) 在“繁忙”树中(根是 free_vmap_area_root),3) 在清除列表中(头部是 vmap_purge_list)

 

posted on 2024-06-19 16:15  Hello-World3  阅读(6)  评论(0编辑  收藏  举报

导航