获取linux进程内存使用信息

对于linux系统,我们可以从/proc/{pid}/maps或者memmap(该文件不是每一个linux版本都有)获取进程的内存使用
情况,这两个文件的可读性还是比较差的。

#cat maps
00008000-00009000  r-xp  00000000   1f:12   288        /mnt/msc_int0/hello    (1)
00010000-00011000  rw-p  00000000   1f:12   288        /mnt/msc_int0/hello   (1)
00011000-00032000  rwxp  00011000   00:00   0[heap]               (33)
40000000-40002000  rw-p  40000000   00:00   0                  (2)
41000000-41017000  r-xp  00000000   1f:0d   817360     /lib/ld-2.3.3.so     (23)
4101e000-41020000  rw-p  00016000   1f:0d   817360     /lib/ld-2.3.3.so     (2)
41028000-41120000  r-xp  00000000   1f:0d   817593     /lib/libc-2.3.3.so    (248)
41120000-41128000  ---p  000f8000   1f:0d   817593     /lib/libc-2.3.3.so    (8)
41128000-41129000  r--p  000f8000   1f:0d   817593     /lib/libc-2.3.3.so    (1)
41129000-4112c000  rw-p  000f9000   1f:0d   817593     /lib/libc-2.3.3.so    (3)
4112c000-4112e000  rw-p  4112c000   00:00   0                  (2)
befeb000-bf000000  rwxp  befeb000   00:00   0[stack]              (21)

上面每个虚拟内存区域所占大小见上面最右侧一列,单位页。

第一列:虚拟内存段的起始和结束地址,表示物理内存中可执行文件或库的各段映射到虚拟内存中的某一段空间,或者
       进程堆栈段的虚拟内存空间。
第二列:代表该段虚拟内存段的权限,r=可读,w=可写,x=可执行,s=共享,p=私有。
第三列:虚拟内存区域在被映射文件中的偏移,该偏移量是页(4KB)对齐,偏移为0的表示ELF文件的该段只占用一页。
第四列:存储被映射文件存储体的主次设备号。
第五列:被映射文件的inode号。
第六列:被映射文件的路径。

上面文件涉及的都是虚拟内存的概念,所以可以看出这个进程占用了多少虚拟内存,不过最重要的是需要知道该进程
吃掉了多少物理内存,下面这个文件可以得到使用的物理内存。

#cat memmap
2

1

1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

1 1

4949494949494949491649494949494949494949494949

1 1

4917171717111617171717171717174949 0 049 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 49 0 0 0(+46)
0 0 0 0 0 0 0 044 042424242 0 0 0 0 41 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 47 0 0 0 0 0 0 0(+42)
04747484949494949494949 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 049 0 0 0 0 0 0 0 0(+49)
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 04849 0 0 0 04849 0 0 047 049 0 0 0 0 0 0 0 0 0 0 04949 0(+ 50)
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 046 0 0 0 0 0 0 0 0 0 0 0 0 0 0(+50)
44 0 0 0 0 0 0 0 0 0 0(+11)

0 0 0 0 0 0 0 0

49

1 1 1

1 1

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1

这个文件需要和maps文件对照起来看,memmap每一行对应maps文件的一行。
以下解读规则为猜测,并无从源码角度验证(十进制的数在linux中不会加入前导0,也就是000 = 0,01 = 1,012 = 12):
1. 只要出现了0,那么该0就代表一个虚拟内存页框,系统并未为其分配物理内存页;
2. 如果有数值出现,那么如何判断到底几个数字代表一个虚拟页框呢?这个就需要从maps文件中计算出每一段虚拟
   内存空间的大小,然后一个一个来数咯。
   这个数值不仅表示了系统已为该页框分配了物理页,同时也代表了该虚拟页框映射的物理页有多少个进程在使用。


例如:
00008000-00009000  r-xp  00000000   1f:12   288        /mnt/msc_int0/hello    (1)
<<< >>>
2
这里看出系统为hello的text段映射了一个物理页,而且是使用一个数字表示一个虚拟页框,同时也看到有2个进程
引用了该物理页。

 

00011000-00032000  rwxp  00011000   00:00   0[heap]               (33)
<<< >>>
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
这是hello进程的堆段,这个堆虚拟空间分配了132KB,但是实际上只分配了4KB的物理内存,也可以证实堆地址是
向上增长的。

