摘要
进程的虚拟地址空间VMA(Virtual Memory Area)
Pagefault的几种可能性、VMA的作用、major缺页和minor缺页
进程内存消耗的4个概念:vss、rss、pss和uss
应用内存泄漏的界定方法
内存泄漏的检测工具:valgrind和addresssanitizer
1. 进程的虚拟地址空间VMA(Virtual Memory Area)
上图中,task_struct中的mm_struct就代表进程的整个内存资源,mm_struct中的pgd为页表,mmap指针指向的vm_area_struct链表的每一个节点就代表进程的一个虚拟地址空间,即一个VMA。一个VMA最终可能对应ELF可执行程序的数据段、代码段、堆、栈、或者动态链接库的某个部分。
VMA的分布情况可以有通过pmap命令,及maps,smaps文件查看,如下图:
pmap pid
pmap – report memory map of a process
/proc/<pid>/maps及/proc/<pid>/smaps更详细展示了进程VMA的分布情况。
观察pmap结果及maps,smaps文件,可以发现注:IA32下,一个进程的VMA是在0~3G之间分散分布的,是一段一段的,中间会有很多空白区域,空白区域对进程来说都是不能访问的,VMA中会标注这一段的RWX的具体权限。如下图所示:
另,VMA的具体内容可参考下图。
2. Page fault的几种可能性、VMA的作用、major缺页和minor缺页
1)如,调用malloc申请100M内存,IA32下在0~3G虚拟地址中立刻就会占用到大小为100M的VMA,且符合堆的定义,这一段VMA的权限是R+W的。但由于Lazy机制,这100M其实并没有获得,这100M全部映射到一个物理地址相同的零页,且在页表中记录的权限为只读的。当100M中任何一页发生写操作时,MMU会给CPU发page fault(MMU可以从寄存器读出发生page fault的地址;MMU可以读出发生page fault的原因),Linux内核收到缺页中断,在缺页中断的处理程序中读出虚拟地址和原因,去VMA中查,发现是用户程序在写malloc的合法区域且有写权限,Linux内核就真正的申请内存,页表中对应一页的权限也修改为R+W。
2)如,程序中有野指针飞到了此程序运行时进程的VMA以外的非法区域,硬件就会收到page fault,进程会收到SIGSEGV信号报段错误并终止。
3)如,代码段在VMA中权限为R+X,如果程序中有野指针飞到此区域去写,则也会发生段错误。(另,malloc堆区在VMA中权限为R+W,如果程序的PC指针飞到此区域去执行,同样发生段错误。)
4)如,执行代码段时会发生缺页,Linux申请1页内存,并从硬盘读取出代码段,此时产生了IO操作,为major主缺页。
综上,page fault后,Linux会查VMA,也会比对VMA中和页表中的权限,体现出VMA的重要作用。
3. 进程内存消耗的4个概念:vss、rss、pss和uss
首先,我们评估一个进程的内存消耗都是指用户空间的内存,不包括内核空间的内存消耗,即IA32下,虚拟地址在0~3G的部分所对应的物理地址的内存。
VSS – Virtual Set Size
RSS – Resident Set Size
PSS – Proportional Set Size
USS – Unique Set Size
1044,1045,1054三个进程,每个进程都有一个页表,对应其虚拟地址如何向real memory上去转换。
process 1044的1,2,3都在虚拟地址空间,所以其VSS=1+2+3。
process 1044的4,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。
评估进程内存耗费情况一般使用smem工具,查看PSS(最公平):
1)命令行模式
2)饼状图模式
3)柱状图模式
跑一个死循环程序,观察其smaps文件。然后再把此程序跑一份,观察其smaps文件变化(smaps内容很长,只看第一段):
可以看出PSS由4k变为2k,而Shared_clean由0k变为4k,Private_clean由4k变为0k。
4. 应用内存泄漏的界定方法
内存泄漏:进程运行的时间越久,耗费的内存越多,申请与释放不成对。
内存泄漏只看USS即可。
观察内存泄漏使用连续多点采样法:
5. 内存泄漏的检测工具:valgrind和addresssanitizer
每次free(p2),没有free(p1),申请与释放不成对。
使用valgrind检测由内存泄漏的程序:
使用addresssanitizer中的lsan工具检测程序内存泄漏:
调用 __lsan_do_leak_check()做检查
检测出泄漏了10次。
如今更流行使用addresssanitizer。
内存管理报名
报名:《Linux的任督二脉》之《内存管理》微课(连续5晚)