参考:
    
     http://linuxperf.com/?cat=7
    
   
    /proc/meminfo是了解Linux系统内存使用状况的主要接口,我们的命令行工具free、vmstat等都是通过此接口获得;但是他的信息远比free等命令丰富的多;接下来对此文件进行探究。
    
    内核负责输出/proc/meminfo的源代码是:fs/proc/meminfo.c : meminfo_proc_show();基本的内容如下所示:
   
     
   
    
     MemTotal:
    
    除了系统引导fireware和bios保留的一些内存,kernel本身占用的内存外,其余可供内核支配的内存,就是MemTotal;
    
    
     MemFree:
    
    表示系统尚未使用的内存。[MemTotal-MemFree]就是已被用掉的内存;
    
    
     MemAvailable:
    
    有些应用程序会根据系统的可用内存大小自动调整内存申请的多少,所以需要一个记录当前可用内存数量的统计值,MemFree并不适用,因为MemFree不能代表全部可用的内存,系统中有些内存虽然已被使用但是可以回收的,比如cache/buffer、slab都有一部分可以回收,所以这部分可回收的内存加上MemFree才是系统可用的内存,即MemAvailable。/proc/meminfo中的MemAvailable是内核使用特定的算法估算出来的,要注意这是一个估计值,并不精确!
    
    
     Buffers:
    
    表示块设备(block device)所占用的缓存页,包括:直接读写块设备、以及文件系统元数据(metadata)比如SuperBlock所使用的缓存页。
    
    
     Cached:
    
    表示普通文件所占用的缓存页,即page cache所占用的内存大小。
    
    
     SwapCache:
    
    每一个交换区设备在内存里都有对应的swap cache,可以把swap cache理解为交换区设备的”page cache”:page cache对应的是一个个文件,swap cache对应的是一个个交换区设备。
    
    注:并不是每一个匿名页都在swap cache中,只有以下情形之一的匿名页才在:
    
    匿名页即将被swap-out时会先被放进swap cache,但通常只存在很短暂的时间,因为紧接着在pageout完成之后它就会从swap cache中删除,毕竟swap-out的目的就是为了腾出空闲内存;
    
    【注:参见mm/vmscan.c: shrink_page_list(),它调用的add_to_swap()会把swap cache页面标记成dirty,然后它调用try_to_unmap()将页面对应的page table mapping都删除,再调用pageout()回写dirty page,最后try_to_free_swap()会把该页从swap cache中删除。】
    
    曾经被swap-out现在又被swap-in的匿名页会在swap cache中,直到页面中的内容发生变化、或者原来用过的交换区空间被回收为止。
    
    【注:当匿名页的内容发生变化时会删除对应的swap cache,代码参见mm/swapfile.c: reuse_swap_page()。】
    
    
     附加知识:
    
    Kernel的动态内存分配通过以下几种接口:
    
    alloc_pages/__get_free_page: 以页为单位分配
    
    vmalloc: 以字节为单位分配虚拟地址连续的内存块
    
    slab allocator
    
    kmalloc: 以字节为单位分配物理地址连续的内存块,它是以slab为基础的,使用slab层的general caches — 大小为2^n,名称是kmalloc-32、kmalloc-64等(在老kernel上的名称是size-32、size-64等)。
    
    通过slab层分配的内存会被精确统计,可以参见/proc/meminfo中的slab/SReclaimable/SUnreclaim;
    
    通过vmalloc分配的内存也有统计,参见/proc/meminfo中的VmallocUsed 和 /proc/vmallocinfo;
    
    而通过alloc_pages分配的内存不会自动统计,除非调用alloc_pages的内核模块或驱动程序主动进行统计,否则我们只能看到free memory减少了,但从/proc/meminfo中看不出它们具体用到哪里去了。
    
    
     SReclaimable: s
    
    lab中可回收的部分。调用kmem_getpages()时加上SLAB_RECLAIM_ACCOUNT标记,表明是可回收的,计入SReclaimable,否则计入SUnreclaim。
    
    
     SUnreclaim:
    
    slab中不可回收的部分。
    
    
     Slab:
    
    slab中所有的内存,等于以上两者之和。
    
    附加疑问:为什么 sync;echo 3  > /proc/sys/vm/drop_caches后,free看到的Cached的值未减小?
    
    1.drop_caches接受以下三种值:
    
    1)释放pagecache;        echo 1 > /proc/sys/vm/drop_caches
    
    2)释放可回收的slab对象 (includes dentries and inodes);  echo 2 > /proc/sys/vm/drop_caches
    
    3)释放以上所有;                echo 3 > /proc/sys/vm/drop_caches
    
    2.了解不同版本procps,free命令Cache的计算方法:
    
    1)版本:procps-3.2.8-36;cache值等于/proc/meminfo中的  [Cached];
    
    2)版本:procps-3.3.9-10.1;cache值等于/proc/meminfo的   [Cached + SReclaimable];
    
    3)版本:procps-ng-3.3.10-3;cache值等于/proc/meminfo的 [Cached + Slab]。
    
    3.drop_cache内存回收的原则
    
    1)只回收clean pages,不回收dirty pages;所以如果想回收更多的cache,应该在drop_caches之前先执行sync命令,把dirty pages变成clean pages。
    
    2)   Page cache用于缓存文件里的数据,不仅包括普通的磁盘文件,还包括了tmpfs文件,tmpfs文件系统是将一部分内存空间模拟成文件系统,由于背后并没有对应着磁盘,无法进行paging(换页),只能进行swapping(交换),在执行drop_cache操作的时候tmpfs对应的page cache并不会回收。
    
    3)Linux kernel利用tmpfs实现共享内存,所以共享内存也和tmpfs一样,属于page cache,但又不能被drop_caches回收。
    
    
     共享内存包括
    
    :
    
    SysV shared memory:是通过
    
     shmget
    
    申请的共享内存,用
    
     ipcs -m
    
    或
    
     cat /proc/sysvipc/shm
    
    查看;
    
    POSIX shared memory:是通过
    
     shm_open
    
    申请的共享内存,用
    
     ls /dev/shm
    
    查看;
    
    tmpfs文件系统和devtmpfs文件系统:所有tmpfs文件系统占用的空间都计入共享内存,devtmpfs是/dev文件系统的类型,/dev/下所有的文件占用的空间也属于共享内存。可以用ls和du命令查看。如果文件没有关闭的情况下被删除,空间仍然不会释放,可以用 lsof -a +L1 /<mount_point>” 命令查看。
    
    shared anonymous mmap:通过
    
     mmap
    
    (…MAP_ANONYMOUS|MAP_SHARED…)申请的内存,可以用”
    
     pmap -x
    
    ”或者”
    
     cat /proc/<PID>/maps
    
    ”查看;
    
    注:mmap调用参数如果不是MAP_ANONYMOUS|MAP_SHARED,则不属于tmpfs,比如MAP_ANONYMOUS|MAP_PRIVATE根本不属于page cache而是属于AnonPages,MAP_SHARED属于普通文件,对应的page cache可以回写硬盘并回收。
    
    
     VmallocUsed:
    
    通过vmalloc分配的内存都统计在/proc/meminfo的 VmallocUsed 值中,不过他不光是包括了分配的物理内存,还统计了VM_IOREMAP、VM_MAP等操作的值,譬如VM_IOREMAP是把IO地址映射到内核空间、并未消耗物理内存,所以我们要把它们排除在外。从物理内存分配的角度,我们只关心VM_ALLOC操作,这可以从/proc/vmallocinfo中的vmalloc记录看到:
    
    通过vmalloc分配了多少内存,可以统计/proc/vmallocinfo中的vmalloc记录,例如:
    
    # grep vmalloc /proc/vmallocinfo | awk ‘{total+=$2}; END {print total}’
    
    
     HardwareCorrupted:
    
    当系统检测到内存的硬件故障时,会把有问题的页面删除掉,不再使用,/proc/meminfo中的HardwareCorrupted统计了删除掉的内存页的总大小。
    
    
     Hugepages:
    
    在/proc/meminfo中是被独立统计的,与其它统计项不重叠,既不计入进程的RSS/PSS中,又不计入LRU Active/Inactive,也不会计入cache/buffer。如果进程使用了Hugepages,它的RSS/PSS不会增加。
    
    
     使用Hugepages有三种方式
    
    :
    
    (详见 https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt)
    
    1.mount一个特殊的 hugetlbfs 文件系统,在上面创建文件,然后用mmap() 进行访问,如果要用 read() 访问也是可以的,但是 write() 不行。
    
    2.通过shmget/shmat也可以使用Hugepages,调用shmget申请共享内存时要加上 SHM_HUGETLB 标志。
    
    3.通过 mmap(),调用时指定MAP_HUGETLB 标志也可以使用Huagepages。
    
    用户程序在申请Hugepages的时候,其实是reserve了一块内存,并未真正使用,此时/proc/meminfo中的 HugePages_Rsvd 会增加,而 HugePages_Free 不会减少。
    
    等到用户程序真正读写Hugepages的时候,它才被消耗掉了,此时HugePages_Free会减少,HugePages_Rsvd也会减少!
    
    
     HugePages_Total :
    
    对应内核参数 vm.nr_hugepages,也可以在运行中的系统上直接修改 /proc/sys/vm/nr_hugepages,修改的结果会立即影响空闲内存 MemFree的大小,因为HugePages在内核中独立管理,只要一经定义,无论是否被使用,都不再属于free memory。
    
    
     AnonHugePages:
    
    它与/proc/meminfo的其他统计项有重叠,首先它被包含在AnonPages之中,而且在/proc/<pid>/smaps中也有单个进程的统计,与进程的RSS/PSS是有重叠的,如果用户进程用到了THP,进程的RSS/PSS也会相应增加,这与Hugepages是不同的。
    
    
     LRU相关:
    
    LRU是Kernel的页面回收算法(Page Frame Reclaiming)。Page cache和所有用户进程的内存(kernel stack和huge pages除外)都在LRU lists上。
    
    LRU lists包括如下几种,在/proc/meminfo中都有对应的统计值:
    
    LRU_INACTIVE_ANON  –  对应
    
     Inactive(anon)
    
    
    LRU_ACTIVE_ANON  –  对应
    
     Active(anon)
    
    
    LRU_INACTIVE_FILE  –  对应
    
     Inactive(file
    
    )
    
    LRU_ACTIVE_FILE  –  对应
    
     Active(file)
    
    
    LRU_UNEVICTABLE  –  对应
    
     Unevictable
    
    
    注:
    
    Inactive list里的是长时间未被访问过的内存页,Active list里的是最近被访问过的内存页,LRU算法利用Inactive list和Active list可以判断哪些内存页可以被优先回收。
    
    1)括号中的 anon 表示匿名页(anonymous pages)。
    
    用户进程的内存页分为两种:file-backed pages(与文件对应的内存页),和anonymous pages(匿名页),比如进程的代码、映射的文件都是file-backed,而进程的堆、栈    都是不与文件相对应的、就属于匿名页。file-backed pages在内存不足的时候可以直接写回对应的硬盘文件里,称为page-out,不需要用到交换区(swap);而anonymous  pages在内存不足时就只能写到硬盘上的交换区(swap)里,称为swap-out。
    
    2)括号中的 file 表示 file-backed pages(与文件对应的内存页)。
    
    Unevictable LRU list上是不能pageout/swapout的内存页,包括VM_LOCKED的内存页、SHM_LOCK的共享内存页(又被统计在”Mlocked”中)、和ramfs。在unevictable list出现之前,这些内存页都在Active/Inactive lists上,vmscan每次都要扫过它们,但是又不能把它们pageout/swapout,这在大内存的系统上会严重影响性能,设计unevictable list的初衷就是避免这种情况,参见:https://www.kernel.org/doc/Documentation/vm/unevictable-lru.txt
    
    LRU与/proc/meminfo中其他统计值的关系:
    
    LRU中不包含HugePages_*。
    
    LRU包含了 Cached 和 AnonPages。
    
    
     Shmem:
    
    /proc/meminfo中的Shmem统计的内容包括:
    
    shared memory
    
    tmpfs和devtmpfs。
    
    注:所有tmpfs类型的文件系统占用的空间都计入共享内存,devtmpfs是/dev文件系统的类型,/dev/下所有的文件占用的空间也属于共享内存。可以用ls和du命令查看。如果文件在没有关闭的情况下被删除,空间仍然不会释放,shmem不会减小,可以用 “lsof -a +L1 /<mount_point>” 命令列出这样的文件。
    
    因为shared memory在内核中都是基于tmpfs实现的,参见:                        https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt
    
    也就是说它们被视为基于tmpfs文件系统的内存页,既然基于文件系统,就不算匿名页,所以不被计入/proc/meminfo中的AnonPages,而是被统计进了:
    
    
     AnonPages:
    
    前面提到用户进程的内存页分为两种:file-backed pages(与文件对应的内存页),和anonymous pages(匿名页)。Anonymous pages(匿名页)的数量统计在/proc/meminfo的AnonPages中。
    
    以下是几个事实,有助于了解Anonymous Pages:
    
    所有page cache里的页面(Cached)都是file-backed pages,不是Anonymous Pages。”Cached”与”AnoPages”之间没有重叠。
    
    注:shared memory 不属于 AnonPages,而是属于Cached,因为shared memory基于tmpfs,所以被视为file-backed、在page cache里,前面说过。
    
    mmap private anonymous pages属于AnonPages(Anonymous Pages),而mmap shared anonymous pages属于Cached(file-backed pages),因为shared anonymous mmap也是基于tmpfs的。
    
    Anonymous Pages是与用户进程共存的,一旦进程退出,则Anonymous pages也释放,不像page cache即使文件与进程不关联了还可以缓存。
    
    AnonPages统计值中包含了Transparent HugePages (THP)对应的 AnonHugePages 。
    
    
     Mlocked:
    
    “Mlocked”统计的是被mlock()系统调用锁定的内存大小。被锁定的内存因为不能pageout/swapout,会从Active/Inactive LRU list移到Unevictable LRU list上。也就是说,当”Mlocked”增加时,”Unevictable”也同步增加,而”Active”或”Inactive”同时减小;当”Mlocked”减小的时候,”Unevictable”也同步减小,而”Active”或”Inactive”同时增加。
    
    “Mlocked”并不是独立的内存空间,它与以下统计项重叠:LRU Unevictable,AnonPages,Shmem,Mapped等。
    
    
     Mapped:
    
    上面提到的用户进程的file-backed pages就对应着/proc/meminfo中的”Mapped”。Page cache中(“Cached”)包含了文件的缓存页,其中有些文件当前已不在使用,page cache仍然可能保留着它们的缓存页面;而另一些文件正被用户进程关联,比如shared libraries、可执行程序的文件、mmap的文件等,这些文件的缓存页就称为mapped。/proc/meminfo中的”Mapped”就统计了page cache(“Cached”)中所有的mapped页面。”Mapped”是”Cached”的子集。
    
    因为Linux系统上shared memory & tmpfs被计入page cache(“Cached”),所以被attached的shared memory、以及tmpfs上被map的文件都算做”Mapped”。
    
    进程所占的内存页分为anonymous pages和file-backed pages,理论上应该有:
    
    【所有进程的PSS之和】 == 【Mapped + AnonPages】。
    
    虽然两者很接近,却总是无法精确相等,我猜也许是因为进程始终在变化、采集的/proc/[1-9]*/smaps以及/proc/meminfo其实不是来自同一个时间点的缘故!
   
    
     DirectMap(DirectMap4k/DirectMap2M/DirectMap1G)
    
   
