用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, 正好對的上。

源碼

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章