大家好,欢迎来到IT知识分享网。
本文转载自http://linuxperf.com/?p=142
/proc/meminfo是了解Linux系统内存使用状况的主要接口,我们最常用的”free”、”vmstat”等命令就是通过它获取数据的 ,/proc/meminfo所包含的信息比”free”等命令要丰富得多,然而真正理解它并不容易,比如我们知道”Cached”统计的是文件缓存页,manpage上说是”In-memory cache for files read from the disk (the page cache)”,那为什么它不等于[Active(file)+Inactive(file)]?AnonHugePages与AnonPages、HugePages_Total有什么联系和区别?很多细节在手册中并没有讲清楚,本文对此做了一点探究。
MemTotal
MemFree
MemAvailable
内存黑洞
追踪Linux系统的内存使用一直是个难题,很多人试着把能想到的各种内存消耗都加在一起,kernel text、kernel modules、buffer、cache、slab、page table、process RSS…等等,却总是与物理内存的大小对不上,这是为什么呢?因为Linux kernel并没有滴水不漏地统计所有的内存分配,kernel动态分配的内存中就有一部分没有计入/proc/meminfo中。
- 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中看不出它们具体用到哪里去了。比如在VMware guest上有一个常见问题,就是VMWare ESX宿主机会通过guest上的Balloon driver(vmware_balloon module)占用guest的内存,有时占用得太多会导致guest无内存可用,这时去检查guest的/proc/meminfo只看见MemFree很少、但看不出内存的去向,原因就是Balloon driver通过alloc_pages分配内存,没有在/proc/meminfo中留下统计值,所以很难追踪。
内存都到哪里去了?
注:page cache比较特殊,很难区分是属于kernel还是属于进程,其中被进程mmap的页面自然是属于进程的了,而另一些页面没有被mapped到任何进程,那就只能算是属于kernel了。
1. 内核
下面讨论的都是/proc/meminfo中所统计的部分。
1.1 SLAB
- SReclaimable: slab中可回收的部分。调用kmem_getpages()时加上SLAB_RECLAIM_ACCOUNT标记,表明是可回收的,计入SReclaimable,否则计入SUnreclaim。
- SUnreclaim: slab中不可回收的部分。
- Slab: slab中所有的内存,等于以上两者之和。
1.2 VmallocUsed
一些driver以及网络模块和文件系统模块可能会调用vmalloc,加载内核模块(kernel module)时也会用到,可参见 kernel/module.c。
1.3 kernel modules (内核模块)
注:我们可以在 /sys/module/<module-name>/ 目录下分别看到coresize和initsize的值。
所以结论是kernel module所占用的内存包含在/proc/vmallocinfo的统计之中,不必再去计算”lsmod”的结果了,而且”lsmod”也不准。
1.4 HardwareCorrupted
当系统检测到内存的硬件故障时,会把有问题的页面删除掉,不再使用,/proc/meminfo中的HardwareCorrupted统计了删除掉的内存页的总大小。相应的代码参见 mm/memory-failure.c:memory_failure ()。
1.5 PageTables
Page Table用于将内存的虚拟地址翻译成物理地址,随着内存地址分配得越来越多,Page Table会增大,/proc/meminfo中的PageTables统计了Page Table所占用的内存大小。
1.6 KernelStack
每一个用户线程都会分配一个kernel stack(内核栈),内核栈虽然属于线程,但用户态的代码不能访问,只有通过系统调用(syscall)、自陷(trap)或异常(exception)进入内核态的时候才会用到,也就是说内核栈是给kernel code使用的。在x86系统上Linux的内核栈大小是固定的8K或16K(可参阅我以前的文章: 内核栈溢出 http://ju.outofmemory.cn/entry/ )。
Kernel stack(内核栈)是常驻内存的,既不包括在LRU lists里,也不包括在进程的RSS/PSS内存里,所以我们认为它是kernel消耗的内存。统计值是/proc/meminfo的KernelStack。
1.7 Buffers
Buffers统计的是直接访问块设备时的缓冲区的总大小,有时候对文件系统元数据的操作也会用到buffers。这部分内存不好直接对应到某个用户进程,应该算作kernel占用。
1.8 Bounce
有些老设备只能访问低端内存,比如16M以下的内存,当应用程序发出一个I/O 请求,DMA的目的地址却是高端内存时(比如在16M以上),内核将在低端内存中分配一个临时buffer作为跳转,把位于高端内存的缓存数据复制到此处。这种额外的数据拷贝被称为”bounce buffering”,会降低I/O 性能。大量分配的bounce buffers 也会占用额外的内存。
2. 用户进程
/proc/meminfo统计的是系统全局的内存使用状况,单个进程的情况要看/proc/<pid>/下的smaps等等。
2.1 Hugepages
注:不要把 Transparent HugePages (THP)跟 Hugepages 搞混了,THP的统计值是/proc/meminfo中的”AnonHugePages”,在/proc/<pid>/smaps中也有单个进程的统计,这个统计值与进程的RSS/PSS是有重叠的,如果用户进程用到了THP,进程的RSS/PSS也会相应增加,这与Hugepages是不同的。
- mount一个特殊的 hugetlbfs 文件系统,在上面创建文件,然后用mmap() 进行访问,如果要用 read() 访问也是可以的,但是 write() 不行。
- 通过shmget/shmat也可以使用Hugepages,调用shmget申请共享内存时要加上 SHM_HUGETLB 标志。
- 通过 mmap(),调用时指定MAP_HUGETLB 标志也可以使用Huagepages。
2.2 AnonHugePages
AnonHugePages统计的是Transparent HugePages (THP),THP与Hugepages不是一回事,区别很大。
上一节说过,Hugepages在/proc/meminfo中是被独立统计的,与其它统计项不重叠,既不计入进程的RSS/PSS中,又不计入LRU Active/Inactive,也不会计入cache/buffer。如果进程使用了Hugepages,它的RSS/PSS不会增加。
- mount时加上”huge=always”等选项
- 通过/sys/kernel/mm/transparent_hugepage/shmem_enabled来控制
2.3 LRU
- Inactive list里的是长时间未被访问过的内存页,Active list里的是最近被访问过的内存页,LRU算法利用Inactive list和Active list可以判断哪些内存页可以被优先回收。
- 括号中的 anon 表示匿名页(anonymous pages)。
用户进程的内存页分为两种:file-backed pages(与文件对应的内存页),和anonymous pages(匿名页),比如进程的代码、映射的文件都是file-backed,而进程的堆、栈都是不与文件相对应的、就属于匿名页。file-backed pages在内存不足的时候可以直接写回对应的硬盘文件里,称为page-out,不需要用到交换区(swap);而anonymous pages在内存不足时就只能写到硬盘上的交换区(swap)里,称为swap-out。 - 括号中的 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中不包含HugePages_*。
- LRU包含了 Cached 和 AnonPages。
2.4 Shmem
- shared memory
- tmpfs。
- SysV shared memory [shmget etc.]
- POSIX shared memory [shm_open etc.]
- shared anonymous mmap [ mmap(…MAP_ANONYMOUS|MAP_SHARED…)]
- Cached (i.e. page cache)
- Mapped (当shmem被attached时候)
- Inactive(anon) 或 Active(anon)
注:虽然它们在LRU中被放进了anon list,但是不会被计入 AnonPages。这是shared memory & tmpfs比较拧巴的一个地方,需要特别注意。 - 或 unevictable (如果被locked的话)
2.5 AnonPages
- 所有page cache里的页面(Cached)都是file-backed pages,不是Anonymous Pages。
注:shared memory 不属于 AnonPages,而是属于Cached,因为shared memory基于tmpfs,所以被视为file-backed、在page cache里,上一节解释过。 - mmap private anonymous pages属于AnonPages(Anonymous Pages),而mmap sharedanonymous pages属于Cached(file-backed pages),因为shared anonymous mmap也是基于tmpfs的,上一节解释过。
- Anonymous Pages是与用户进程共存的,一旦进程退出,则Anonymous pages也释放,不像page cache即使文件与进程不关联了还可以缓存。
- AnonPages统计值中包含了Transparent HugePages (THP)对应的 AnonHugePages 。参见:
2.6 Mapped
上面提到的用户进程的file-backed pages就对应着/proc/meminfo中的”Mapped”。Page cache中(“Cached”)包含了文件的缓存页,其中有些文件当前已不在使用,page cache仍然可能保留着它们的缓存页面;而另一些文件正被用户进程关联,比如shared libraries、可执行程序的文件、mmap的文件等,这些文件的缓存页就称为mapped。
/proc/meminfo中的”Mapped”就统计了page cache(“Cached”)中所有的mapped页面。
2.7 Cached
- Cached不仅包括mapped,也包括unmapped的页面,当一个文件不再与进程关联之后,原来在page cache中的页面并不会立即回收,仍然被计入Cached,还留在LRU中,但是 Mapped 统计值会减小。【ummaped = (Cached – Mapped)】
- Cached包含tmpfs中的文件,POSIX/SysV shared memory,以及shared anonymous mmap。
注:POSIX/SysV shared memory和shared anonymous mmap在内核中都是基于tmpfs实现的,参见:
https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt
2.8 SwapCached
- 匿名页即将被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()。】
2.9 Mlocked
其它问题
DirectMap
Dirty pages到底有多少?
为什么【Active(anon)+Inactive(anon)】不等于AnonPages?
- 如果shmem或anonymous pages被mlock的话,就不在Active(non)或Inactive(anon)里了,而是到了Unevictable里,以上等式就不平衡了;
- 当anonymous pages准备被swap-out时,分几个步骤:先被加进swap cache,再离开AnonPages,然后离开LRU Inactive(anon),最后从swap cache中删除,这几个步骤之间会有间隔,而且有可能离开AnonPages就因某些情况而结束了,所以在某些时刻以上等式会不平衡。
【注:参见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中删除。】
为什么【Active(file)+Inactive(file)】不等于Mapped?
- 因为LRU Active(anon)和Inactive(anon)中包含unmapped页面;
- Mapped中包含shared memory & tmpfs,这部分内存被计入了LRU Active(anon)或Inactive(anon)、而不在Active(file)和Inactive(file)中。
Linux的内存都用到哪里去了?
kernel内存的统计方式应该比较明确,即
- 注1:VmallocUsed其实不是我们感兴趣的,因为它还包括了VM_IOREMAP等并未消耗物理内存的IO地址映射空间,我们只关心VM_ALLOC操作,(参见1.2节),所以实际上应该统计/proc/vmallocinfo中的vmalloc记录,例如(此处单位是byte):
- 注2:kernel module的内存被包含在VmallocUsed中,见1.3节。
- 注3:X表示直接通过alloc_pages/__get_free_page分配的内存,没有在/proc/meminfo中统计,不知道有多少,就像个黑洞。
用户进程的内存主要有三种统计口径:
- [1]围绕LRU进行统计
【(Active + Inactive + Unevictable) + (HugePages_Total * Hugepagesize)】 - [2]围绕Page Cache进行统计
当SwapCached为0的时候,用户进程的内存总计如下:
【(Cached + AnonPages) + (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) + (HugePages_Total * Hugepagesize)
所以系统内存的使用情况可以用以下公式表示:
- MemTotal = MemFree +【Slab+ VmallocUsed + PageTables + KernelStack + Buffers + HardwareCorrupted + Bounce + X】+【Active + Inactive + Unevictable + (HugePages_Total * Hugepagesize)】
- MemTotal = MemFree +【Slab+ VmallocUsed + PageTables + KernelStack + Buffers + HardwareCorrupted + Bounce + X】+【Cached + AnonPages + (HugePages_Total * Hugepagesize)】
- MemTotal = MemFree +【Slab+ VmallocUsed + PageTables + KernelStack + Buffers + HardwareCorrupted + Bounce + X】+【ΣPss + (Cached – mapped) + (HugePages_Total * Hugepagesize)】
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/122777.html