sou*_*edi 15 linux memory cache
我如何描述或解释输出中的“缓冲区” free?
$ free -h
total used free shared buff/cache available
Mem: 501M 146M 19M 9.7M 335M 331M
Swap: 1.0G 85M 938M
$ free -w -h
total used free shared buffers cache available
Mem: 501M 146M 19M 9.7M 155M 180M 331M
Swap: 1.0G 85M 938M
Run Code Online (Sandbox Code Playgroud)
我对这个系统没有任何(已知的)问题。我只是惊讶和好奇地看到“缓冲区”几乎和“缓存”一样高(155M vs 180M)。我认为“缓存”代表文件内容的页面缓存,并且往往是“缓存/缓冲区”中最重要的部分。我不确定什么是“缓冲区”。
例如,我将其与具有更多 RAM 的笔记本电脑进行了比较。在我的笔记本电脑上,“缓冲区”数字比“缓存”小一个数量级(200M 与 4G)。如果我了解什么是“缓冲区”,那么我就可以开始研究为什么缓冲区在较小的系统上增长到如此大的比例。
来自man proc(我忽略了“大”这个滑稽过时的定义):
缓冲区 %lu
原始磁盘块的相对临时存储,不应变得非常大(20MB 左右)。
缓存 %lu
从磁盘读取的文件的内存缓存(页面缓存)。不包括 SwapCached。
$ free -V
free from procps-ng 3.3.12
$ uname -r # the Linux kernel version
4.9.0-6-marvell
$ systemd-detect-virt # this is not inside a virtual machine
none
$ cat /proc/meminfo
MemTotal: 513976 kB
MemFree: 20100 kB
MemAvailable: 339304 kB
Buffers: 159220 kB
Cached: 155536 kB
SwapCached: 2420 kB
Active: 215044 kB
Inactive: 216760 kB
Active(anon): 56556 kB
Inactive(anon): 73280 kB
Active(file): 158488 kB
Inactive(file): 143480 kB
Unevictable: 10760 kB
Mlocked: 10760 kB
HighTotal: 0 kB
HighFree: 0 kB
LowTotal: 513976 kB
LowFree: 20100 kB
SwapTotal: 1048572 kB
SwapFree: 960532 kB
Dirty: 240 kB
Writeback: 0 kB
AnonPages: 126912 kB
Mapped: 40312 kB
Shmem: 9916 kB
Slab: 37580 kB
SReclaimable: 29036 kB
SUnreclaim: 8544 kB
KernelStack: 1472 kB
PageTables: 3108 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 1305560 kB
Committed_AS: 1155244 kB
VmallocTotal: 507904 kB
VmallocUsed: 0 kB
VmallocChunk: 0 kB
$ sudo slabtop --once
Active / Total Objects (% used) : 186139 / 212611 (87.5%)
Active / Total Slabs (% used) : 9115 / 9115 (100.0%)
Active / Total Caches (% used) : 66 / 92 (71.7%)
Active / Total Size (% used) : 31838.34K / 35031.49K (90.9%)
Minimum / Average / Maximum Object : 0.02K / 0.16K / 4096.00K
OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME
59968 57222 0% 0.06K 937 64 3748K buffer_head
29010 21923 0% 0.13K 967 30 3868K dentry
24306 23842 0% 0.58K 4051 6 16204K ext4_inode_cache
22072 20576 0% 0.03K 178 124 712K kmalloc-32
10290 9756 0% 0.09K 245 42 980K kmalloc-96
9152 4582 0% 0.06K 143 64 572K kmalloc-node
9027 8914 0% 0.08K 177 51 708K kernfs_node_cache
7007 3830 0% 0.30K 539 13 2156K radix_tree_node
5952 4466 0% 0.03K 48 124 192K jbd2_revoke_record_s
5889 5870 0% 0.30K 453 13 1812K inode_cache
5705 4479 0% 0.02K 35 163 140K file_lock_ctx
3844 3464 0% 0.03K 31 124 124K anon_vma
3280 3032 0% 0.25K 205 16 820K kmalloc-256
2730 2720 0% 0.10K 70 39 280K btrfs_trans_handle
2025 1749 0% 0.16K 81 25 324K filp
1952 1844 0% 0.12K 61 32 244K kmalloc-128
1826 532 0% 0.05K 22 83 88K trace_event_file
1392 1384 0% 0.33K 116 12 464K proc_inode_cache
1067 1050 0% 0.34K 97 11 388K shmem_inode_cache
987 768 0% 0.19K 47 21 188K kmalloc-192
848 757 0% 0.50K 106 8 424K kmalloc-512
450 448 0% 0.38K 45 10 180K ubifs_inode_slab
297 200 0% 0.04K 3 99 12K eventpoll_pwq
288 288 100% 1.00K 72 4 288K kmalloc-1024
288 288 100% 0.22K 16 18 64K mnt_cache
287 283 0% 1.05K 41 7 328K idr_layer_cache
240 8 0% 0.02K 1 240 4K fscrypt_info
Run Code Online (Sandbox Code Playgroud)
sou*_*edi 21
Buffers干什么用的?Buffers特别期望更大或更小?Buffers显示用于块设备的页面缓存量。“块设备”是最常见的数据存储设备类型。
内核在报告Cached. 见meminfo_proc_show():
cached = global_node_page_state(NR_FILE_PAGES) -
total_swapcache_pages() - i.bufferram;
...
show_val_kb(m, "MemTotal: ", i.totalram);
show_val_kb(m, "MemFree: ", i.freeram);
show_val_kb(m, "MemAvailable: ", available);
show_val_kb(m, "Buffers: ", i.bufferram);
show_val_kb(m, "Cached: ", cached);
Run Code Online (Sandbox Code Playgroud)
页缓存以 MMU 页大小为单位工作,通常最小为 4096 字节。这对于mmap(),即内存映射文件访问至关重要。[1][2] 它旨在在不同的进程之间共享已加载程序/库代码的页面,并允许按需加载单个页面。(也用于在其他东西需要空间时卸载页面,并且最近没有使用它们)。
[1]内存映射 I/O - GNU C 库手册。
[2] mmap- 维基百科。
早期的 UNIX 有一个磁盘块的“缓冲区缓存”,并且没有 mmap()。显然,当首次添加 mmap() 时,他们将页面缓存作为新层添加到顶部。这听起来很混乱。最终,基于 UNIX 的操作系统摆脱了单独的缓冲区缓存。所以现在所有的文件缓存都是以页为单位的。页面是通过(文件,偏移量)而不是磁盘上的位置来查找的。这被称为“统一缓冲区缓存”,也许是因为人们更熟悉“缓冲区缓存”。 [3]
[3] UBC:用于 NetBSD 的高效统一 I/O 和内存缓存子系统
(“Linux 增加的一个有趣的变化是,页面存储在磁盘上的设备块号以buffer_head结构列表的形式与页面一起缓存。当修改的页面要写回磁盘时,I/ O 请求可以立即发送到设备驱动程序,而无需读取任何间接块来确定应将页面数据写入何处。”[3])
在 Linux 2.2 中,有一个单独的“缓冲区缓存”用于写入,但没有用于读取。“页面缓存使用缓冲区缓存来写回其数据,需要额外的数据副本,并且某些写入负载的内存要求加倍”。[4] 让我们不要太担心细节,但这段历史将是 LinuxBuffers单独报告使用情况的原因之一。
[4] Linux 2.4 内存管理中的页面替换,Rik van Riel。
相比之下,在 Linux 2.4 及更高版本中,额外的副本不存在。“系统直接与页面缓存页面进行磁盘 IO。”[4] Linux 2.4 于 2001 年发布。
Buffers用?块设备被视为文件,因此具有页面缓存。这用于“文件系统元数据和原始块设备的缓存”。 [4] 但是在当前版本的 Linux 中,文件系统不会通过它复制文件内容,因此没有“双重缓存”。
我认为Buffers页面缓存的一部分是 Linux 缓冲区缓存。一些消息来源可能不同意这个术语。
文件系统使用多少缓冲区缓存(如果有)取决于文件系统的类型。问题中的系统使用ext4。ext3/ext4 将 Linux 缓冲区缓存用于日志、目录内容和其他一些元数据。
某些文件系统,包括 ext3、ext4 和 ocfs2,使用 jbd 或 jbd2 层来处理它们的物理块日志,而该层从根本上使用缓冲区缓存。
在 Linux 内核版本 2.4 之前,Linux 具有单独的页面和缓冲区缓存。从 2.4 开始,页面缓存和缓冲区缓存是统一的,并且
Buffers是未在页面缓存中表示的原始磁盘块,即不是文件数据。...
然而,缓冲区缓存仍然存在,因为内核仍然需要按照块而不是页来执行块 I/O。由于大多数块代表文件数据,因此大部分缓冲区缓存由页面缓存表示。但是少量的块数据不是文件支持的——例如元数据和原始块 I/O——因此仅由缓冲区缓存表示。
-一对Quora的答案由罗伯特·爱,最后更新2013。
两位作者都是 Linux 开发人员,他们从事过 Linux 内核内存管理。第一个来源更具体地涉及技术细节。第二个来源是一个更笼统的总结,在某些细节上可能存在矛盾和过时。
文件系统确实可以执行部分页面元数据写入,即使缓存是按页面索引的。甚至用户进程在使用write()(相对于mmap())时也可以执行部分页面写入,至少直接写入块设备。这仅适用于写入,不适用于读取。当您通读页面缓存时,页面缓存始终读取完整页面。
Linus 喜欢咆哮说不需要缓冲区缓存来执行块大小的写入,并且文件系统可以执行部分页面元数据写入,即使页面缓存附加到他们自己的文件而不是块设备。我相信他说 ext2 这样做是正确的。ext3/ext4 及其日志系统没有。目前尚不清楚导致这种设计的问题是什么。他咆哮的人已经厌倦了解释。
ext4_readdir() 没有改变以满足 Linus 的咆哮。我也没有在其他文件系统的 readdir() 中看到他想要的方法。我认为 XFS 也使用目录的缓冲区缓存。bcachefs 根本不使用 readdir() 的页面缓存;它为 btree 使用自己的缓存。我不确定 btrfs。
Buffers特别期望更大或更小?在这种情况下,我的文件系统的ext4 日志大小是 128M。所以这就解释了为什么 1) 我的缓冲区缓存可以稳定在略高于 128M;2) 缓冲区缓存不会与笔记本电脑上较大的 RAM 量成比例地扩展。
对于其他一些可能的原因,请参阅free 输出中的缓冲区列是什么? 请注意,报告的“缓冲区”free实际上是Buffers可回收内核平板内存的组合。
为了验证日志写入使用缓冲区缓存,我在快速 RAM (tmpfs) 中模拟了一个文件系统,并比较了不同日志大小的最大缓冲区使用量。
# dd if=/dev/zero of=/tmp/t bs=1M count=1000
...
# mkfs.ext4 /tmp/t -J size=256
...
# LANG=C dumpe2fs /tmp/t | grep '^Journal size'
dumpe2fs 1.43.5 (04-Aug-2017)
Journal size: 256M
# mount /tmp/t /mnt
# cd /mnt
# free -w -m
total used free shared buffers cache available
Mem: 7855 2521 4321 285 66 947 5105
Swap: 7995 0 7995
# for i in $(seq 40000); do dd if=/dev/zero of=t bs=1k count=1 conv=sync status=none; sync t; sync -f t; done
# free -w -m
total used free shared buffers cache available
Mem: 7855 2523 3872 551 237 1223 4835
Swap: 7995 0 7995
Run Code Online (Sandbox Code Playgroud)
# dd if=/dev/zero of=/tmp/t bs=1M count=1000
...
# mkfs.ext4 /tmp/t -J size=16
...
# LANG=C dumpe2fs /tmp/t | grep '^Journal size'
dumpe2fs 1.43.5 (04-Aug-2017)
Journal size: 16M
# mount /tmp/t /mnt
# cd /mnt
# free -w -m
total used free shared buffers cache available
Mem: 7855 2507 4337 285 66 943 5118
Swap: 7995 0 7995
# for i in $(seq 40000); do dd if=/dev/zero of=t bs=1k count=1 conv=sync status=none; sync t; sync -f t; done
# free -w -m
total used free shared buffers cache available
Mem: 7855 2509 4290 315 77 977 5086
Swap: 7995 0 7995
Run Code Online (Sandbox Code Playgroud)
我首先找到了 Ted Tso 的电子邮件,并且对它强调写入缓存很感兴趣。如果“脏”、未写入的数据能够达到我系统上 RAM 的 30%,我会感到惊讶。 sudo atop显示在 10 秒的时间间隔内,有问题的系统始终只写入 1MB。相关的文件系统将能够跟上这个速度的 100 多倍。(它位于 USB2 硬盘驱动器上,最大吞吐量 ~20MB/s)。
使用 blktrace( btrace -w 10 /dev/sda) 确认正在缓存的 IO 必须是写入,因为几乎没有数据被读取。这mysqld也是唯一一个进行 IO 的用户空间进程。
我停止了负责写入的服务(icinga2 写入 mysql)并重新检查。我看到“缓冲区”下降到 20M 以下 - 我对此没有任何解释 - 并留在那里。再次重新启动写入器显示“缓冲区”每 10 秒间隔增加约 0.1M。我观察到它一直保持这个速度,回升到 70M 及以上。
运行echo 3 | sudo tee /proc/sys/vm/drop_caches足以再次将“缓冲区”降低到 4.5M。这证明我的缓冲区积累是一个“干净”的缓存,Linux 可以在需要时立即删除它。该系统不会累积未写入的数据。(drop_caches不执行任何回写,因此不能删除脏页。如果您想运行一个先清理缓存的测试,您可以使用该sync命令)。
整个mysql目录只有150M。累积的缓冲区必须代表来自 mysql 写入的元数据块,但想到这些数据会有这么多元数据块让我感到惊讶。