目录
一、buffer/cache
https://www.cnblogs.com/M18-BlankBox/p/5326484.html
https://www.cnblogs.com/sky-heaven/p/13743693.html
1.buffer和cache都是为了解决互访的两种设备存在速率差异,使磁盘的IO的读写性能或cpu更加高效,减少进程间通信等待的时间
2.buffer:缓冲区-用于存储速度不同步的设备或优先级不同的设备之间传输数据,通过buffer可以减少进程间通信需要等待的时间,当存储速度快的设备与存储速度慢的设备进行通信时,存储快的设备先把数据缓存到buffer上,等到系统统一把buffer上的数据写到速度慢的设备上。常见的有把内存的数据往磁盘进行写操作,这时你可以查看一下buffers
3.cache:缓存区-用于对读取速度比较严格,却因为设备间因为存储设备存在速度差异,而不能立刻获取数据,这时cache就会为了加速缓存一部分数据。常见的是CPU和内存之间的数据通信,因为CPU的速度远远高于主内存的速度,CPU从内存中读取数据需等待很长的时间,而Cache保存着CPU刚用过的数据或循环使用的部分数据,这时Cache中读取数据会更快,减少了CPU等待的时间,提高了系统的性能。
二、/proc/meminfo
kernel /proc/meminfo
redhat /proc/meminfo
meminfo文件详解
linux内存占用分析之meminfo
cat /proc/meminfo
MemTotal: 16393748 kB
MemFree: 12984548 kB
MemAvailable: 13358292 kB
Buffers: 11076 kB //buffers + cached的值就是可以使用的磁盘告诉缓存的大小。
Cached: 724524 kB //buffers + cached = Active(file) + Inactive(file) + Shmem
SwapCached: 0 kB
Active: 2014956 kB //Active(anon) + Active(file)
Inactive: 676260 kB //Inactive(anon) + Inactive(file)
Active(anon): 1955948 kB
Inactive(anon): 33100 kB
Active(file): 59008 kB
Inactive(file): 643160 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 40 kB
Writeback: 0 kB
AnonPages: 1955648 kB
Mapped: 516224 kB
Shmem: 33400 kB
KReclaimable: 37024 kB
Slab: 103712 kB
SReclaimable: 37024 kB
SUnreclaim: 66688 kB
KernelStack: 11520 kB
PageTables: 10708 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 8196872 kB
Committed_AS: 5143360 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 0 kB
VmallocChunk: 0 kB
Percpu: 4192 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 0 kB
DirectMap4k: 107312 kB
DirectMap2M: 6154240 kB
DirectMap1G: 12582912 kB
Shmem
tmpfs所使用的内存.
tmpfs即利用物理内存来提供RAM磁盘的功能。在tmpfs上保存文件时,文件系统会暂时将它们保存到磁盘高速缓存上,因此它是属于磁盘高速缓存对应的"buffers+cached"一类。 但是由于磁盘上并没有与之对应的内容,因此它并记录在File-backed内存对应的LRU列表上,而是记录在匿名内存的LRU表上。 这就是 buffers + cached = Active(file) + Inactive(file) + Shmem 公式的由来
SwapCached
记录在交换缓存上的内容容量。
所谓交换缓存是指,在换入某个内存页之后,物理磁盘上的交换空间依然保留同样的数据,这样的内存页会记录在"交换缓存"的列表上。 这样的好处在于,当需要再次换出记录在交换缓存中的的内存页时,可以直接使用交换分区中保存的内容,而无需将内存再次写入交换空间。
当该交换空间上的其他数据被换出,或是换入内存中的数据被改写,则交换缓存上的记录会被清空。
Active(anon) / Inactive(anon) / Active(file) / Inactive(file)
括号中为anon的内存为匿名内存,括号中为file的内存为file-backed内存,这两个内存的区别在于,物理内存的内容是否与物理磁盘上的文件相关联。
其中,匿名内存就是进程中堆上分配的内存,是用malloc分配的内存。
而file-backed内存为磁盘高速缓存的内存空间和“文件映射(将物理磁盘上的文件内容与用户进程的逻辑地址直接关联)”的内存空间,其中的内容与物理磁盘上的文件相对应。
而Active和Inactive的区别在于内存空间中是否包含最近被使用过的数据。当物理内存不足,不得不释放正在使用的内存空间时,会优先释放Inactive的内存空间。
Linux内核中使用4类LRU表来分别记录对应的这4类内存页,内存页一般以4K为一页。
Unevictable
有些内存页是不能被释放的,这些内存页不能放在LRU表中,而是记录到Unevictable标中
SwapTotal/SwapFree
SwapTotal 交换空间的总大小
SwapFree 交换空间的剩余容量
Dirty
脏数据,在磁盘缓冲区中尚未写入物理磁盘的内存大小
AnonPages
Linux内核中存在一个rmap(reverse mapping)机制,负责管理匿名内存中每一个物理内存页映射到哪个进程的哪个逻辑地址这样的信息。 这个rmap中记录的内存页总和就是AnonPages的值。
Slab
由"Slab分配器"分配的总量。Slab分配器针对一些经常分配并释放的对象(如进程描述符)统计各种数据类型的汇总信息,然后为每种数据类型创建多个由多个内存页组成的Slab(这些Slab组成一个Slab列表)。 再在Slab内部划分成一个个相应数据类型的对象。
当内核要使用某种类型的数据结构时,就从对应的slab列表中分配一个对象出去,而当要释放时,将其重新保存在Slab列表中,从而避免内存碎片。
当可供使用的对象不足时,会使用空闲的内存页来创建并添加新的Slab到对应对象的Slab列表中。 相反,若Slab中所有对象都被内核回收,即所有对象都未使用时,根据需要也可以回收Slab,释放成空闲内存。
从 /proc/slabinfo 中我们可以查看每个Slab的信息。
SReclaimable
不存在活跃对象,可以回收的Slab容量
SUnreclaim
对象处于活跃状态,不能被回收的Slab容量
KernelStack
KernelStack是内核代码使用的堆栈区域。
由于Linux内核中用户进程在运行过程中需要不断切换,因此内核需要为每个用户进程都设置各自的堆栈区域。 因此,每启动一个新进程,KernelStack的值都会增加。
PageTables
PageTables就是页表,用于存储各个用户进程的逻辑地址和物理地址的变换关系,它本身也是一个内存区域。
VmallocTotal
Linux使用内存时,除了使用Slab中配置的对象外,还能直接将空闲内存页映射到逻辑地址上。
这个容量指的是,理论上内核内部可以用来映射的逻辑地址的范围。这个值非常大,但并非实际使用的物理内存
VmallocUsed
实际上,Linux将空闲内存页映射到逻辑地址上的容量。
值得说明的是,这个容量除了物理内存上所作的映射外,也包括诸如视频卡这样的外部设备内存所作的映射,这类映射叫做"ioremap"
我们可以通过 /proc/vmallocainfo 查看VmallocUsed中包含内存区域的详情
三、MemAvailable计算
diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c
index a77d2b2991998..24270eceddbff 100644
--- a/fs/proc/meminfo.c
+++ b/fs/proc/meminfo.c
@@ -26,7 +26,11 @@ static int meminfo_proc_show(struct seq_file *m, void *v)
unsigned long committed;
struct vmalloc_info vmi;
long cached;
+ long available;
+ unsigned long pagecache;
+ unsigned long wmark_low = 0;
unsigned long pages[NR_LRU_LISTS];
+ struct zone *zone;
int lru;
/*
@@ -47,12 +51,44 @@ static int meminfo_proc_show(struct seq_file *m, void *v)
for (lru = LRU_BASE; lru < NR_LRU_LISTS; lru++)
pages[lru] = global_page_state(NR_LRU_BASE + lru);
+ for_each_zone(zone)
+ wmark_low += zone->watermark[WMARK_LOW];
+
+ /*
+ * Estimate the amount of memory available for userspace allocations,
+ * without causing swapping.
+ *
+ * Free memory cannot be taken below the low watermark, before the
+ * system starts swapping.
+ */
+ available = i.freeram - wmark_low;
+
+ /*
+ * Not all the page cache can be freed, otherwise the system will
+ * start swapping. Assume at least half of the page cache, or the
+ * low watermark worth of cache, needs to stay.
+ */
+ pagecache = pages[LRU_ACTIVE_FILE] + pages[LRU_INACTIVE_FILE];
+ pagecache -= min(pagecache / 2, wmark_low);
+ available += pagecache;
+
+ /*
+ * Part of the reclaimable swap consists of items that are in use,
+ * and cannot be freed. Cap this estimate at the low watermark.
+ */
+ available += global_page_state(NR_SLAB_RECLAIMABLE) -
+ min(global_page_state(NR_SLAB_RECLAIMABLE) / 2, wmark_low);
+
+ if (available < 0)
+ available = 0;
+
/*
* Tagged format, for easy grepping and expansion.
*/
seq_printf(m,
"MemTotal: %8lu kB\n"
"MemFree: %8lu kB\n"
+ "MemAvailable: %8lu kB\n"
"Buffers: %8lu kB\n"
"Cached: %8lu kB\n"
"SwapCached: %8lu kB\n"
MemAvailable
= MemFree - wmark_low
+ pagecache
+ slab_reclaimable
= MemFree - wmark_low
+ Active(file) + Inactive(file) - min((Active(file) + Inactive(file))/2, wmark_low)
+ SReclaimable - min(SReclaimable/2, wmark_low)
= MemFree + Active(file) + Inactive(file) + SReclaimable
- wmark_low
- min((Active(file) + Inactive(file))/2, wmark_low)
- min(SReclaimable/2, wmark_low)
四、ZONE/watermark
内存回收是以zone为单位进行的(也会以memcg为单位,这里不讨论这种情况),为避免回收过程对系统造成很大的IO压力,一般会为每个zone设置一条线,当此zone的空闲页框不足以到达这条线时,就会对此zone进行内存回收,实际上一个zone有三条线,这三条线分别是最小阀值(WMARK_MIN),低阀值(WMARK_LOW),高阀值(WMARK_HIGH),它们都保存在zone的watermark[NR_WMARK]数组中,这个数组中保存的是各个阀值要求的页框数量,而每个阀值都会对内存回收造成影响。而它们的描述如下:
- watermark[WMARK_MIN](min阀值):在快速分配失败后的慢速分配中会使用此阀值进行分配,如果慢速分配过程中使用此值还是无法进行分配,那就会执行直接内存回收和快速内存回收
- watermark[WMARK_LOW](low阀值):也叫低阀值,是快速分配的默认阀值,在分配内存过程中,如果zone的空闲页框数量低于此阀值,系统会对zone执行快速内存回收
- watermark[WMARK_HIGH](high阀值):也叫高阀值,是zone对于空闲页框数量比较满意的一个值,当zone的空闲页框数量高于这个值时,表示zone的空闲页框较多。所以对zone进行内存回收时,目标也是希望将zone的空闲页框数量提高到此值以上,系统会使用此阀值用于oomkill进行内存回收。
这三个阀值的关系是:min阀值 < low阀值 < high阀值。在系统初始化期间,根据系统中整个内存的数量与每个zone管理的页框数量,计算出每个zone的min阀值,然后:
low阀值 = min阀值 + (min阀值 / 4),high阀值 = min阀值 + (min阀值 / 2)
相对于整个zone管理的总页框数量(managed),这三个值是非常非常小的,连managed的1%都不到,这些都是在系统初始化期间进行设置的,具体设置函数是__setup_per_zone_wmarks()。