淺析Linux進(jìn)程的內(nèi)存管理
本文轉(zhuǎn)載自微信公眾號「人人都是極客」,作者布道師Peter。轉(zhuǎn)載本文請聯(lián)系人人都是極客公眾號。
幾個(gè)關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)
一個(gè)進(jìn)程的虛擬地址空間主要由兩個(gè)數(shù)據(jù)結(jié)來描述,一個(gè)是 mm_struct,一個(gè)是 vm_area_structs。
mm_struct結(jié)構(gòu)描述了一個(gè)進(jìn)程的整個(gè)虛擬地址空間,vm_area_truct描述了虛擬地址空間的一個(gè)區(qū)間(簡稱虛擬區(qū))。下圖就是我們所說的由task_struct到mm_struct,進(jìn)程的地址空間的分布。
每一個(gè)進(jìn)程都會有自己獨(dú)立的mm_struct,這樣每一個(gè)進(jìn)程都會有自己獨(dú)立的地址空間,這樣才能互不干擾。當(dāng)進(jìn)程之間的地址空間被共享的時(shí)候,我們可以理解為這個(gè)時(shí)候是多個(gè)進(jìn)程使用一份地址空間,這就是線程。
- struct mm_struct
 - {
 - struct vm_area_struct *mmap; //指向虛擬區(qū)間(VMA)鏈表
 - struct rb_root mm_rb; //指向red_black樹
 - struct vm_area_struct *mmap_cache; //找到最近的虛擬區(qū)間
 - unsigned long(*get_unmapped_area)(struct file *filp,unsigned long addr,unsigned long len,unsigned long pgoof,unsigned long flags);
 - void (*unmap_area)(struct mm_struct *mm,unsigned long addr);
 - unsigned long mmap_base;
 - unsigned long task_size; //擁有該結(jié)構(gòu)體的進(jìn)程的虛擬地址空間的大小
 - unsigned long cached_hole_size;
 - unsigned long free_area_cache;
 - pgd_t *pgd; //指向頁全局目錄
 - atomic_t mm_users; //用戶空間中有多少用戶
 - atomic_t mm_count; //對"struct mm_struct"有多少引用
 - int map_count; //虛擬區(qū)間的個(gè)數(shù)
 - struct rw_semaphore mmap_sem;
 - spinlock_t page_table_lock; //保護(hù)任務(wù)頁表和mm->rss
 - struct list_head mmlist; //所有活動mm的鏈表
 - mm_counter_t _file_rss;
 - mm_counter_t _anon_rss;
 - unsigned long hiwter_rss;
 - unsigned long hiwater_vm;
 - unsigned long total_vm,locked_vm,shared_vm,exec_vm;
 - usingned long stack_vm,reserved_vm,def_flags,nr_ptes;
 - unsingned long start_code,end_code,start_data,end_data; //代碼段的開始start_code ,結(jié)束end_code,數(shù)據(jù)段的開始start_data,結(jié)束end_data
 - unsigned long start_brk,brk,start_stack; //start_brk和brk記錄有關(guān)堆的信息,start_brk是用戶虛擬地址空間初始化,brk是當(dāng)前堆的結(jié)束地址,start_stack是棧的起始地址
 - unsigned long arg_start,arg_end,env_start,env_end; //參數(shù)段的開始arg_start,結(jié)束arg_end,環(huán)境段的開始env_start,結(jié)束env_end
 - unsigned long saved_auxv[AT_VECTOR_SIZE];
 - struct linux_binfmt *binfmt;
 - cpumask_t cpu_vm_mask;
 - mm_counter_t context;
 - unsigned int faultstamp;
 - unsigned int token_priority;
 - unsigned int last_interval;
 - unsigned long flags;
 - struct core_state *core_state;
 - }
 
分配的每個(gè)虛擬內(nèi)存區(qū)域都由一個(gè)vm_area_struct 數(shù)據(jù)結(jié)構(gòu)來管理,包括虛擬內(nèi)存的起始和結(jié)束地址,以及內(nèi)存的訪問權(quán)限等,通常命名為vma;vm_area_struct 數(shù)據(jù)結(jié)構(gòu)的定義如下:
- struct vm_area_struct {
 - /* The first cache line has the info for VMA tree walking.
 - 第一個(gè)緩存行具有VMA樹移動的信息*/
 - unsigned long vm_start; /* Our start address within vm_mm. */
 - unsigned long vm_end; /* The first byte after our end address within vm_mm. */
 - /* linked list of VM areas per task, sorted by address
 - 每個(gè)任務(wù)的VM區(qū)域的鏈接列表,按地址排序*/
 - struct vm_area_struct *vm_next, *vm_prev;
 - struct rb_node vm_rb;
 - /*
 - 此VMA左側(cè)最大的可用內(nèi)存間隙(以字節(jié)為單位)。
 - 在此VMA和vma-> vm_prev之間,
 - 或者在VMA rbtree中我們下面的一個(gè)VMA與其->vm_prev之間。
 - 這有助于get_unmapped_area找到合適大小的空閑區(qū)域。
 - */
 - unsigned long rb_subtree_gap;
 - /* Second cache line starts here.
 - 第二個(gè)緩存行從這里開始*/
 - struct mm_struct *vm_mm; /* 我們所屬的address space*/
 - pgprot_t vm_page_prot; /* 此VMA的訪問權(quán)限 */
 - unsigned long vm_flags; /* Flags, see mm.h. */
 - /*
 - 對于具有地址空間(address apace)和后備存儲(backing store)的區(qū)域,
 - 鏈接到address_space->i_mmap間隔樹,或者鏈接到address_space-> i_mmap_nonlinear列表中的vma。
 - */
 - union {
 - struct {
 - struct rb_node rb;
 - unsigned long rb_subtree_last;
 - } linear;
 - struct list_head nonlinear;
 - } shared;
 - /*
 - 在其中一個(gè)文件頁面的COW之后,文件的MAP_PRIVATE vma可以在i_mmap樹和anon_vma列表中。
 - MAP_SHARED vma只能位于i_mmap樹中。
 - 匿名MAP_PRIVATE,堆?;騜rk vma(帶有NULL文件)只能位于anon_vma列表中。
 - */
 - struct list_head anon_vma_chain; /* Serialized by mmap_sem & * page_table_lock
 - 由mmap_sem和* page_table_lock序列化*/
 - struct anon_vma *anon_vma; /* Serialized by page_table_lock 由page_table_lock序列化*/
 - /* 用于處理此結(jié)構(gòu)體的函數(shù)指針 */
 - const struct vm_operations_struct *vm_ops;
 - /* 后備存儲(backing store)的信息: */
 - unsigned long vm_pgoff; /* 以PAGE_SIZE為單位的偏移量(在vm_file中),*不是* PAGE_CACHE_SIZE*/
 - struct file * vm_file; /* 我們映射到文件(可以為NULL)*/
 - void * vm_private_data; /* 是vm_pte(共享內(nèi)存) */
 - #ifndef CONFIG_MMU
 - struct vm_region *vm_region; /* NOMMU映射區(qū)域 */
 - #endif
 - #ifdef CONFIG_NUMA
 - struct mempolicy *vm_policy; /* 針對VMA的NUMA政策 */
 - #endif
 - };
 
小實(shí)驗(yàn)
- insmod test.ko pid_mem=3253 顯示各個(gè)vma區(qū)域
 
- cat /proc/3253/maps 顯示各個(gè)vma區(qū)域
 
看下兩種方式的對比:




















 
 
 






 
 
 
 