在Linux系统中我们进行内存使用定位时,经常使用free命令查看内存的使用情况,但是总有那么两列可能没有含糊不清楚,网上找资料也良莠不齐,很难将问题讲透、讲明白。在翻阅了很多资料后,将我的理解记录如下。这篇文章后面应该会进行更新,因为现在的分析可能还不是准确的。

free命令

首先我们对free命令的输出做一下解读。free命令使用-m -w参数的输出如下:

  1. # free -m -w
  2. total used free shared buffers cache available
  3. Mem: 486 92 103 0 20 270 381
  4. Swap: 0 0 0

其中-m参数表示使用MBytes为单位显示,-w使用wide模式,将buffers和cache分为两列显示。
free命令的数据来源于/proc/meminfo,详细解释可以查看free命令的帮助手册(man free)。
命令输出解读:

输出列名 /proc/meminfo数据 解释
total MemTotal和SwapTotal 系统的全部内存,包含物理内存条和交换设备
used total - free - buffers - cache
free MemFree 和 SwapFree 没有被使用的内存
shared Shmem 大多数情况下是被tmpfs使用的内存
buffers Buffers 内核buffers使用的内存
cache Cached 和 SReclaimable page cache和slab使用的内存
available MemAvailable 内核计算出可供新程序使用的内存

下面是man手册中对buff/cache的解释:

  1. buffers
  2. Memory used by kernel buffers (Buffers in /proc/meminfo)
  3. cache Memory used by the page cache and slabs (Cached and SReclaimable in /proc/meminfo)
  4. buff/cache
  5. Sum of buffers and cache

对于meminfo中的各个字段的含义,我们可以通过man proc命令来查看,如果使用该命令抱以下错误,则可以通过安装man-pages来解决。
错误:

  1. # man proc
  2. 没有 proc 的手册页条目

安装:

  1. # yum install -y man-pages

可以通过搜索直接找到meminfo的部分:

  1. Buffers %lu
  2. Relatively temporary storage for raw disk blocks that shouldn't get tremendously large (20MB
  3. or so).
  4. Cached %lu
  5. In-memory cache for files read from the disk (the page cache). Doesn't include SwapCached.
  6. SReclaimable %lu (since Linux 2.6.19)
  7. Part of Slab, that might be reclaimed, such as caches.
  • Buffers:对原始磁盘块的临时存储,通常不会特别大(20M左右)
  • Cached:磁盘文件读取的缓存(page cache)
  • SReclaimable:Slab的一部分,可回收的部分,比如缓存

