本文转载自微信公众号《人人都是极客》,作者为布道师Peter。转载本文请联系大家是极客公众号。几个关键数据结构进程的虚拟地址空间主要由两个数据节点描述,一个是mm_struct,一个是vm_area_structs。mm_struct结构描述了进程的整个虚拟地址空间,vm_area_truct描述了虚拟地址空间(简称virtualarea)的一个区间。下图就是我们所说的进程的地址空间从task_struct到mm_struct的分布情况。每个进程都会有自己独立的mm_struct,这样每个进程都会有自己独立的地址空间,这样就不会互相干扰了。当进程间共享地址空间时,我们可以理解为,此时多个进程使用一个地址空间,即为一个线程。structmm_struct{structvm_area_struct*mmap;//指向虚拟区域(VMA)链表structrb_rootmm_rb;//指向red_black树structvm_area_struct*mmap_cache;//寻找最近的虚拟区域unsignedlong(*get_unmapped_area)(structfile*filp,unsignedlongaddr,unsignedlongsign,p);void(*unmap_area)(structmm_struct*mm,unsignedlongaddr);unsignedlongmmap_base;unsignedlongtask_size;//拥有该结构的进程的虚拟地址空间大小unsignedlongcached_hole_size;unsignedlongfree_area_cache;pgd_t*pgd;//指向pageglobaldirectoryatomic;/tmm_/userspace中有多少用户atomic_tmm_count;//对“structmm_struct”的引用有多少intmap_count;//虚拟区??间数structrw_semaphoremmap_sem;spinlock_tpage_table_lock;//保护任务页表和mm->rssstructlist_headmmlist;//所有活跃mm的链表mm_counter_t_file_rss;mm_counter_t_anon_rss;unsignedlonghiwter_rss;unsignedlonghiwater_vm;unsignedlongtotal_vm,locked_vm,shared_vm,exec_vm;usingnedlongstack_vm,reserved_vm,def_flags,nr_ptes;unsingnedlongstart_code,end_code,start_data,end_data;//代码段的开始start_code,结束end_code,数据段的开始start_data,结束end_dataunsignedlongstart_brk,brk,start_stack;//start_brk和brk记录堆的信息,start_brk是用户虚拟地址空间初始化,brk是当前堆的结束地址,start_stack是栈的起始地址unsignedlongarg_start,arg_end,env_start,env_end;//参数段的开始arg_start,结束arg_end,环境段的开始env_start,结束env_endunsignedlongsaved_auxv[AT_VECTOR_SIZE];structlinux_binfmt*binfmt;cpumask_tcpu_vm_mask;mm_counter_tcontext;unsignedintfaultstamp;unsignedinttoken_priority;unsignedintlast_interval;unsignedlongflags;structcore_state*core_state;}分配的每个虚拟内存区都由一个vm_area_struct数据结构管理,包括虚拟内存的起始地址和结束地址,以及内存访问权限等,通常命名为vma;vm_area_struct数据结构的定义如下:structvm_area_struct{/*ThefirstcachelinehastheinfoforVMAtreewalking。第一个缓存行有VMATree移动信息*/unsignedlongvm_start;/*Ourstartaddresswithinvm_mm.*/unsignedlongvm_end;/*第一个byteafterourendaddresswithinvm_mm.*//*linkedlistofVMareaspertask,sortedbyaddress每个task的VM区域链表,按地址排序*/structvm_area_structv*vm_buprex,*vtr_m;/*在这个VMA和vma->vm_prev之间,或者在我们下面的一个VMA和VMArbtree中的its->vm_prev之间,这个VMA左边的最大可用内存间隙(以字节为单位)。这有助于get_unmapped_area找到合适大小的空闲区域。*/unsignedlongrb_subtree_gap;/*第二个缓存行从这里开始。第二个缓存行从这里开始*/structmm_struct*vm_mm;/*我们所属的地址空间*/pgprot_tvm_page_prot;/*这个VMA的访问权限*/unsignedlongvm_flags;/*Flags,seemm.h.*//*链接到address_space->i_mmap区间树,或者链接到address_space->i_mmap_nonlinear列表中的vma,用于有地址空间(addressapace)和后备存储(backingstore)的区域。*/union{struct{structrb_noderb;unsignedlongrb_subtree_last;}linear;structlist_headnonlinear;}shared;/*其中一个文件页的COW之后,文件的MAP_PRIVATEvma可以在i_mmap树和anon_vma列表中。MAP_SHAREDvma只能位于i_mmap树中。匿名MAP_PRIVATE、堆栈或brkvma(带有NULL文件)只能在anon_vma列表中。*/structlist_headanon_vma_chain;/*Serializedbymmap_sem&*page_table_lock被mmap_sem和*page_table_lock序列化*/structanon_vma*anon_vma;/*Serializedbypage_table_lock被page_table_lock序列化*//*处理这个结构体的函数指针*/conststructvm_operations_struct;*vm_backingstore(后备存储)信息:*/unsignedlongvm_pgoff;/*以PAGE_SIZE为单位的偏移量(在vm_file中),*不是*PAGE_CACHE_SIZE*/structfile*vm_file;/*我们映射到文件(可以为NULL)*/void*vm_private_data;/*是vm_pte(共享内存)*/#ifndefCONFIG_MMUstructvm_region*vm_region;/*NOMMU映射区域*/#endif#ifdefCONFIG_NUMAstructmempolicy*vm_policy;/*NUMA策略对于VMA*/#endif};小实验insmodtest.kopid_mem=3253显示每个vma区域cat/proc/3253/maps显示每个vma区域看看两种方式的对比:
