参考
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
...
- 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