Linux Top中的内存参数详解

Posted by 云谷计算 on January 4, 2019

Linux内存类型

一般来说,业务进程使用的内存主要有以下几种情况:

  1. 用户空间的匿名映射页(Anonymous pages in User Mode address spaces),比如调用malloc分配的内存,以及使用MAP_ANONYMOUS的mmap;当系统内存不够时,内核可以将这部分内存交换出去;

  2. 用户空间的文件映射页(Mapped pages in User Mode address spaces),包含map file和map tmpfs;前者比如指定文件的mmap,后者比如IPC共享内存;当系统内存不够时,内核可以回收这些页,但回收之前可能需要与文件同步数据;

  3. 文件缓存(page in page cache of disk file);发生在程序通过普通的read/write读写文件时,当系统内存不够时,内核可以回收这些页,但回收之前可能需要与文件同步数据;

  4. buffer pages,属于page cache;比如读取块设备文件。

Linux Top支持的内存参数

使用top命令,按f按键进入字段选择模式,可以选择更多top支持的指标,上,下方向键选择,空格键确定;并同时在’VIRT’参数上按’s’键,表示top显示的时候按VIRT指标排序。

Fields Management for window 1:Def, whose current sort field is VIRT
   Navigate with Up/Dn, Right selects for move then <Enter> or Left commits,
   'd' or <Space> toggles display, 's' sets sort.  Use 'q' or <Esc> to end!

* PID     = Process Id           * SWAP    = Swapped Size (KiB)   * RSlk    = RES Locked (KiB)
* USER    = Effective User Name  * CODE    = Code Size (KiB)      * RSsh    = RES Shared (KiB)
* PR      = Priority             * DATA    = Data+Stack (KiB)       CGNAME  = Control Group name
* NI      = Nice Value           * nMaj    = Major Page Faults
* VIRT    = Virtual Image (KiB)  * nMin    = Minor Page Faults
* RES     = Resident Size (KiB)    nDRT    = Dirty Pages Count
* SHR     = Shared Memory (KiB)    WCHAN   = Sleeping in Function
* S       = Process Status         Flags   = Task Flags <sched.h>
* %CPU    = CPU Usage              CGROUPS = Control Groups
* %MEM    = Memory Usage (RES)     SUPGIDS = Supp Groups IDs
* TIME+   = CPU Time, hundredths   SUPGRPS = Supp Groups Names
* COMMAND = Command Name/Line      TGID    = Thread Group Id
  PPID    = Parent Process pid     OOMa    = OOMEM Adjustment
  UID     = Effective User Id      OOMs    = OOMEM Score current
  RUID    = Real User Id           ENVIRON = Environment vars
  RUSER   = Real User Name         vMj     = Major Faults delta
  SUID    = Saved User Id          vMn     = Minor Faults delta
  SUSER   = Saved User Name        USED    = Res+Swap Size (KiB)
  GID     = Group Id               nsIPC   = IPC namespace Inode
  GROUP   = Group Name             nsMNT   = MNT namespace Inode
  PGRP    = Process Group Id       nsNET   = NET namespace Inode
  TTY     = Controlling Tty        nsPID   = PID namespace Inode
  TPGID   = Tty Process Grp Id     nsUSER  = USER namespace Inode
  SID     = Session Id             nsUTS   = UTS namespace Inode
  nTH     = Number of Threads      LXC     = LXC container name
  P       = Last Used Cpu (SMP)  * RSan    = RES Anonymous (KiB)
  TIME    = CPU Time             * RSfd    = RES File-based (KiB)

这里我们主要关注如下内存指标:VIRT, RES, SHR,SWAP, CODE, DATA, nMaj, nMin, RSan, RSfd, RSlk RSsh,经过定制后的top看起来是如下这样的:

top - 14:36:34 up 11 days,  3:52,  5 users,  load average: 0.88, 0.93, 0.99
Tasks: 622 total,   1 running, 380 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.1 us,  0.1 sy,  0.0 ni, 99.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem : 19667219+total, 16811641+free,  7277708 used, 21278080 buff/cache
KiB Swap:        0 total,        0 free,        0 used. 18772587+avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND      SWAP   CODE    DATA nMaj nMin   RSan   RSfd   RSlk   RSsh
2863765 root      20   0 17.139g 2.346g  22680 S   0.3  1.3  35:42.99 qemu-syst+      0  10824 16.552g    0  14k 2.325g  22660      0     20
1185988 root      20   0 8690284 2.937g  22920 S   1.0  1.6 102:49.77 qemu-syst+      0  10952 4790564    0 375k 2.916g  22900      0     20
3333090 falcon    20   0 4588624  38420   7240 S   0.0  0.0  24:58.97 falcon-ag+      0   6444  584208    0  11k  31180   7240      0      0
   3155 root      20   0 3932828 121680  10708 S   0.0  0.1   3:27.14 devpi-ser+      0      4  533308   10  34k 110972  10708      0      0
   2134 root      10 -10 3645212 417552   8416 S   1.0  0.2 188:09.43 ovs-vswit+      0    100  409052    0  42k 409136   8416 3.476g      0
   1435 root      20   0 2705640  66616  28572 S   0.0  0.0  13:44.90 dockerd         0  32668  608428  261  72k  38044  28572      0      0
   1743 root      20   0 2422500  27820   8104 S   0.0  0.0   8:23.19 docker-co+      0   7496  512056   70  22k  19716   8104      0      0
   1503 root      20   0 1276176  31344  20592 S   0.0  0.0   4:08.83 libvirtd        0    472  289924  113  12k  10752  20592      0      0
