admin 管理员组文章数量: 887018
top命令VIRT,RES,SHR,DATA
linux top命令VIRT,RES,SHR,DATA的含义
1 VIRT RES SHR的准确含义
三个内存指标,VRIT,RES,SHR准确含义是什么?
那我们就去看下源码吧,这就是开源软件的最大的好处。
首先这三个数据的源头,肯定是内核,进程的相关数据结构肯定是由内核维护。那么top作为一个用户空间的程序,要想获取内核空间的数据,就需要通过系统接口(API)获取。而proc文件系统是Linux内核空间和用户空间交换数据的一个途径,而且是非常重要的一种途径,这点和windows更倾向于基于函数调用的形式不同。
当你调用系统函数read读取一个普通文件时,内核执行对应文件系统的代码从磁盘传送文件内容给你。
当你调用系统函数read读取一个 proc文件时,内核执行对应的proc文件系统的代码从内核的数据结构中传送相关内容给你。proc文件和磁盘没有关系。只是系统接口而已。
而一个进程的相关信息,Linux全部通过/proc/<pid>/内的文件告诉了我们。
如下,你可以使用普通的文件读写工具,比如cat获取进程的各种信息。这比函数调用的方式灵活多了、丰富多了。
回到我们的问题,top命令显示的进程信息,肯定也是通过proc获取的,因为除此之外没有其他途径,没有系统函数可以做这个事情,top也不可能越过用户层直取内核获取数据。
带着以上信息,很快就可以从top的源码中找到关键代码:
啊哈,statm文件:
根据sscanf的顺序,第一个值是VIRT,第二个值是RES,第三个值是SHR!
等等,好像数值对不上,top显示的SHR是344k,而statm给出的是86!
再来看一行关键代码:
statm显示的是页数,top显示的是KB。X86下,一页是4KB,86 * 4 = 344。这就对了!
于是乎,我们找到了最关键的入口,接下来按图索骥,看看内核是怎么产生statm文件内容就可以了。~~
proc_pid_statm函数负责产生statm文件内容,当你使用cat命令打印statm文件时,内核中的这个函数会执行。
proc_pid_statm获取进程的mm_struct数据结构,而这个数据结构就是进程的内存描述符,通过它可以获取进程内存使用、映射的全部信息。
进一步考察task_statm函数,可以看到:
第一个值(VIRT)就是mm->total_vm,即进程虚存的总大小,这个比较清晰,只要进程申请了内存,无论是malloc还是堆栈还是全局,都会计入这个值;
第二个值(RES)是mm->file_rss+mm->anon_rss;
第三个值(SHR)是mm->file_rss。
RES要和SHR结合者看,内核把物理内存分为了两部分,一部分是映射至文件的,一部分是没有映射至文件的即匿名内存,完全和共不共享没有关系!
但file_rss为什么叫做shared呢?应该是一种指示性表述,表示这部分内存可能是共享的。但并不代表真正共享了。那么到底哪些计入file_rss?通过查阅相关代码,发现(可能有遗漏):
l 程序的代码段。
l 动态库的代码段。
l 通过mmap做的文件映射。
l 通过mmap做的匿名映射,但指明了MAP_SHARED属性。
l 通过shmget申请的共享内存。
即进程通过以上方式占用的物理内存,计入file_rss,也就是top的SHR字段。我们看到一般这些内存都是以共享方式存在。但如果某个动态库只一个进程在使用,它的代码段就没有被共享着。
反过来再来看anon_rss统计的内容,是否就一定是独占的?也不是,比如新fork之后的子进程,由于copy on write机制,在页面被修改之前,和父进程共享。这部分值并不体现在top命令的SHR字段内。
综上所述top命令显示的SHR字段,并不是准确描述了进程与其他进程共享使用的内存数量,是存在误差的。
那么如何获取进程准确的共享内存数量?
2 获取进程准确的共享内存数量
我们注意到在描述进程信息的proc/<pid>内,有一个smaps文件,里面展示了所有内存段的信息,其中有Shared_Clean Shared_Dirty Private_Clean Private_Dirty:几个字段。
找到相关代码,可以看到,一个页面如果映射数>=2计入Shared_* ; 如果=1计入Private_*。(脏页计入*_Dirty,否则计入*_Clean)
统计smaps文件内所有段的Shared_*值的总和就是进程准确的共享内存数量!
统计smaps文件内所有段的Private_*值的总和就是进程准确的独占内存数量!
3 总结
通过以上分析,我们可以得到如下结论:
l top命令通过解析/proc/<pid>/statm统计VIRT和RES和SHR字段值。
l VIRT是申请的虚拟内存总量。
l RES是进程使用的物理内存总和。
l SHR是RES中”映射至文件”的物理内存总和。包括:
程序的代码段。
动态库的代码段。
通过mmap做的文件映射。
通过mmap做的匿名映射,但指明了MAP_SHARED属性。
通过shmget申请的共享内存。
l /proc/<pid>/smaps内Shared_*统计的是RES中映射数量>=2的物理内存。
l /proc/<pid>/smaps内Private_*统计的是RES中映射数量=1的物理内存。
top 运行中可以通过 top 的内部命令对进程的显示方式进行控制。内部命令如下: s – 改变画面更新频率 l – 关闭或开启第一部分第一行 top 信息的表示 t – 关闭或开启第一部分第二行 Tasks 和第三行 Cpus 信息的表示 m – 关闭或开启第一部分第四行 Mem 和 第五行 Swap 信息的表示 N – 以 PID 的大小的顺序排列表示进程列表 P – 以 CPU 占用率大小的顺序排列进程列表 M – 以内存占用率大小的顺序排列进程列表 h – 显示帮助 n – 设置在进程列表所显示进程的数量 q – 退出 top s – 改变画面更新周期 序号 列名 含义 a PID 进程id b PPID 父进程id c RUSER Real user name d UID 进程所有者的用户id e USER 进程所有者的用户名 f GROUP 进程所有者的组名 g TTY 启动进程的终端名。不是从终端启动的进程则显示为 ? h PR 优先级 i NI nice值。负值表示高优先级,正值表示低优先级 j P 最后使用的CPU,仅在多CPU环境下有意义 k %CPU 上次更新到现在的CPU时间占用百分比 l TIME 进程使用的CPU时间总计,单位秒 m TIME+ 进程使用的CPU时间总计,单位1/100秒 n %MEM 进程使用的物理内存百分比 o VIRT 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES p SWAP 进程使用的虚拟内存中,被换出的大小,单位kb。 q RES 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA r CODE 可执行代码占用的物理内存大小,单位kb s DATA 可执行代码以外的部分(数据段+栈)占用的物理内存大小,单位kb t SHR 共享内存大小,单位kb u nFLT 页面错误次数 v nDRT 最后一次写入到现在,被修改过的页面数。 w S 进程状态。(D=不可中断的睡眠状态,R=运行,S=睡眠,T=跟踪/停止,Z=僵尸进程) x COMMAND 命令名/命令行 y WCHAN 若该进程在睡眠,则显示睡眠中的系统函数名 z Flags 任务标志,参考 sched.h
默认情况下仅显示比较重要的 PID、USER、PR、NI、VIRT、RES、SHR、S、%CPU、%MEM、TIME+、COMMAND 列。可以通过下面的快捷键来更改显示内容。
通过 f 键可以选择显示的内容。按 f 键之后会显示列的列表,按 a-z 即可显示或隐藏对应的列,最后按回车键确定。 按 o 键可以改变列的显示顺序。按小写的 a-z 可以将相应的列向右移动,而大写的 A-Z 可以将相应的列向左移动。最后按回车键确定。 按大写的 F 或 O 键,然后按 a-z 可以将进程按照相应的列进行排序。而大写的 R 键可以将当前的排序倒转。测试如下:
一)
- #include <iostream>
- int main()
- {
- char * p = new char [1024*1024*512];
- getchar();
- return 0;
- }
top结果如下:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
401 huyiyang 17 0 523m 916 792 S 0.0 0.0 0:00.00 ./main
VIRT包含了new出来的512MB空间,但是RES不包含该空间。即刚new出来的空间,如果没有使用,会放入SWAP中,并不在内容中真实的分配物理内存。
二)
- #include <iostream>
- int main()
- {
- char * p = new char [1024*1024*512];
- memset(p, 0, 1024*1024*512);
- getchar();
- return 0;
- }
top结果如下:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
32604 test 17 0 523m 512m 792 S 0.0 26.2 0:00.33 ./main
VIRT包含new出来的512MB空间,RES包含目前使用的memset的512M空间。即new出来的空间被使用后,会真实分配物理内存。
三)
[cpp] view plain copy- #include <iostream>
- int main()
- {
- char * p = new char [1024*1024*512];
- memset(p + 1024*1024*128, 0, 1024*1024*128);
- getchar();
- return 0;
- }
top结果如下:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
456 test 17 0 523m 128m 792 S 0.0 6.6 0:00.07 ./main
VIRT包含new出来的512MB空间,RES包含目前使用的memset的128M空间。即new出来的空间,如果只使用部分,则只分配部分物理内存。
四)
[cpp] view plain copy- #include <iostream>
- int main()
- {
- char p[1024*1024*10];
- getchar();
- return 0;
- }
top结果如下:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
544 test 17 0 21568 884 760 S 0.0 0.0 0:00.00 ./main
没有使用的栈空间,VIRT会包含(没有使用的栈空间会在SWAP中)。
五)
[cpp] view plain copy- #include <iostream>
- int main()
- {
- char p[1024*1024*10];
- memset(p, 0, 1024*1024*10);
- getchar();
- return 0;
- }
top结果如下:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
561 test 17 0 21568 10m 760 S 0.0 0.6 0:00.00 ./main
已经使用的栈空间,VIRT和RES都会包含。
六)
[cpp] view plain copy- #include <iostream>
- int main()
- {
- char ** pp = new char * [1024];
- for(int i=0;i<1024;i++)
- {
- pp[i] = new char [1024*1024*512];
- memset(pp[i], 0, 1024*1024*512);
- printf("p%d\n", i);
- getchar();
- }
- return 0;
- }
第一个循环时:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ SWAP COMMAND
3627 test 18 0 523m 512m 836 S 0.0 26.2 0:00.34 10m ./main
第二个循环时:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ SWAP COMMAND
3627 huyiyang 18 0 1035m 1.0g 836 S 0.0 52.4 0:00.69 10m ./main
第三个循环时:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ SWAP COMMAND 3627 test 18 0 1547m 1.5g 836 S 0.0 78.5 0:01.03 10m ./main在我的服务器上,当执行到第四个循环时,并且有其他程序占用较大内存的情况下,top结果如下:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ SWAP COMMAND
3427 test 16 0 2059m 711m 836 S 0.0 36.4 0:01.45 1.3g ./main
版权声明:本文标题:top命令VIRT,RES,SHR,DATA 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1730809840h1386596.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论