内存区域由vm_area_struct结构体描述,内存区域在内核中也被称作虚拟内存区域或VMA。vm_area_struct结构体描述了指定地址空间内连续区间上的一个独立内存范围。内核将每个内存区域作为一个单独的内存对象管理,每个内存区域都有一致的属性和操作:
- 在Mm.h中
- struct vm_area_struct {
- struct mm_struct * vm_mm;
- unsigned long vm_start;
- unsigned long vm_end;
-
- struct vm_area_struct *vm_next;
- pgprot_t vm_page_prot;
- unsigned long vm_flags;
- struct rb_node vm_rb;
-
- union {
- struct {
- struct list_head list;
- void *parent;
- struct vm_area_struct *head;
- } vm_set;
- struct raw_prio_tree_node prio_tree_node;
- } shared;
-
- struct list_head anon_vma_node;
- struct anon_vma *anon_vma;
-
- struct vm_operations_struct * vm_ops;
-
- unsigned long vm_pgoff;
- struct file * vm_file;
- void * vm_private_data;
- unsigned long vm_truncate_count;
- #ifndef CONFIG_MMU
- atomic_t vm_usage;
- #endif
- #ifdef CONFIG_NUMA
- struct mempolicy *vm_policy;
- #endif
- };
每个内存描述符都对应于进程地址空间中的惟一区间。内存区域的位置就在[vm_start,vm_end)之中。注意,在同一个地址空间内的不同内存区间不能重叠。
mv_mm指向与VMA相关的mm_struct结构体。每个VMA对其相关的mm_struct结构体来说都是惟一的,所以即使两个独立的进程将同一个文件映射到各自的地址空间,它们都分别有一个vm_area_struct结构体来标志自己的内存区域;但是如果两个进程共享一个地址空间,那么它们也同时共享其中的所有vm_area_struct结构体。
VMA标志
VMA标志是一种位标志,标志了内存区域所包含的页面的行为和信息。和物理页的访问权限不同,VMA标志反映了内核处理页面所需要遵循的行为准则,而不是硬件要求。该标志同时也包含了内存区域中页面的信息,或内存区域的整体信息:
- 在mm.h中
- #define VM_READ 0x00000001 /* currently active flags */
- #define VM_WRITE 0x00000002
- #define VM_EXEC 0x00000004
- #define VM_SHARED 0x00000008
- #define VM_MAYREAD 0x00000010 /* limits for mprotect() etc */
- #define VM_MAYWRITE 0x00000020
- #define VM_MAYEXEC 0x00000040
- #define VM_MAYSHARE 0x00000080
- #define VM_GROWSDOWN 0x00000100 /* general info on the segment */
- #define VM_GROWSUP 0x00000200
- #define VM_PFNMAP 0x00000400 /* Page-ranges managed without "struct page", just pure PFN */
- #define VM_DENYWRITE 0x00000800 /* ETXTBSY on write attempts.. */
- #define VM_EXECUTABLE 0x00001000
- #define VM_LOCKED 0x00002000
- #define VM_IO 0x00004000 /* Memory mapped I/O or similar */
-
- #define VM_SEQ_READ 0x00008000 /* App will access data sequentially */
- #define VM_RAND_READ 0x00010000 /* App will not benefit from clustered reads */
- #define VM_DONTCOPY 0x00020000 /* Do not copy this vma on fork */
- #define VM_DONTEXPAND 0x00040000 /* Cannot expand with mremap() */
- #define VM_RESERVED 0x00080000 /* Count as reserved_vm like IO */
- #define VM_ACCOUNT 0x00100000 /* Is a VM accounted object */
- #define VM_HUGETLB 0x00400000 /* Huge TLB Page VM */
- #define VM_NONLINEAR 0x00800000 /* Is non-linear (remap_file_pages) */
- #define VM_MAPPED_COPY 0x01000000 /* T if mapped copy of data (nommu mmap) */
- #define VM_INSERTPAGE 0x02000000 /* The vma has had "vm_insert_page()" done on it */
- #define VM_ALWAYSDUMP 0x04000000 /* Always include in core dumps */
vm_area_struct结构体中的vm_ops域指向与指定内存域相关的操作函数表,内核使用表中的方法操作VMA。vm_area_struct为通用对象代表了任何类型的内存区域,而操作表描述针对特定的对象实例的特定方法。
- 在mm.h中
- struct vm_operations_struct {
- void (*open)(struct vm_area_struct * area);
- void (*close)(struct vm_area_struct * area);
- struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int *type);
- unsigned long (*nopfn)(struct vm_area_struct * area, unsigned long address);
- int (*populate)(struct vm_area_struct * area, unsigned long address, unsigned long len, pgprot_t prot, unsigned long pgoff, int nonblock);
-
- int (*page_mkwrite)(struct vm_area_struct *vma, struct page *page);
- #ifdef CONFIG_NUMA
- int (*set_policy)(struct vm_area_struct *vma, struct mempolicy *new);
- struct mempolicy *(*get_policy)(struct vm_area_struct *vma,
- unsigned long addr);
- int (*migrate)(struct vm_area_struct *vma, const nodemask_t *from,
- const nodemask_t *to, unsigned long flags);
- #endif
- };
可以通过内存描述中的mmap和mm_rb域之一访问内存区域,这两个域各自独立地指向域内存描述符相关的全体内存区域对象。其实,它们包含完全相同的vm_area_struct结构体指针,仅仅是组织方法不同。
mmap使用单独的链表连接所有的内存区域对象。每一个vm_area_struct结构体通过自身的vm_next联入链表,所有的区域按地址增长的方向排序,mmap域指向链表中第一个内存区域,链表中最后一个VMA结构体指针指向空。
mm_rb使用红黑树连接所欲哦的内存区域对象。mm_rb指向红黑树的根节点,地址空间中的每一个vm_area_struct结构体通过自身的vm_rb连接到树中。
红黑树中的所有节点都遵从:左边节点值小于右边节点值;每个节点都被配以红色或黑色。分配规则为:红色节点的子结点为黑色,并且数中的任何一条从节点到叶子的路径必须包含同样数目的黑色节点。根节点总为红色。红黑树的搜索、插入、删除等操作的复杂度都为O(log(n))。
链表用于需要遍历全部节点的时候,红黑树适用于在地址空间中定位特定内存区域的时候。内核为了内存区域是上的各种不同操作都能获得高性能,所以使用了这两种数据结构。
使用/proc文件系统和pmap(1)工具可以查看给定进程的内存空间和其中所含的内存区域。
相关内容请参看
Linux 的 Virtual Memory Areas(VMA):基本概念介紹http://lixiang.cn/?q=node/105
最后给出mm_struct和VMA的关系图:
a