參考
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, 正好對的上。
源碼
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