这篇文章给大家介绍如何理解Linux进程的内存管理,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。
一个进程的虚拟地址空间主要由两个数据结来描述,一个是 mm_struct,一个是 vm_area_structs。
mm_struct结构描述了一个进程的整个虚拟地址空间,vm_area_truct描述了虚拟地址空间的一个区间(简称虚拟区)。下图就是我们所说的由task_struct到mm_struct,进程的地址空间的分布。
每一个进程都会有自己独立的mm_struct,这样每一个进程都会有自己独立的地址空间,这样才能互不干扰。当进程之间的地址空间被共享的时候,我们可以理解为这个时候是多个进程使用一份地址空间,这就是线程。
struct mm_struct { struct vm_area_struct *mmap; //指向虚拟区间(VMA)链表 struct rb_root mm_rb; //指向red_black树 struct vm_area_struct *mmap_cache; //找到最近的虚拟区间 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; //拥有该结构体的进程的虚拟地址空间的大小 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; //虚拟区间的个数 struct rw_semaphore mmap_sem; spinlock_t page_table_lock; //保护任务页表和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 ,结束end_code,数据段的开始start_data,结束end_data unsigned long start_brk,brk,start_stack; //start_brk和brk记录有关堆的信息,start_brk是用户虚拟地址空间初始化,brk是当前堆的结束地址,start_stack是栈的起始地址 unsigned long arg_start,arg_end,env_start,env_end; //参数段的开始arg_start,结束arg_end,环境段的开始env_start,结束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; }
分配的每个虚拟内存区域都由一个vm_area_struct 数据结构来管理,包括虚拟内存的起始和结束地址,以及内存的访问权限等,通常命名为vma;vm_area_struct 数据结构的定义如下:
struct vm_area_struct { /* The first cache line has the info for VMA tree walking. 第一个缓存行具有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 每个任务的VM区域的链接列表,按地址排序*/ struct vm_area_struct *vm_next, *vm_prev; struct rb_node vm_rb; /* 此VMA左侧最大的可用内存间隙(以字节为单位)。 在此VMA和vma-> vm_prev之间, 或者在VMA rbtree中我们下面的一个VMA与其->vm_prev之间。 这有助于get_unmapped_area找到合适大小的空闲区域。 */ unsigned long rb_subtree_gap; /* Second cache line starts here. 第二个缓存行从这里开始*/ struct mm_struct *vm_mm; /* 我们所属的address space*/ pgprot_t vm_page_prot; /* 此VMA的访问权限 */ unsigned long vm_flags; /* Flags, see mm.h. */ /* 对于具有地址空间(address apace)和后备存储(backing store)的区域, 链接到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; /* 在其中一个文件页面的COW之后,文件的MAP_PRIVATE vma可以在i_mmap树和anon_vma列表中。 MAP_SHARED vma只能位于i_mmap树中。 匿名MAP_PRIVATE,堆栈或brk 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序列化*/ /* 用于处理此结构体的函数指针 */ 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(共享内存) */ #ifndef CONFIG_MMU struct vm_region *vm_region; /* NOMMU映射区域 */ #endif #ifdef CONFIG_NUMA struct mempolicy *vm_policy; /* 针对VMA的NUMA政策 */ #endif };
insmod test.ko pid_mem=3253 显示各个vma区域
cat /proc/3253/maps 显示各个vma区域
看下两种方式的对比:
关于如何理解Linux进程的内存管理就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。