/proc/meminfo中的DirectMap所统计的不是关于内存的使用,而是一个反映TLB效率的指标。TLB(Translation Lookaside Buffer)是位于CPU上的缓存,用于将内存的虚拟地址翻译成物理地址,由于TLB的大小有限,不能缓存的地址就需要访问内存里的page table来进行翻译,速度慢很多。为了尽可能地将地址放进TLB缓存,新的CPU硬件支持比4k更大的页面从而达到减少地址数量的目的, 比如2MB,4MB,甚至1GB的内存页,视不同的硬件而定。”DirectMap4k”表示映射为4kB的内存数量, “DirectMap2M”表示映射为2MB的内存数量,以此类推。所以DirectMap其实是一个反映TLB效率的指标。
    
     用户进程的内存主要有三种统计:
    
    
    围绕LRU进行统计
    
    【(Active + Inactive + Unevictable) + (HugePages_Total * Hugepagesize)】
    
    2.   围绕Page Cache进行统计
    
    当SwapCached为0的时候,用户进程的内存总计如下:
    
    【(Cached + AnonPages + Buffers) + (HugePages_Total * Hugepagesize)】
    
    当SwapCached不为0的时候,以上公式不成立,因为SwapCached可能会含有Shmem,而Shmem本来被含在Cached中,一旦swap-out就从Cached转移到了SwapCached,可是    我们又不能把SwapCached加进上述公式中,因为SwapCached虽然不与Cached重叠却与AnonPages有重叠,它既可能含有Shared memory又可能含有Anonymous Pages。
    
    3.  围绕RSS/PSS进行统计
    
    把/proc/[1-9]*/smaps 中的 Pss 累加起来就是所有用户进程占用的内存,但是还没有包括Page Cache中unmapped部分、以及HugePages,所以公式如下:
    
    ΣPss + (Cached – mapped) + Buffers + (HugePages_Total * Hugepagesize)
    
    参考:http://linuxperf.com/?p=142
   
 
