用free命令看到的cache跟/proc/meminfo看到的为什么不同?

参考

https://gitlab.com/procps-ng/procps

问题

在使用free命令时发现,free命令输出的buff/cache跟从/proc/meminfo里看到的并不相同,这是为什么呢?

  • free命令的输出
root@ubuntu-vm:~# free
              total        used        free      shared  buff/cache   available
Mem:        2915040      203052     2474248        1864      237740     2665100
Swap:       4194300           0     4194300

或者将buff和cache分开显示:

root@ubuntu-vm:~# free -w
              total        used        free      shared     buffers       cache   available
Mem:        2915040      202844     2474456        1864       16356      221384     2665308
Swap:       4194300           0     4194300
  • meminfo的内容
root@ubuntu-vm:~# cat /proc/meminfo
MemTotal:        2915040 kB
MemFree:         2474076 kB
MemAvailable:    2664928 kB
Buffers:           16356 kB
Cached:           172904 kB
SwapCached:            0 kB
Active:            97604 kB
Inactive:         124956 kB
Active(anon):        360 kB
Inactive(anon):    34804 kB
Active(file):      97244 kB
Inactive(file):    90152 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:       4194300 kB
SwapFree:        4194300 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:         33332 kB
Mapped:            53588 kB
Shmem:              1864 kB
KReclaimable:      48480 kB
Slab:             179512 kB
SReclaimable:      48480 kB
SUnreclaim:       131032 kB
KernelStack:        3040 kB
PageTables:         1740 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     5651820 kB
Committed_AS:     124320 kB
VmallocTotal:   34359738367 kB
VmallocUsed:        4268 kB
VmallocChunk:          0 kB
Percpu:             1824 kB
CmaTotal:              0 kB
CmaFree:               0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
Hugetlb:               0 kB
DirectMap4k:      128884 kB
DirectMap2M:     4065280 kB

上面meminfo的输出里:

Buffers:           16356 kB
Cached:           172904 kB

而free的输出的是:

buffers       cache
16356         221384

其中Buffersbuffers可以对的上,但是Cachedcache就相差很大了。

原因

通过分析free命令的实现,发现了问题所在。free命令输出的cache其实是meminfo中的Cached + SReclaimable。上面SReclaimable的输出是48480,加上172904得到221384, 正好对的上。

man手册里对这个也有解释:

DESCRIPTION
       free  displays  the total amount of free and used physical and swap memory in the system, as well as the buffers and caches used by the kernel. The
       information is gathered by parsing /proc/meminfo. The displayed columns are:

       total  Total installed memory (MemTotal and SwapTotal in /proc/meminfo)

       used   Used memory (calculated as total - free - buffers - cache)

       free   Unused memory (MemFree and SwapFree in /proc/meminfo)

       shared Memory used (mostly) by tmpfs (Shmem in /proc/meminfo)

       buffers
              Memory used by kernel buffers (Buffers in /proc/meminfo)

       cache  Memory used by the page cache and slabs (Cached and SReclaimable in /proc/meminfo)

       buff/cache
              Sum of buffers and cache

       available
              Estimation of how much memory is available for starting new applications, without swapping. Unlike the data provided by the  cache  or  free
              fields,  this field takes into account page cache and also that not all reclaimable memory slabs will be reclaimed due to items being in use
              (MemAvailable in /proc/meminfo, available on kernels 3.14, emulated on kernels 2.6.27+, otherwise the same as free)

源码

main
	-> procps_meminfo_new(&mem_info)
		-> meminfo_read_failed(p)
	-> printf(" %11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_MEM_CACHED_ALL, ul_int), flags, args)
  • meminfo_read_failed的源码
/*
 * meminfo_read_failed():
 *
 * Read the data out of /proc/meminfo putting the information
 * into the supplied info structure
 */
static int meminfo_read_failed (
        struct meminfo_info *info)
{
 /* a 'memory history reference' macro for readability,
    so we can focus the field names ... */
 #define mHr(f) info->hist.new. f
    char buf[MEMINFO_BUFF];  // 8KB
    char *head, *tail;
    int size;
    unsigned long *valptr;
    signed long mem_used;

    // remember history from last time around
    memcpy(&info->hist.old, &info->hist.new, sizeof(struct meminfo_data));
    // clear out the soon to be 'current' values
    memset(&info->hist.new, 0, sizeof(struct meminfo_data));

    if (-1 == info->meminfo_fd
    && (-1 == (info->meminfo_fd = open(MEMINFO_FILE, O_RDONLY))))
        return 1;

    if (lseek(info->meminfo_fd, 0L, SEEK_SET) == -1)
        return 1;

	// 读取/proc/meminfo的内容到buf中
    for (;;) {
        if ((size = read(info->meminfo_fd, buf, sizeof(buf)-1)) < 0) {
            if (errno == EINTR || errno == EAGAIN)
                continue;
            return 1;
        }
        break;
    }
    if (size == 0) {
        errno = EIO;
        return 1;
    }
    buf[size] = '\0';

    head = buf;

	// 解析读到的数据
    for (;;) {
        static __thread ENTRY e;  // keep coverity off our backs (e.data)
        ENTRY *ep;

        if (!(tail = strchr(head, ':')))
            break;
        *tail = '\0';
        valptr = NULL;

        e.key = head;
        if (hsearch_r(e, FIND, &ep, &info->hashtab))
            valptr = ep->data;
        head = tail + 1;
        if (valptr)
            *valptr = strtoul(head, NULL, 10);

        if (!(tail = strchr(head, '\n')))
            break;
        head = tail + 1;
    }

    if (0 == mHr(MemAvailable))
        mHr(MemAvailable) = mHr(MemFree);
    mHr(derived_mem_cached) = mHr(Cached) + mHr(SReclaimable);   // 这里derived_mem_cached就是后面输出的cache的数据来源

    /* if 'available' is greater than 'total' or our calculation of mem_used
       overflows, that's symptomatic of running within a lxc container where
       such values will be dramatically distorted over those of the host. */
    if (mHr(MemAvailable) > mHr(MemTotal))
        mHr(MemAvailable) = mHr(MemFree);
    mem_used = mHr(MemTotal) - mHr(MemAvailable);
    if (mem_used < 0)
        mem_used = mHr(MemTotal) - mHr(MemFree);
    mHr(derived_mem_used) = (unsigned long)mem_used;

    if (mHr(HighFree) < mHr(HighTotal))
         mHr(derived_mem_hi_used) = mHr(HighTotal) - mHr(HighFree);

    if (0 == mHr(LowTotal)) {
        mHr(LowTotal) = mHr(MemTotal);
        mHr(LowFree)  = mHr(MemFree);
    }
    if (mHr(LowFree) < mHr(LowTotal))
        mHr(derived_mem_lo_used) = mHr(LowTotal) - mHr(LowFree);

    if (mHr(SwapFree) < mHr(SwapTotal))
        mHr(derived_swap_used) = mHr(SwapTotal) - mHr(SwapFree);

    return 0;
 #undef mHr
} // end: meminfo_read_failed
  • MEMINFO_GET
#define MEMINFO_GET( info, actual_enum, type ) ( { \
    struct meminfo_result *r = procps_meminfo_get( info, actual_enum ); \
    r ? r->result . type : 0; } )
  • procps_meminfo_get
