说明:本文大量参考了文末链接给出的几篇文章。

【free命令】

在查看Linux的内存使用状况的手段中,free命令恐怕是最为人熟知,也是被使用的最多的,毕竟简洁方便嘛。来看下free命令的输出:

第一行"Mem"中,"total"是总的物理内存,"free"是空闲内存,"shared"是基于tmpfs的共享内存,"buff/cache"主要就是page cache("shared"属于"buff/cache"的一部分,原因将在后面介绍)。而"used"是根据空闲内存和page cache计算出来的结果:

used=total−free−buffers/cacheused = total - free - buffers/cache

"available"是内核3.14版本加入的,包括空闲内存和可回收的内存,但"available"的值实际是小于"free+buffer/cache"的,具体的原因请参考这篇文章

第二行"Swap"中,"total"是磁盘中划定的swap space大小,由于我的机器现在没有跑什么任务,内存很充足,不需要将页面swap out,所以swap space的"used"为0。

【meminfo】

对应关系

free命令的输出信息有限,如果要获取更详细的内容,应查看"/proc/meminfo"。其实啊,"free"输出的结果,就是从"meminfo"中提取出来后进一步加工过的,这从代码的实现也不难发现:

parse_meminfo --> xfopen_for_read("/proc/meminfo");openat(AT_FDCWD, "/proc/meminfo", O_RDONLY) = 3

因此,两者存在很多直接的对应关系(以Red Hat Enterprise Linux 7.1为例):

表 1

至于"meminfo"中数据的计算方法,则是在"/fs/proc/meminfo.c"文件中。

Buffers和Cached

当通过ext3/ext4等文件系统去访问file时,产生的page cache就是"Cached",而直接访问"/dev/sda1"这种基于裸分区的file时,产生的缓存就是"Buffers",可通过如下实验验证这一点:

LRU和Shmem

"meminfo"中记录了对用于内存回收的LRU链表的统计信息,其中"Active"等于"Active(anon)"+"Active(file)","Inactive"等于"Inactive(anon)"+"Inctive(file)",但是好像"AnonPages"并不等于"Active(anon)"+"Inactive(anon)","Buffers"+"Cached"也并不等于"Active(file)"+"Inactive(file)"?

差值就在Shmem里,基于内存文件系统tmpfs的share memory比较特殊,从文件系统的角度,它是属于page cache的("buff/cache"中的"cache"部分),这可以通过以下的实验得到验证:新增一个10MB大小的tmpfs的文件系统镜像,可以看到"Cached"和"Shmem"的值都增大了10MB左右,而"Buffers"的值没有变化。

但是它又没有对应磁盘上的文件,在回收的时候,其页面内容需要像anonymous page一样swap out到磁盘保存起来,因此它只能放在Active(anon)/Inactive(anon)中。

Mapped

因为page cache中的页面即便已经不再被任一进程使用,但考虑到以后的某个时刻可能还会用到,在内存相对充足的情况下,这些页面将继续驻留在内存而不会被释放,但此时它们属于没有进程映射的状态。

为了统计Page Cache中被进程正在使用的内存,增加了一个"Mapped"信息,它属于page cache的一部分(可视作“子集”),其值永远小于(或等于)page cache的大小。对于Shmem正在被进程关联的内存,也属于"Mapped"范畴。

Swap Cache

"SwapCached"表示的是swap cache的大小,因为swap cache是用于anonymous pages的换入和换出的,因此它属于"AnonPages"的一部分。虽然"SwapCached"名字中有一个"Cached",但它和表示page cache的"Cached"本质上毫无关联。

Slab

还有一个是关于slab cache的统计信息(由"Slab"表示),它分为可回收的"SReclaimable"部分和不可回收的"SUnreclaim"部分。其中可回收的部分主要包括inode cache和dentry cache,它们除了在内存资源紧张时会被自动回收,还可手动强制回收,详情请参考这篇文章

free命令输出中的"buff/cache"通常包含"Slab"(比如上面的表1),但"meminfo"中的"Cached"并不包含它:

可通过以下实验验证这一点:

【内核内存的计算】

根据内核的地址空间构成,可以得知内核运行时所占据的内存由哪几部分组成,获取各个部分内存的使用情况的方法分别如下:

(1) "code"代码段, "rwdata"可读写数据段, "rodata"只读数据段, "bss"未初始化数据段。查看方式的方式很简单,因为它包含在了内核的启动信息中,所以直接使用"dmesg |grep memory"即可获取。因为"init"段所使用的内存在启动完成后就释放掉了,所以不算在内。

(2) kernel的"stack",可通过"meminfo"中的"KernelStack"查询。

(3) kernel的"heap",根据获取路径的不同,包括以下几种情况:

通过kmalloc()申请的较小的内存。此时,kmalloc将基于slab分配器获取内存,因此可通过"meminfo"中的"Slab"查询。直接调用alloc_page()或__get_free_page()分配的内存,包括通过kmalloc()申请较大内存时,以及使用vmalloc()时。对应vmalloc,可经" /proc/vmallocinfo"查看其分配和映射情况,并计算总和。

(4) 进程的Page Tables是由内核统一管理的,可通过"meminfo"中的"PageTable"得到所有进程使用的页表占据的总内存。

如果要查看每个进程各自的页表所消耗的内存,则可根据进程号PID,由"/proc/[PID]/status"中的"VmPTE"获取(对于64位系统还有"VmPMD")。所有的"VmPTE"加上"VmPMD",大致就等于"PageTable"的总大小。

【进程内存的计算】

一个进程所使用的内存可通过PSS和RSS来衡量,这两者的信息都记录在" /proc/[PID]/smaps"中。

进程PSS的总大小基本等于"meminfo"中"Mapped"加上"AnonPages"的大小:

上文讲过,"Mapped"是page cache中正在被进程使用的内存部分,而"AnonPages"与进程解除关联后,在内存中就没有存在的必要了,因此它所包含的都是当前正在被使用的,两者之后就是进程使用的总内存。

参考:

/PROC/MEMINFO之谜 FREE命令显示的BUFFERS与CACHED的区别 解读DMESG中的内存初始化信息 怎样统计所有进程总共占用多少内存Interpreting /proc/meminfo and free output
分类: 软件分享 标签: 暂无标签

评论

暂无评论数据

暂无评论数据

目录