4121036 root      20   0  623520  11864   4984 S   0.0  0.0   0:01.35 qemu-nbd        0   1480  271352    0 1762   6880   4984      0      0
    826 root      20   0  484100 327264 316964 S   0.0  0.2   0:29.71 systemd-j+      0    120   18464    1  48k  10300   4372      0 312592
   2957 root      20   0  413516   5428   2512 S   0.0  0.0   0:00.37 docker-co+      0   2248   78220   21  490   2916   2512      0      0
   2113 root      20   0  314440  10844   8352 S   0.0  0.0   0:01.15 smbd            0     76    1596   23  21k   2492   8212      0    140

参数的具体解释:

  • VIRT: 进程当前使用的所有虚拟内存总和,/proc/meminfoCommitted_AS参数是所有进程的VIRT总和;
  • RES: Resident Memory Size (KiB), 进程当前使用的物理内存,其中包括与其他进程共享的物理内存,RES = RSan + RSfd + RSsh;
  • SHR: Shared Memory Size (KiB), 进程与其他进程共享使用的物理内存;
  • CODE: 进程当前可执行二进制程序,依赖的库使用的物理内存;
  • DATA: 进程当前通过malloc, mmap等申请的虚拟地址总和,这些空间可能并没有被访问或者写入过,所以并没有被计入RES
  • nMaj: Major Page Fault Count, 一个程序可能占几Mb, 但并不是所有的指令都要同时运行, 有些是在初始化时运行, 有些是在特定条件下才会去运行. 因此linux并不会把所有的指令都从磁盘加载到page内存. 那么当cpu在执行指令时, 如果发现下一条要执行的指令不在实际的物理内存page中时, CPU 就会 raise a page fault, 通知MMU把下面要执行的指令从磁盘加载到物理内存page中。Major Fault通常意味着需要磁盘IO。
  • nMin: Minor Page Fault count, 实际需要的页面已经在物理内存page中了, 只是这个page没有被分配给当前进程, 这时CPU就会raise一个minor page fault, 让MMU把这个page分配给当前进程使用, 因此minor page fault并不需要去访问磁盘。
  • RSan: Resident Anonymous Memory Size (KiB), 当前通过malloc,mmap匿名映射占用的物理内存;
  • RSfd: Resident File-Backed Memory Size (KiB), 当前通过mmap文件映射占用的物理内存;
  • RSsh: Resident Shared Memory Size (KiB), 该进程通过匿名shm*/mmap系统调用获得的共享内存占用的物理内存;
  • RSlk: Resident Locked Memory Size (KiB), 该进程通过mlock/mlockall调用获得的不可以被换出的内存,OpenVSwitch –mlockall参数就支持通锁定内存,保证用来接收/发送缓存不会被交换出去。

DATA,VIRT,RES的区别

观察下面这个程序的top行为:

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <string.h>


int main(){
        int *data, size, count, i;

        printf( "fyi: your ints are %d bytes large\n", sizeof(int) );

        printf( "Enter number of ints to malloc: " );
        scanf( "%d", &size );
        data = malloc( sizeof(int) * size );
        if( !data ){
                perror( "failed to malloc" );
                exit( EXIT_FAILURE );
        }

        printf( "Enter number of ints to initialize: " );
        scanf( "%d", &count );
        for( i = 0; i < count; i++ ){
                data[i] = 1337;
        }

        printf( "I'm going to hang out here until you hit <enter>" );
        while( getchar() != '\n' );
        while( getchar() != '\n' );

        exit( EXIT_SUCCESS );
}
$ ./a.out
fyi: your ints are 4 bytes large
Enter number of ints to malloc: 1250000
Enter number of ints to initialize: 500000

通过top观察到的结果

 PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  SWAP CODE DATA COMMAND
<程序开始>
11129 xxxxxxx   16   0  3628  408  336 S    0  0.0   0:00.00 3220    4  124 a.out
<分配1250000 ints>
11129 xxxxxxx   16   0  8512  476  392 S    0  0.0   0:00.00 8036    4 5008 a.out
<初始化其中500000 ints>
11129 xxxxxxx   15   0  8512 2432  396 S    0  0.0   0:00.00 6080    4 5008 a.out

整理简化下上面的结果

时间点 DATA CODE RES VIRT
before allocation 124 4 408 3628
after 5MB allocation 5008 4 476 8512
after 2MB initialization 5008 4 2432 8512

结论:

  1. 进程通过任何方式获得的虚拟内存空间都会计入VIRT。
  2. DATA是进程通过malloc等方式获得的数据内存空间,这是虚拟空间,可能还并没有使用。
  3. RES是进程的物理空间,被初始化(使用)过的DATA会计入RES。

参考