41000000-41017000  r-xp  00000000   1f:0d   817360     /lib/ld-2.3.3.so     (23)
<<< >>>
4949494949494949491649494949494949494949494949
这是ld进程的txt段,系统为其23个虚拟页框全部分配了物理页,而且是2位数字表示一个虚拟页框,同时可以看到
每个物理页都有49个进程在引用。

befeb000-bf000000  rwxp  befeb000   00:00   0[stack]              (21)
<<< >>>
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1
这是hello的stack段,系统开辟了21个虚拟页框,但实际上只做了2个物理页的映射。也能够证实stack的地址是向下生长的。

对于上面数字最长的那一行,表示的是进程/lib/libc-2.3.3.so的虚拟空间,共248个虚拟页框,有数值的是2位代表一个页框,仔细数一数的话,可以证实。

ld和libc共享库占用的内存也反映到我们的这个程序上,导致表象上看起来我这小程序貌似是吃了不少内存,
所以除了进程自己的代码段,数据段,堆段,stack段之外的共享库占用的内存都是不真实的。由于代码段这种系统共享的特性,导致了我们想了解一个进程使用了多少内存,给我一个数,这样一个简单的要求,变得很复杂。

不过在2.6.16的linux版本中/proc/{pid}中出现了smaps的文件,这个可读性比较好。
# cat smaps
...
08048000-08107000 r-xp 00000000 08:01 392453     /bin/bash
Size:                764 kB
Rss:                 592 kB
Pss:                 296 kB
Shared_Clean:        592 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         0 kB
Referenced:          592 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
...
08108000-0810d000 rw-p 000bf000 08:01 392453     /bin/bash
Size:                 20 kB
Rss:                  20 kB
Pss:                  20 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:        20 kB
Referenced:           20 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
...
0964e000-0967c000 rw-p 00000000 00:00 0          [heap]
Size:                184 kB
Rss:                 184 kB
Pss:                 184 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:       184 kB
Referenced:          184 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
...
bfe07000-bfe28000 rw-p 00000000 00:00 0          [stack]
Size:                136 kB
Rss:                  28 kB
Pss:                  28 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:        28 kB
Referenced:           28 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB

size:  是进程使用内存空间,并不一定实际分配了物理内存;
Rss:   "Resident Set Size",实际驻留"在内存中"的内存数. 不包括已经交换出去的页面。RSS还包括了与其它
        进程共享的内存区域,通常用于共享库;
Pss:   Private Rss, Rss中私有的内存页面;
Shared_Clean:  Rss中和其他进程共享的未改写页面;
Shared_Dirty:  Rss和其他进程共享的已改写页面;
Private_Clean:  Rss中改写的私有页面页面;
Private_Dirty:  Rss中已改写的私有页面页面;
(其中Dirty页面如果没有交换机制的情况下,应该是不能回收的)


网上流传着Ben Maurer写的一个perl脚本可以将一个进程的使用情况整理得更清楚:

#!/usr/bin/perl

# Copyright Ben Maurer
# you can distribute this under the MIT/X11 License

use Linux::Smaps;

my $pid=shift @ARGV;
unless ($pid) {
 print "./smem.pl <pid>/n";
 exit 1;
}
my $map=Linux::Smaps->new($pid);
my @VMAs = $map->vmas;

format STDOUT =
VMSIZE:  @######## kb
$map->size
RSS:     @######## kb total
$map->rss
         @######## kb shared
$map->shared_clean + $map->shared_dirty
         @######## kb private clean
$map->private_clean
         @######## kb private dirty
$map->private_dirty
.

write;
   
printPrivateMappings ();
printSharedMappings ();

sub sharedMappings () {
    return grep { ($_->shared_clean  + $_->shared_dirty) > 0 } @VMAs;
}

sub privateMappings () {
    return grep { ($_->private_clean  + $_->private_dirty) > 0 } @VMAs;
}

sub printPrivateMappings ()
{
    $TYPE = "PRIVATE MAPPINGS";
    $^ = 'SECTION_HEADER';
    $~ = 'SECTION_ITEM';
    $- = 0;
    $= = 100000000;
    foreach  $vma (sort {-($a->private_dirty <=> $b->private_dirty)}
       privateMappings ()) {
 $size  = $vma->size;
 $dirty = $vma->private_dirty;
 $clean = $vma->private_clean;
 $file  = $vma->file_name;
 write;
    }
}

