我们了解了内存在内核态是如何管理的,本篇文章我们一起来看下内存在用户态的使用情况,如果上一篇文章说是内核驱动工程师经常面对的内存管理问题,那本篇就是应用工程师常面对的问题。
相信大家都知道对用户态的内存消耗对象是进程,应用开发者面对的所有代码操作最后的落脚点都是进程,这也是说为什么内存和进程两个知识点的重要性,理解了内存和进程两大法宝,对所有软件开发的理解都会有了全局观(关于进程的知识以后再整理和大家分享)。
下面闲话少说,开始本篇的内容——进程的内存消耗和泄漏
进程的虚拟地址空间VMA(Virtual Memory Area)
在linux操作系统中,每个进程都通过一个task_struct的结构体描叙,每个进程的地址空间都通过一个mm_struct描叙,c语言中的每个段空间都通过vm_area_struct表示,他们关系如下 :
上图中,task_struct中的mm_struct就代表进程的整个内存资源,mm_struct中的pgd为页表,mmap指针指向的vm_area_struct链表的每一个节点就代表进程的一个虚拟地址空间,即一个VMA。一个VMA最终可能对应ELF可执行程序的数据段、代码段、堆、栈、或者动态链接库的某个部分。
VMA的分布情况可以有通过pmap命令,及maps,smaps文件查看Linux用户态进程的内存管理,如下图:
另,VMA的具体内容可参考下图。
page fault的几种可能性
我们先来看张图:
(此图来源于宋宝华老师)
(此图来源于宋宝华老师)
综上Linux用户态进程的内存管理,page fault后,Linux会查VMA,也会比对VMA中和页表中的权限,体现出VMA的重要作用。
malloc分配的原理
malloc的过程其实就是把VMA分配到各种段当中,这时候是没有真正分配物理地址的。malloc 调用后,只是分配了内存的逻辑地址,在内核的mm_struct 链表中插入vm_area_struct结构体,没有分配实际的内存。当分配的区域写入数据是,引发页中断,建立物理页和逻辑地址的映射。下图表示了这个过程。
从操作系统角度来看,进程分配内存有两种方式,分别由两个系统调用完成:brk和mmap(不考虑共享内存)。
内存的消耗VSS RSS PSS USS
首先,我们评估一个进程的内存消耗都是指用户空间的内存linux进程运行空间查看,不包括内核空间的内存消耗 。这里我们用工具 procrank先来看下Linux进程的内存占用量 。
下面再用一张图来更好的解释VSS,RSS,PSS,USS之间的区别:
有了对VSS,RSS,PSS,USS的了解,我们趁热打铁来看下内存在进程中是如何被瓜分的:
(此图来源于宋宝华老师)
1044,1045,1054三个进程,每个进程都有一个页表,对应其虚拟地址如何向real memory上去转换。
process 1044的1,2,3都在虚拟地址空间,所以其VSS=1+2+3。
process 1044的4linux进程运行空间查看,5,6都在real memory上,所以其RSS=4+5+6。
分析real memory的具体瓜分情况:
4 libc代码段,1044,1045,1054三个进程都使用了libc的代码段,被三个进程分享。
5 bash shell的代码段,1044,1045都是bash shell,被两个进程分享。
6 1044独占
所以,上图中4+5+6并不全是1044进程消耗的内存,因为4明显被3个进程指向,5明显被2个进程指向,衍生出了PSS(按比例计算的驻留内存)的概念。进程1044的PSS为4/3 +5/2 +6。
最后,进程1044独占且驻留的内存USS为 6。
一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS
【部分内容整理于宋宝华老师课程】
留言评论