【转】linux进程的内存使用解析
进程XXX占用了多少内存?这是个经常被问到,也经常被答错的问题。Linux进程的内存分配是个比较复杂的话题,而Linux上的工具往往把这个问题过分简单化,因此引出不少误解和困惑。首先把ps, top这类工具扔掉,然后看这么一个简单程序:
[root@pczou pczou]# cat ./prog.c #i nclude <stdio.h> #i nclude <stdio.h> #i nclude <sys/types.h> #i nclude <unistd.h> #define ONEM (1024*1024) int func() { char s[16*ONEM]; char* p; p = malloc(32*ONEM); pause(); return 0; } int main() { printf("pid: %d\n", getpid()); func(); return 0; }其中func()这个函数分配了32MB的内存,以及16MB的堆栈。 运行一下这个prog程序,prog会停在pause()的位置,看看ps怎么说:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 4238 0.0 0.0 52396 352 pts/0 S 21:29 0:00 ./progVSZ指的是进程内存空间的大小,这里是52396KB; RSS指的是驻留物理内存中的内存大小,这里是352KB。 一般系统管理员知道VSZ并不代表进程真正用到的内存,因为有些空间会仅在页表中挂个名,也就是说只是虚拟存在着,只有真正用到的时候内核才会把虚拟页面和真正的物理页面映射起来。比如,prog.c中用malloc()分配的32MB内存,由于程序中并没有用到这些内存,没有物理内存被分配,也就不应算到进程的帐上。 进程的内存使用情况比较复杂,这是因为:
- 进程所申请的内存不一定真正会被用到
- 真正用到了的内存也不一定是只有该进程自己在用 (比如动态共享库)
Virtual memory : 52396 KB Effective VM : 52120 KB Mapped : 352 KB Effective mapped: 76.6 KB Sole use : 72 KB Per file memory use ld-2.3.4.so : VM 94208 B, M 90112 B, S 8192 B prog : VM 8192 B, M 8192 B, S 8192 B libc-2.3.4.so : VM 1180 KB, M 221184 B, S 16384 B可以看出,虽然虚拟地址空间是52396KB,实际映射(a.k.a. 分配)的空间是352KB,这和ps给出的结果一致。再看"Effective Mapped"这个值,仅为76.6 KB。这个值的计算方法是:
有效的实际使用内存 = 该进程独占的内存 + 共享的内存A /共享A的进程数目 + 共享的内存B /共享B的进程数目 + ...比如对于一个kde应用程序kontact,它用的Qt库的虚拟地址空间为7M,而实际映射的空间有4.5M,也就是说真正给 Qt分配物理内存大小为4.5M。假设有10个KDE应用正在运行,那么记到kontact帐上的就不应该是4.5M,而是A-A之后的0.45M。这么算帐虽然并不十分准确,但"Effective Mapped"已经足以说明进程所占用内存的实际大小了。 OK,最后用这个方法给系统中所有进程都“结下帐”: 从上面的统计结果可以看出:
- 虽然firefox的占用虚拟空间是最大的,但其实际占用的内存却比X Server要少。
- firefox 的实际占用的内存和其RSS (a.k.a. mapped)差别不大,占RSS的99%;而kontact的实际占用内存却仅占RSS的63%,有27%的内存是共享的。由此可以粗略看出我用的窗口管理器是KDE而非Gnome,why? 因为Qt之类的共享库被很多KDE进程分担了。
- sole值可以理解为"private mapped",也就是这个进程退出后可能被释放的内存 (对于非匿名的映射页面可能还会存留一段时间)。
- http://blog.zhaoke.com/31.html
- http://www.berthels.co.uk/exmap/
- http://projects.o-hand.com/#misc
- smaps
先介绍几个基本概念:
SIZE: 进程使用的地址空间, 如果进程映射了100M的内存, 进程的地址空间将报告为100M内存. 事实上, 这个大小不是一个程序实际使用的内存数.
RSS: "Resident Set Size", 实际驻留"在内存中"的内存数. 不包括已经交换出去的代码. 举一个例子: 如果你有一个程序使用了100K内存, 操作系统交换出40K内存, 那么RSS为60K. RSS还包括了与其它进程共享的内存区域. 这些区域通常用于libc库等.
SHARE: RSS中与其它进程共享的内存部分大小.
VMSIZE: 一个进程占用的总的地址空间大小. 它包括了没有映射到内存中的页面.
Private RSS: 映射到内存中的页面, 这些页面仅由进程单独使用. 这也是我们最关心地方: 进程实际占用的内存数.
如何来查看Private RSS呢? /proc接口中每一个进程目录下的smaps提供了private rss信息. smaps是在2.6.16内核版本引进来的.
私有驻留内存数(Private RSS):
查看/proc/$pid/smaps下面我以本站使用的Fedora Core 5为例, 查看进程号1293(vmware-guestd, 本站使用的是一台基于vmware的客户虚拟机, )使用的Private RSS数.
CODE: # cat /proc/1293/smaps 00111000-00112000 rwxp 00111000 00:00 0 Size: 4 kB Rss: 4 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 4 kB 0050e000-0050f000 rwxp 0050e000 00:00 0 Size: 4 kB Rss: 0 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 0 kB 0051a000-0051b000 r-xp 0051a000 00:00 0 [vdso] Size: 4 kB Rss: 4 kB Shared_Clean: 4 kB Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 0 kB 0051b000-00534000 r-xp 00000000 fd:00 194898 /lib/ld-2.4.so Size: 100 kB Rss: 0 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 0 kB 00534000-00535000 r-xp 00018000 fd:00 194898 /lib/ld-2.4.so Size: 4 kB Rss: 0 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 0 kB 00535000-00536000 rwxp 00019000 fd:00 194898 /lib/ld-2.4.so Size: 4 kB Rss: 0 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 0 kB 00538000-00665000 r-xp 00000000 fd:00 194905 /lib/libc-2.4.so Size: 1204 kB Rss: 212 kB Shared_Clean: 204 kB Shared_Dirty: 0 kB Private_Clean: 8 kB Private_Dirty: 0 kB 00665000-00667000 r-xp 0012d000 fd:00 194905 /lib/libc-2.4.so Size: 8 kB Rss: 8 kB Shared_Clean: 4 kB Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 4 kB 00667000-00668000 rwxp 0012f000 fd:00 194905 /lib/libc-2.4.so Size: 4 kB Rss: 4 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 4 kB 00668000-0066b000 rwxp 00668000 00:00 0 Size: 12 kB Rss: 8 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 8 kB 08047000-08062000 r-xp 00000000 fd:00 292327 /usr/sbin/vmware-guestd Size: 108 kB Rss: 64 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 64 kB Private_Dirty: 0 kB 08062000-08063000 rwxp 0001a000 fd:00 292327 /usr/sbin/vmware-guestd Size: 4 kB Rss: 4 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 4 kB 08063000-08068000 rwxp 08063000 00:00 0 Size: 20 kB Rss: 12 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 12 kB 08385000-08886000 rwxp 08385000 00:00 0 [heap] Size: 5124 kB Rss: 5080 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 5080 kB bfeb2000-bfec7000 rwxp bfeb2000 00:00 0 [stack] Size: 84 kB Rss: 12 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 12 kB上面我们看到从smaps看不太方便, 推荐使用Ben Maurer写的: (本地)
下面是由seme.pl脚本解析的smaps数据:
首先安装Linux::Smaps模块:
perl -MCPAN -e 'install Linux::Smaps'然后用seme.pl解析1293进程的Smaps数据:
CODE: # ./smem.pl 1293 VMSIZE: 7200 kb RSS: 1052 kb total 192 kb shared 100 kb private clean 760 kb private dirty PRIVATE MAPPINGS vmsize rss clean rss dirty file 5636 kb 8 kb 724 kb [heap] 84 kb 0 kb 12 kb [stack] 4 kb 0 kb 4 kb 8 kb 0 kb 4 kb /lib/libc-2.4.so 4 kb 0 kb 4 kb /lib/libc-2.4.so 12 kb 4 kb 4 kb 4 kb 0 kb 4 kb /usr/sbin/vmware-guestd 20 kb 8 kb 4 kb 1204 kb 16 kb 0 kb /lib/libc-2.4.so 108 kb 64 kb 0 kb /usr/sbin/vmware-guestdSHARED MAPPINGS
vmsize rss clean rss dirty file 1204 kb 188 kb 0 kb /lib/libc-2.4.so 4 kb 4 kb 0 kb [vdso]从上面看到rss大小被分成了两个部分: private(私有)和shared(共享).
private rss就是我们最关心的进程实际占用的内存数.参考:
1. 2. smaps and maps, also proc man pages. 3. 关键字: linux [root ~]# cat /proc/self/status Name: cat State: R (running) SleepAVG: 88% Tgid: 5783 Pid: 5783 PPid: 5742 TracerPid: 0 Uid: 0 0 0 0 Gid: 0 0 0 0 FDSize: 256 Groups: 0 1 2 3 4 6 10 VmSize: 6588 kB VmLck: 0 kB VmRSS: 400 kB VmData: 144 kB VmStk: 2040 kB VmExe: 14 kB VmLib: 1250 kB StaBrk: 0804e000 kB Brk: 088df000 kB StaStk: bfe03270 kB ExecLim: 0804c000 Threads: 1 SigPnd: 0000000000000000 ShdPnd: 0000000000000000 SigBlk: 0000000000000000 SigIgn: 0000000000000000 SigCgt: 0000000000000000 CapInh: 0000000000000000 CapPrm: 00000000fffffeff CapEff: 00000000fffffeff 输出解释 参数 解释 Name 应用程序或命令的名字 State 任务的状态,运行/睡眠/僵死/ SleepAVG 任务的平均等待时间(以nanosecond为单位),交互式任务因为休眠次数多、时间长,它们的 sleep_avg 也会相应地更大一些,所以计算出来的优先级也会相应高一些。 Tgid 线程组号 Pid 任务ID Ppid 父进程ID TracerPid 接收跟踪该进程信息的进程的ID号 Uid Uid euid suid fsuid Gid Gid egid sgid fsgid FDSize 文件描述符的最大个数,file->fds Groups VmSize(KB) 任务虚拟地址空间的大小 (total_vm-reserved_vm),其中total_vm为进程的地址空间的大小,reserved_vm:进程在预留或特殊的内存间的物理页 VmLck(KB) 任务已经锁住的物理内存的大小。锁住的物理内存不能交换到硬盘 (locked_vm) VmRSS(KB) 应用程序正在使用的物理内存的大小,就是用ps命令的参数rss的值 (rss) VmData(KB) 程序数据段的大小(所占虚拟内存的大小),存放初始化了的数据; (total_vm-shared_vm-stack_vm) VmStk(KB) 任务在用户态的栈的大小 (stack_vm) VmExe(KB) 程序所拥有的可执行虚拟内存的大小,代码段,不包括任务使用的库 (end_code-start_code) VmLib(KB) 被映像到任务的虚拟内存空间的库的大小 (exec_lib) VmPTE 该进程的所有页表的大小,单位:kb Threads 共享使用该信号描述符的任务的个数,在POSIX多线程序应用程序中,线程组中的所有线程使用同一个信号描述符。 SigQ 待处理信号的个数 SigPnd 屏蔽位,存储了该线程的待处理信号 ShdPnd 屏蔽位,存储了该线程组的待处理信号 SigBlk 存放被阻塞的信号 SigIgn 存放被忽略的信号 SigCgt 存放被俘获到的信号 CapInh Inheritable,能被当前进程执行的程序的继承的能力 CapPrm Permitted,进程能够使用的能力,可以包含CapEff中没有的能力,这些能力是被进程自己临时放弃的,CapEff是CapPrm的一个子集,进程放弃没有必要的能力有利于提高安全性 CapEff Effective,进程的有效能力