sub printSharedMappings ()
{
    $TYPE = "SHARED MAPPINGS";
    $^ = 'SECTION_HEADER';
    $~ = 'SECTION_ITEM';
    $- = 0;
    $= = 100000000;
   
    foreach  $vma (sort {-(($a->shared_clean + $a->shared_dirty)
      <=>
      ($b->shared_clean + $b->shared_dirty))}
     sharedMappings ()) {
 
 $size  = $vma->size;
 $dirty = $vma->shared_dirty;
 $clean = $vma->shared_clean;
 $file  = $vma->file_name;
 write;
 
 
    }
}

format SECTION_HEADER =
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$TYPE
@>>>>>>>>>> @>>>>>>>>>>  @>>>>>>>>>   @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
"vmsize" "rss clean" "rss dirty" "file"
.

format SECTION_ITEM =
@####### kb @####### kb @####### kb   @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$size $clean $dirty $file
.

使用之前需要先安装Linux::Smaps模块:
perl -MCPAN -e 'install Linux::Smaps'  // 需要网络

#./seme.pl 1762
VMSIZE:       6812 kb
RSS:          1876 kb total
              1496 kb shared
                24 kb private clean
               356 kb private dirty
PRIVATE MAPPINGS
     vmsize   rss clean   rss dirty   file
     184 kb        0 kb      184 kb   [heap]
     136 kb        0 kb       28 kb   [stack]
      20 kb        0 kb       20 kb   /bin/bash
      20 kb        0 kb       20 kb
      12 kb        0 kb       12 kb
       8 kb        0 kb        8 kb   /lib/libc-2.12.1.so
       8 kb        0 kb        8 kb   /lib/libncurses.so.5.7
       8 kb        0 kb        8 kb
       8 kb        0 kb        8 kb
       4 kb        0 kb        4 kb   /lib/libc-2.12.1.so
       4 kb        0 kb        4 kb   /lib/libncurses.so.5.7
       4 kb        0 kb        4 kb   /lib/libdl-2.12.1.so
       4 kb        0 kb        4 kb   /lib/libdl-2.12.1.so
       4 kb        0 kb        4 kb   /lib/ld-2.12.1.so
       4 kb        0 kb        4 kb   /lib/ld-2.12.1.so
       4 kb        0 kb        4 kb   /lib/libnss_nis-2.12.1.so
       4 kb        0 kb        4 kb   /lib/libnss_nis-2.12.1.so
       4 kb        0 kb        4 kb   /lib/libnss_files-2.12.1.so
       4 kb        0 kb        4 kb   /lib/libnss_files-2.12.1.so
       4 kb        0 kb        4 kb   /lib/libnss_compat-2.12.1.so
       4 kb        0 kb        4 kb   /lib/libnss_compat-2.12.1.so
       4 kb        0 kb        4 kb   /lib/libnsl-2.12.1.so
       4 kb        0 kb        4 kb   /lib/libnsl-2.12.1.so
       4 kb        0 kb        4 kb   /bin/bash
     764 kb       24 kb        0 kb   /bin/bash
SHARED MAPPINGS
     vmsize   rss clean   rss dirty   file
     764 kb      600 kb        0 kb   /bin/bash
    1372 kb      528 kb        0 kb   /lib/libc-2.12.1.so
     216 kb      108 kb        0 kb   /lib/libncurses.so.5.7
     112 kb      104 kb        0 kb   /lib/ld-2.12.1.so
    1612 kb       48 kb        0 kb   /usr/lib/locale/locale-archive
      76 kb       20 kb        0 kb   /lib/libnsl-2.12.1.so
      28 kb       20 kb        0 kb   /usr/lib/gconv/gconv-modules.cache
      36 kb       16 kb        0 kb   /lib/libnss_nis-2.12.1.so
      24 kb       16 kb        0 kb   /lib/libnss_compat-2.12.1.so
      40 kb       12 kb        0 kb   /lib/libnss_files-2.12.1.so
    2048 kb       12 kb        0 kb   /usr/lib/locale/locale-archive
       8 kb        8 kb        0 kb   /lib/libdl-2.12.1.so
       4 kb        4 kb        0 kb   [vdso]

从上面看到rss大小被分成了两个部分: private(私有)和shared(共享).
private rss就是我们最关心的进程实际占用的内存数.

参考文档:
1.
http://www.north36.com/?p=209
2. http://netcome.iteye.com/blog/746931
3. http://challengezcy.blog.163.com/blog/static/692292722009112395221795/
4. http://blog.csdn.net/tinnycloud/archive/2010/04/30/5546440.aspx

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值