当前位置: 首页 > Linux

Linux进程内存使用情况

时间:2023-04-06 20:23:44 Linux

Linux下,在使用top、ps等命令查看某个进程的内存使用情况时,经常会看到VIRT、RES、SHR等,它们是什么意思呢?不同的尺寸如何影响过程?本文将在未来讨论这个问题。在阅读本文之前,建议阅读Linux内存管理,了解Linux下内存的一些基本概念,例如什么是匿名和文件备份映射。查看进程使用的内存在进程看来,所有的内存都是虚拟内存,但是这个虚拟内存对应多少物理内存呢?正如我们在Linux内存管理中介绍的,并不是每一块虚拟内存都有对应的物理内存,对应的数据可能在磁盘上的某个文件中,也可能在交换空间的某个区域中。只有内核知道一个进程真实的物理内存使用情况,我们只能通过内核开放的一些接口来获取这些统计信息。top先看top的输出(top使用的数据来自/proc/[pid]/statm),这里只是摘录了一些数据:PIDUSERPRNIVIRTRESSHRS%CPU%MEMTIME+COMMAND2530root200000S0.30.00:02.69kworker/0:02714dev2004182437003084R0.30.70:00.02top3008dev2002246451243356S0.01.00:00:02bash使用的VI虚拟RT进程大小RES:系统为虚拟内存分配的物理内存大小,包括filebacked和匿名内存,其中匿名包括进程自身分配使用的内存,以及通过mmap与其他进程共享的内存;而文件backed的内存是指加载可执行文件和动态库所占用的内存,以及通过private方式调用mmap映射文件所占用的内存(当这部分数据在内存中被修改且文件没有回写时,则这部分内存变为匿名),这部分内存也可能与其他进程共享。SHR:RES的一部分,意思是与其他进程共享的内存,包括通过mmap共享的内存和filebackedmemory。通过priv调用mmap映射文件时,如果文件内容没有被修改,那么那部分内容就是SHR。一旦文件内容被修改,文件没有被回写,那么这部分内存就是匿名的,不是SHR。%MEM:等于RES/total*100%,其中total是指总的物理内存大小。注意:由于SHR可能被多个进程共享,所以系统中所有进程的RES之和可能会超过物理内存总量。同理,所有进程的%MEM之和可能会超过100%。从上面的分析可以看出,VIRT的引用值意义不大,它只能反映程序的大小,而RES并不能完全代表一个进程实际占用的内存空间,因为它也包含了部分SHR的,比如三个bash进程共享一个libc动态库,那么libc占用的内存归谁所有呢?三个过程是否平分?如果启动一个bash占用4M的RES,其中3M被libc占用,由于三个进程共享libc的3M,那么启动三个bash实际占用的内存为3*(4-3)+3=6M,但是如果简单的按照RES计算的话,三个进程会使用12M的空间。因此,了解RES和SHR这两个数据的含义,对于我们评估一台服务器能运行多少个进程就显得尤为重要。不要一看到apache的进程占用20M物理内存数除以20M就认为系统可以运行的apache进程数就是apache进程总数。其实这20M中有很大一部分可能是SHR。注意:top命令输出中的RES和pmap输出中的RSS是一回事。pmap上的top命令只显示一个进程占用了多少内存,而pmap可以给出更多关于谁占用了内存的详细信息。pmap命令的输出来自/proc/[pid]/maps和/proc/[pid]/smaps这两个文件。第一个文件包含每个段的一般描述,后一个文件包含更详细的描述。信息。这里使用pmap查看当前bash内存使用情况:#这里$$表示当前bash的进程ID,下面只显示部分输出结果dev@dev:~$pmap$$2805:bash0000000000400000976Kr-x--bash00000000006f30004Kr----Bash000000006F400036KRW---Bash00000000006FD00024KRW---[匿名]0000000000BE40001544KRW---[匿名]......00007F1FA0E9E0002912KR-----1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792k1792K1792K1792K1792k1792k1792k176171717171717171717171717171717Fthat.so00007f1fa13360002044K-----LIBC-2.23.SO00007F1FA153500016KR----------------libc-2.23.so00007f1fa15390008krw-----------------------投物-S-gconv-modules.cache00007f1fa1b8500016krw---[anon]00007f1fa1b8f0008krw----[anon]00007F1B910004KRW-------[匿名]00007ffde903a000132Krw---[堆栈]00007ffde90e40008Kr----[匿名]00007ffde90e40008Kr----[匿名]00007ffde90e4000n]ffffffffff6000004Kr-x--[anon]total22464K这里第一列是内存的起始地址,第二列是映射的地址大小,第三列是这块内存的访问权限,最后一个column是映射文件这里的地址都是虚拟地址,size也是虚拟地址的大小。这里的输出有很多[anon]行,说明磁盘上没有对应的文件。这些一般是动态库中的可执行文件或bss段。当然,对应文件的映射也可以是匿名的,比如文件的数据段。程序的data段和bss段的介绍可以参考elf的相关资料。可以看到bash、libc-2.23.so等文件有多行,但是每一行的权限都不一样。这是因为每个动态库或可执行文件都被分成很多段,有些只能读取和执行。代码段包括可以读写的数据段,以及“00007f1fa153b00016Krw---[anon]”这一行,也就是它上面的libc-2.23.so的bss段。[stack]表示进程使用的栈空间,这里看不到堆,因为pmap默认没有单独标记堆。由于堆是匿名的,所以从这里的大小可以推断出堆是“0000000000be40001544Krw---[anon]”。其实从上面的结果中,是看不出每个segment实际占用了多少物理内存的。要查看RSS,您需要使用-X参数。让我们看看更详细的输出:dev@dev:~$pmap-X$$2805:bashAddressPermOffsetDeviceInodeSizeRssPssReferencedAnonymousShared_HugetlbPrivate_HugetlbSwapSwapPssLockedMapping00400000r-xp00000000fc:00390914976888526888000000庆典006f3000r--p000f3000fc:003909144444400000庆典006f4000rw-p000f4000fc:00390914363636r3636000fsh00-p0000000000:0002404002400000-p0000000000:0001544154415441544154400000[堆].....7f1fa0e9e000r--p0000f:000136340291240083400000000locale-archive7f1fa1176000r-xp00000000fc:0052172617921512541512000000libc-2.23.so7f1fa130fc:0fc:0fc:00005217262044000000000libc-2.23.so7f1fa1535000r--p001bf000fc:00521726161616161600000libc-2.23.so7f1fa153-9000r1fa1539000r5217268888800000libc-2.23.so7f1fa153b000rw-p0000000000:000161212121200000......7f1fa196c000r-xp00000000fc1:02021521444144000000ld-2.23.so7f1fa1b7e000r--s00000000fc:001327382828928000000gconv-modules.cache7f1fa1b85000rw-p0000000000:0001616116016007f1fa1b8f000rw-p0000000000:00088888000007f1fa1b91000r--p00025000fc:005217024444400000ld-2.23.so2p00f0207f1fa1b9:005217024444400000ld-2.23.so7f1fa1b93000rw-p0000000000:00044444000007ffde903a000rw-p0000000012344402400000[堆栈]7ffde90e4000r--p0000000000:0008000000000[vvar]7ffde90e6000r-xp0000000000:0008404000000[vdso]ffffffffff600000r-xp0000000000:0004000000000[vsyscall]=============================================================================22468508425785084176400000KB权限字段多了s和p的标记,s表示是与他人共享的内存空间,读写会影响其他进程,p表示这是自己的私有内存空间,读写这部分内存不会影响其他进程。output标记了[heap]段,同时也解释了后面几个[anon]代表什么(vvar、vdso、vsyscall都是映射到内核的特殊文件段),映射字段为空的是bss段中的前一行映射文件(但在gconv-modules.txt之后有两行匿名映射)Anonymous列表示以Anonymous方式映射了哪些物理内存和多少物理内存,其大小小于或等于表示实际占用物理内存大小的RSSRSS列。top命令输出的SHR内存。最后我们看一下top命令输出的SHR有哪些输出dev@dev:~$pmap-d$$3108:bashAddressKbytesModeOffsetDeviceMapping0000000000400000976r-x--00000000000000000fc:00000bash0004000-00000000-0000000000000F30000FC:00000BASH00000000006F400036RW-------------000000000000000F40000FC:00000BASH0000000000006FD00024RW---------------00000000000000000000000000000:00000[anon]0000000000000000000000000000000000000000000000000000000000000000000000000000000000来----0000000000000000000000000:00000[anon]00007F53AF1980008RW----0000000000000000000000000000000000000:00007F53AF19A002.23.so00007f53af19c0004rw---0000000000000000000:00000[匿名]00007ffc5a94b000132rw---0000000000000000000:00000[堆栈]00007ffc5a90000008R-----00000000000000000000000:00000[ANON]00007FFC5A9B90008R-X---00000000000000000000000000000000000:00000:00000[ANON]FFFFFFFFFFFFFF6000000dev:~$top-p$$PIDUSERPRNIVIRTRESSHRS%CPU%MEMTIME+COMMAND3108dev2002246450283264S0.01.00:00.02bash从上面的输出可以看出SHR≈RES-writeable/private,其中writeable/private主要包括stack和heap,还有可执行文件和动态库的data和bss段,stack+heap=1544+132=1675,已经占了绝大多数,所以data和bss段之类的基本可以忽略不计,所以一般来说,SHR≈RES-[堆]-[栈],由于栈一般比较小,上式可以进一步约等于:SHR≈RES-[堆]总结一下top命令可以看到一个进程占用的虚拟内存空间,物理内存空间,和其他进程共享的物理内存空间。这里的共享空间包括通过mmap共享的内存、共享的可执行文件和动态库。mmap命令可以看到更详细的信息,比如可执行文件和链接的动态库的大小,占用了物理内存的哪些段。进程占用的虚拟地址空间的大小与程序的大小有关。除了栈和堆段,其他的段的大小基本是固定的,在程序链接的时候就已经确定了,所以基本上你只需要关注栈和堆段就可以了。由于栈相对于堆来说是比较小的,所以只要没有栈异常,只需要关注堆即可。在实际工作过程中,其实我们更关心的是RSS用了多少,被谁用了。简单来说,如果我们不同时启动多个进程(同一个程序),RSS是一个很好的实际物理内存占用的参考值,但是如果像apache一样同时运行多个进程,那么RSS减去SHR占用的空间是实际物理内存使用情况的一个很好的参考值。当然,这些都是大概的估计。准确评估一个进程占用多少内存还是有难度的。它需要深入了解流程的每个部分,尤其是在SHR部分中哪些流程被共享在一起。但是现在服务器上的内存都是以G为单位的形式,所以一般来说,粗略的估算和合理的测试就可以满足我们的需求。参考UnderstandingProcessmemoryUnderstandingmemoryusageonLinux