通过上面的描述,感觉还是有点模糊,尤其在Buffers和Cached这两块,Buffers是对原始磁盘块的临时存储,那么这个是针对读的还是写的,还是都有?Cached写的是从磁盘读取文件的缓存,那么正常对文件的写操作是Buffers还是Cached?如果Cached的只是针对文件读取,那么Cached中的文件被修改后会发生什么?
为了解决上面的疑问,我们分别设计以下实验:

  • 对文件进行读/写操作
  • 对磁盘进行读/写操作

    文件读写

    文件写入

    首先我们需要使用以下命令将page cache清掉,然后使用dd命令进行对文件的读写操作。
    1. # echo '3' > /proc/sys/vm/drop_caches
    同时打开终端1和终端2,终端1负责写入,终端2负责使用vmstat命令进行观察buff和cache的变化,如下所示。
    终端1:
    1. # dd if=/dev/urandom of=/tmp/file bs=2M count=500
    2. 记录了500+0 的读入
    3. 记录了500+0 的写出
    4. 1048576000字节(1.0 GB)已复制,9.59103 秒,109 MB/秒
    终端2:
    1. # vmstat -SM 2
    2. procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
    3. r b swpd free buff cache si so bi bo in cs us sy id wa st
    4. 0 0 0 337 0 55 0 0 0 0 1359 2977 1 1 98 0 0
    5. 0 0 0 337 0 55 0 0 0 0 1367 2988 1 1 99 0 0
    6. 3 0 0 337 0 55 0 0 0 0 1381 3001 1 1 98 0 0
    7. 5 0 0 235 1 155 0 0 108 32804 1390 2114 0 39 61 0 0
    8. 3 1 0 4 0 387 0 0 2 128000 1403 732 0 99 0 1 0
    9. 4 0 0 7 0 384 0 0 2 122880 1591 1265 0 86 0 13 0
    10. 21 2 0 5 0 386 0 0 2050 100388 1511 1383 0 81 0 19 0
    11. 2 0 0 4 0 387 0 0 1798 98328 1617 1517 0 79 0 21 0
    12. 0 0 0 7 0 386 0 0 738 29716 1459 2866 1 10 61 28 0
    13. 1 0 0 7 0 386 0 0 0 0 1422 3052 1 1 99 0 0
    14. 0 0 0 7 0 386 0 0 0 0 1347 2958 1 0 99 0 0
    通过终端2可以看出在进行对文件写入时cache列增长,buff没有基本上没有什么变化,这说明在对文件进行写入时也会使用cache。

    文件读取

    首先使用上面提到的方式清理cache:
    # echo '3' > /proc/sys/vm/drop_caches
    
    继续使用dd命令将文件的内容读到系统中,如下所示。
    终端1:
    # dd of=/dev/null if=/tmp/file bs=2M count=500
    记录了500+0 的读入
    记录了500+0 的写出
    1048576000字节(1.0 GB)已复制,9.37269 秒,112 MB/秒
    
    终端2:
    # vmstat -SM 2
    procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
    r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
    1  0      0    349      0     42    0    0     0     0 1339 2967  1  1 98  0  0
    0  0      0    349      0     42    0    0     0     0 1354 2965  1  1 99  0  0
    0  0      0    349      0     43    0    0     2     0 1356 2975  1  1 99  0  0
    0  1      0    135      0    255    0    0 108716    32 1625 3100  1  5 10 85  0
    0  1      0      6      0    383    0    0 108546     0 1566 2941  1  6  0 94  0
    0  1      0      6      0    384    0    0 108544     6 1577 2959  0  6  0 94  0
    1  1      0      6      0    383    0    0 107020     0 1607 2976  1  6  0 93  0
    0  0      0      7      0    385    0    0 84636     0 1559 2986  1  5 21 74  0
    0  0      0      7      0    385    0    0     0     0 1333 2953  1  1 98  0  0
    0  0      0      7      0    385    0    0     0     6 1335 2949  0  1 99  1  0
    0  0      0      7      0    385    0    0     0     0 1311 2935  1  0 99  0  0
    
    通过中断2 的结果看以看出对文件的读入操作时cache是增加的,但是buff没有增加,由此可以知道对文件的读取确实会走cache。

    磁盘写入

    为了验证对磁盘的写入会走buff还是cache,我们在申请虚拟机的时候多加了一块hdd盘,下面这个操作需要注意下,使用空白磁盘或者分区,否则会导致数据丢失。
    首先使用上面提到的命令对cache进行清理:
    # echo '3' > /proc/sys/vm/drop_caches
    
    然后使用dd命令对/dev/vdb进行写入操作。同时打开终端1和终端2,终端1负责写入,终端2负责使用vmstat命令查看内存使用情况,如下所示。
    终端1:
    # dd if=/dev/urandom of=/dev/vdb bs=2M count=500
    记录了500+0 的读入
    记录了500+0 的写出
    1048576000字节(1.0 GB)已复制,9.69532 秒,108 MB/秒
    
    终端2:
    # vmstat -SM 2
    procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
    r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
    1  0      0    335      0     57    0    0     0     8 1331 2950  1  0 99  0  0
    0  0      0    335      0     57    0    0     0     0 1429 3055  1  1 99  0  0
    1  0      0    219     93     75    0    0    38 32768 1391 2096  0 39 61  0  0
    3  0      0      8    280     95    0    0     2 110735 1756 1354  0 100  0  0  0
    4  1      0      4    282     96    0    0     0 101619 1743 1589  1 99  0  0  0
    5  0      0      6    282     95    0    0     2 103268 1916 1790  0 100  0  0  0
    5  0      0      6    281     96    0    0     2 104839 2273 2169  0 99  0  0  0
    0  0      0     12    281     98    0    0  2018 50608 1485 2436  0 36 44 20  0
    0  0      0     12    281     98    0    0     0     0 1348 2970  1  1 98  0  0
    0  0      0      6    280    105    0    0  3884     0 1365 3006  1  0 98  1  0
    
    从终端2的输出可以看出,在对磁盘进行写入的时候buff在增长,而cache增长的非常有限,这里我们可以任务在对磁盘进行直接写入时buff会承担缓存的作用。

    磁盘读取

    在验证磁盘读取之前首先使用前面提到的命令对缓存进行清理。
    # echo '3' > /proc/sys/vm/drop_caches
    
    同样是使用两个终端,终端1改为直接从/dev/vdb读取数据,通过终端2观察内存使用情况,如下所示。
    终端1:
    # dd of=/dev/null if=/dev/vdb bs=2M count=500
    记录了500+0 的读入
    记录了500+0 的写出
    1048576000字节(1.0 GB)已复制,9.58241 秒,109 MB/秒
    
    终端2:
    # vmstat -SM 2
    procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
    r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
    1  0      0    337      0     55    0    0     0     2 1400 3047  1  1 98  0  0
    0  0      0    337      0     55    0    0     0     0 1371 3002  0  1 99  0  0
    0  1      0    130    204     55    0    0 104552     0 1621 3076  1  5  7 88  0
    1  1      0      6    333     50    0    0 110202    30 1819 3262  2  7  0 91  0
    1  1      0      6    333     51    0    0 106496     0 1643 3074  1  6  0 93  0
    0  1      0      6    343     40    0    0 104450     0 1602 3013  0  6  0 94  0
    0  0      0      8    342     41    0    0 94872    10 1605 3034  1  6 14 80  0
    0  0      0      8    342     41    0    0     0     0 1388 3028  1  1 99  0  0
    0  0      0      8    342     41    0    0     0     0 1383 3014  1  1 99  0  0
    
    从上面可以看出在对磁盘直接进行读入时,buff在增加。可以看出对磁盘进行直接读取操作会使用buff作为缓存使用。

    结论

    Linux系统中对文件进行读写时会,会使用cached来提高性能;对磁盘进行读写时会使用buffers来提高性能。

参考文档:
https://github.com/firmianay/Life-long-Learner/blob/master/linux-kernel-development/chapter-16.md
https://medium.com/geekculture/linux-memory-buffer-vs-cache-44d8a187f310