点击上方“小强的进阶之路”,选择“星标”公众号
优质文章,及时送达
预计阅读时间: 18分钟
本文主要分析 Linux 系统内存统计的一些指标以及进程角度内存使用监控的一些方法。
开始阅读这篇文章前,请先简单阅读下面的几篇文章。
想必这几篇文章过后,基本概念就不需要再赘述了。所以下文直接就找一台 Intel x86_64 架构下安装了 64bit Linux 系统的服务器作为例进行相关的实验和结果分析。Linux 的内存管理从物理内存管理到虚拟内存管理涉及的概念和统计项实在太多,本文从实用和系统运维的角度出发,只列举一些最实用的统计。
上面的背景介绍文章把内存相关的基础概念讲的差不多了,这里不再赘述。本文定位是内存统计,所以从最基础的内存统计的命令—free命令开始。执行free命令,可以看到如下的输出:

纵向是内存和Swap分区,横向是统计项。纵向的含义以及Swap不需要解释,我们看横向的统计项:
/proc/meminfo 获取的)tmpfsbuffers和cache使用的内存之和这台机器的系统和内核稍微新一点,这个输出可能和你看到的不一样,早先的free命令的输出是这样:

这里的shared为0,因为这台服务器没用共享内存。这里多解释下 -/+ buffer/cache 这行,字面意思就是used - buffers/cache和used + buffers/cache。前者指的是从应用程序角度系统被用掉了多少内存,后者指的是从应用程序角度看系统还有多少内存能用。听起来很复杂,其实说白了就是因为buffers和cached可以被释放出来,多几个指标看看系统还能用多少内存而已。
下面用几个公式来解释这个输出:
# 内存总量 = 已使用内存 + 空闲内存
`total` = `used` + `free`
# 系统被用掉的内存
`-buffers/cache` = `used` - `buffers` - `cached`
# 系统还能用的内存
`+buffers/cache` = `free` + `buffers` + `cached`
# 所以,其实还有下面的公式
`total` = `-buffers/cache` + `+buffers/cache`buffers/cached不是100%都能释放出来使用的,上面的“可用内存”其实就是个近似值。最上面新版本系统的输出中有一个available项目表示可用内存,值小于 free + buff/cache,内核 3.14 之后支持该特性(虽然也不是绝对意义上的精确的可用内存大小,囧)。
这里稍微多说一点buffers 和cached。Linux 2.4.10 内核之前,磁盘的缓存有两种,即 Buffer Cache和 Page Cache。前者缓存管理磁盘文件系统时读取的块,后者存放访问具体文件内容时生成的页。在 2.4.10 之后,Buffer Cache这个概念就不存在了,这些数据被放在Page Cache中(这种 Page 被称为 Buffer Pages)。
简而言之,现在磁盘的 cache 只有 Page Cache 一种,在Page Cache中,有一种Page叫Buffer Page,这种Page都与一个叫buffer_head的数据结构关联,这些页也就在内存统计中用buffers这个指标来单独统计了。
很多命令的内存统计都是从/proc/meminfo读取的。鉴于 /proc/meminfo 的 man 文档(man proc)写的实在不够清晰,很多条目居然还是To be documented状态,所以这里逐一列举出来常见的统计项解释一下。
首先明确一点,内核目前并没有绝对精确的统计所有的内存使用量,比如alloc_pages接口申请的内存不一定被统计在内(除非所有调用 alloc_pages 的代码主动进行统计,如果某些不讲究的驱动程序没有主动统计的话统计值就肯定对不上了)。
先看这三项全局统计:
RAM减去保留的以及内核代码占用的,系统启动后一般固定不变)MemFree不代表所有可用的内存,Cache/Buffer、Slab 均有部分可以临时释放的内存要计算在内)用户进程的内存页分为两种:
File-backed Pages), 比如程序文件、读取文件时数据对应的缓存页Anonymous Pages),比如进程的堆、栈等分配的内存所有Page Cache里的页面都是File-backed Pages,File-backed Pages在内存不足的时候可以直接写回对应的硬盘文件里,即Page-out。而Anonymous Pages在内存不足时就只能写到硬盘上的交换区Swap里来释放内存,称之为Swap-out。
Anonymous Pages与用户进程共存,进程退出则Anonymous pages释放,而Page Cache即使在进程退出后还可以缓存。
下面是磁盘缓存相关的统计项:
meta信息如SuperBlock等,直接读写块设备产生的缓存也统计在这里(例如dd命令)Page cache)Swap中包含的确定要被换出,但是尚未写入物理交换区的匿名内存页Swap总大小Swap的free大小以下几项和内核的页面回收算法(Page Frame Reclaiming)相关,Page Cache和所有用户进程的内存(除内核栈和HugePages外)都在相关的LRU Lists上。内核在 2.6 以前就引入了增强的LRU算法来解决朴素的LRU算法完全不考虑使用频率的问题。具体的Active 链表和Inactive 链表的使用详情请参阅其他资料。
Active 链表中的匿名页(Anonymous Pages)部分Inactive 链表中的匿名页(Anonymous Pages)部分Active 链表中的File-backed Pages部分Inactive 链表中的File-backed Pages部分Unevictable 链表,其中包括VM_LOCKED的内存页、SHM_LOCK的共享内存页(也统计在Mlocked中)、和Ramfs等mlock() 系统调用锁定的内存大小共享内存在 Linux 中细分的话可以分为以下几种:
shmgetshm_openmmap(MAP_ANONYMOUS | MAP_SHARED)共享内存在内核中都是 基于tmpf机制实现 的。因为基于文件系统所以就不能算是匿名页,不能计入AnonPages的统计项,而只能计入Cached和Mapped统计项。但是,tmpfs 背后并没有真实的磁盘文件存在,如果想要被临时释放出来,只能通过Swap的方式,所以内存页被链接到了Inactive(anon)和Active(anon)里。
也就是说,共享内存的页面属于File-backed Pages,但是被放在Inactive(anon)和Active(anon)链表里,统计也不算在AnonPages里,而是算在Cached和Mapped里。特别地,如果这些页被mlock()的话,就放在 Unevictable链里并计算在内。所以从数值上看,Inactive(anon)项 + Active(anon)项 不等于AnonPages项,因为前者包括共享内存的部分。Active(file)项 + Inactive(file)项 也不等于 Mapped项,因为前者中包括Unmapped的内存,后者还包含共享内存的部分(这部分在 Inactive(anon)和Active(anon)里)。
这里有一个情况要注意,与文件关联的页也有可能是匿名页(MAP_PRIVATE 映射的页面被修改时会产生一个匿名页拷贝),会被算到AnonPages里。
与此相关的相关的统计项有:
Anonymous pages)的大小,同时也包含Transparent HugePages (THP)对应的 AnonHugePagesMapped统计了Cached中所有的Mapped页面,是Cached的子集(满足Cached - Mapped = Unmapped)。共享内存、可执行程序的文件、动态库、mmap的文件等都统计在这里Shared Memory、tmpfs和devtmpfs注意 Linux 的内存是真正使用时才分配的,所以注意这里的大小都是已分配的大小,而不是程序里申请的大小。
下面都是内核使用的内存相关的统计项:
Slab结构使用的大小(就是那个Slab分配器占用的)Slab里面可回收的部分(调用 kmem_getpages() 时带有 SLAB_RECLAIM_ACCOUNT 标的)Slab里面无法回收的大小,等于 Slab项 - SReclaimable项Kernel Stack,系统调用syscall、trap、exception后进入内核态执行代码时候使用)之前说过,HugePages 是独立统计的,如果进程使用了 HugePages,是不会计入自身的RSS/PSS 的。注意下面的 AnonHugePages 指的是透明大页(THP,Transparent HugePages),THP是统计在进程的RSS/PSS里的,要注意区别。下面是相关的统计项:
/proc/sys/vm/nr_hugepages,可以动态改/proc/sys/vm/nr_hugepages的大小, 最大值由 /proc/sys/vm/nr_overcommit_hugepages 限制先介绍几个通用概念:
Virtual Set Size,虚拟内存大小,包含共享库占用的全部内存,以及分配但未使用内存Resident Set Size,实际使用物理内存,包含了共享库占用的全部内存Proportional Set Size,实际使用的物理内存,共享库占用的内存按照进程数等比例划分Unique Set Size,进程独自占用的物理内存,不包含共享库占用的内存在/proc/{pid}/smaps文件对应每个进程的详细内存分段统计。截取一部分:

下面分别解释下含义:
mapping size)RAM的内存大小(包括共享库的大小,不包括已经交换出去的页面)Clean内存的大小Dirty内存的大小Clean内存的大小Dirty内存的大小Swap的大小MMU页大小mlock()的内存大小可以看到Rss这个指标实际上是包含了共享库的大小的,不同的进程会共享这个映射的,如果想通过累加这个值来计算所有进程用到的内存的话就不准确了,而Pss把共享库的大小均摊给了所有用到映射了这个库的进程,所以累加起来就不会重复计算共享库大小了。
P.S. 最新的内核文档提到了要加
smaps_rollup这个统计,支持Pss_Anon、Pss_File和Pss_Shmem三个分类统计,这个在进程级别看,用到内存就很清晰了。
我们可以累加一下这个值看看某进程用到的内存总和:

注意单位是KB,所以这里进程用到的内存是 1.17 GB 左右。
这是个使用共享内存作为存储的服务,所以这是符合预期的。如果想要看排除共享内存的部分,那要看Anonymous部分的总和:

所以实际匿名内存使用是 63 MB 左右。
top命令中关于内存使用的统计:

内存相关的统计有VIRT、RES、SHR、SWAP、CODE、DATA、USED
Virtual Memory Size,虚拟内存大小,包括所有代码、数据和共享库,以及已交换的页面和已映射但未使用的内存Resident Memory Size,驻留内存大小,共享的内存比如动态库也会计算在内Shared Memory Size,共享的内存大小,并非所有共享的内存都是常驻的常用的还有这些:vmstat、sar、slabtop、kmstat、ps、prstat、pmap等等。懒得写了,有问题看man文档得了。
[1] Understanding the Linux Kernel, Daniel Plerre Bovet / Marco Cesati, 2005-11
[2] Professional Linux Kernel Architecture, Wolfgang Mauerer, 2008-10-13
[3] Systems Performance: Enterprise and the Cloud, Brendan Gregg, 2013-10-26
[4] https://raw.githubusercontent.com/torvalds/linux/master/Documentation/filesystems/proc.txt
[5] https://en.wikipedia.org/wiki/Resident_set_size
[6] https://en.wikipedia.org/wiki/Proportional_set_size
[7] https://en.wikipedia.org/wiki/Unique_set_size
End
明天见(。・ω・。)ノ♡
本文分享自 MoziInnovations 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!