参考
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
其中Buffers
跟buffers
可以对的上,但是Cached
与cache
就相差很大了。
原因
通过分析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 ... YAML 复制 全屏
- 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