PROCPS_EXPORT struct meminfo_result *procps_meminfo_get (
        struct meminfo_info *info,
        enum meminfo_item item)
{
    time_t cur_secs;

    errno = EINVAL;
    if (info == NULL)
        return NULL;
    if (item < 0 || item >= MEMINFO_logical_end)
        return NULL;
    errno = 0;

    /* we will NOT read the meminfo file with every call - rather, we'll offer
       a granularity of 1 second between reads ... */
    cur_secs = time(NULL);
    if (1 <= cur_secs - info->sav_secs) {
        if (meminfo_read_failed(info))
            return NULL;
        info->sav_secs = cur_secs;
    }

    info->get_this.item = item;
    //  with 'get', we must NOT honor the usual 'noop' guarantee
    info->get_this.result.ul_int = 0;
    Item_table[item].setsfunc(&info->get_this, &info->hist);

    return &info->get_this;
} //
  • Item_table
static struct {
    SET_t setsfunc;              // the actual result setting routine
    char *type2str;              // the result type as a string value
} Item_table[] = {
/*  setsfunc                   type2str
    -------------------------  ---------- */
 ...
  { RS(MEM_BUFFERS),           TS(ul_int) },
  { RS(MEM_CACHED),            TS(ul_int) },
  { RS(MEM_CACHED_ALL),        TS(ul_int) },
 ...

其中{ RS(MEM_CACHED_ALL), TS(ul_int) }展开后就是

{(SET_t)set_meminfo_MEM_CACHED_ALL, "ul_int"}
  • set_meminfo_MEM_CACHED_ALL
#define setNAME(e) set_meminfo_ ## e
#define setDECL(e) static void setNAME(e) \
    (struct meminfo_result *R, struct mem_hist *H)

// regular assignment
#define MEM_set(e,t,x) setDECL(e) { R->result. t = H->new. x; }
// delta assignment
#define HST_set(e,t,x) setDECL(e) { R->result. t = ( H->new. x - H->old. x ); }

setDECL(noop)  { (void)R; (void)H; }
setDECL(extra) { (void)H; R->result.ul_int = 0; }

...
MEM_set(MEM_BUFFERS,            ul_int,  Buffers)
MEM_set(MEM_CACHED,             ul_int,  Cached)
MEM_set(MEM_CACHED_ALL,         ul_int,  derived_mem_cached)

其中:MEM_set(MEM_CACHED_ALL, ul_int, derived_mem_cached)展开后:

static void set_meminfo_MEM_CACHED_ALL \
    (struct meminfo_result *R, struct mem_hist *H) {R->result.ul_int = H->new.derived_mem_cached;}

其他

与free类似,vmstat和top命令的输出也存在这样的现象:

  • vmstat
root@ubuntu-vm:~# vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0 2475548  16420 221352    0    0     0     0    2    2  0  0 100  0  0
 0  0      0 2475424  16420 221352    0    0     0     0   18   15  0  0 100  0  0
 ...
  • top
root@ubuntu-vm:~# top -b -n 1
top - 22:07:29 up 1 day, 18:08,  1 user,  load average: 0.00, 0.00, 0.00
Tasks:  92 total,   1 running,  91 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.9 us, 12.0 sy,  0.0 ni, 87.2 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   2846.7 total,   2415.9 free,    198.3 used,    232.5 buff/cache
MiB Swap:   4096.0 total,   4096.0 free,      0.0 used.   2602.6 avail Mem
...
  • free
root@ubuntu-vm:~# free -h
              total        used        free      shared  buff/cache   available
Mem:          2.8Gi       198Mi       2.4Gi       1.0Mi       232Mi       2.5Gi
Swap:         4.0Gi          0B       4.0Gi
posted @ 2022-08-09 22:08  摩斯电码  阅读(856)  评论(0编辑  收藏  举报