找回密码
 注册
关于网站域名变更的通知
查看: 370|回复: 1
打印 上一主题 下一主题

转一篇大神给咱们提供的Linux可用内存的统计方法

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2020-1-10 10:36 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
# z) C( G2 L) d: Y2 x: X
背景
! l8 b1 G4 a7 V8 H6 a; n由于Linux缓存机制的设计,系统对缓存的使用是非常狠的,所以经常会看到某些环境内存只剩几十兆了,而应用只用了不到一半。所以在计算可用内存的时候,一定要算上缓存的部分。
# [3 F6 W* y  \% S通常方法,就是通过free命令首行free+cached+buffers计算,或者直接使用第二行的free字段。但这个方法有时仍然会造成比较大的误差,导致性能监控等方面的问题。
5 h: ^. S% [- C" i, t) S7 z比如系统中使用了大量的共享内存会造成多计算可用内存;再比如对大量的文件做了查询(find / ?!!!),会导致少计算可用内存。对于这点我在《说说free命令》中有详细的说明。这里就不再赘述了。) Z5 H& v; l2 D# Q& a. n& S
2 v6 S! R9 K( l3 t, j3 H7 b
SUSE11 SP1基于2.6.32内核,内核暴露了更多的统计接口给用户空间,把slab分为可回收和不可回收两类指标来统计。free命令也对应做了修改。解决了free命令少计算可用内存的问题。但多计算的问题还是存在。) |; i. Z. d, {
因此,在这里对统计可用内存的方法做了个总结。供需要的同学参考。(后面有空可能会开发一个统计工具)
% P# f& I* X3 D/ b' S其中SUSE10由于内核版本过低(2.6.16)暴露信息不足,下面的方法仍然不能很精确,但相比通过free命令简单统计而言,一般不会造成比较大的误差。. d6 J# c" q: i
/ z4 Y5 v# h1 i7 ?
可用内存定义
1 ^. |6 g0 M, A/ O7 B包括未被使用的空闲内存,以及已经被使用但用作缓存可以自动回收的部分。8 R; U8 r5 }0 X6 a% z

0 t* T# o; R, b7 {! S2 mSUSE 10可用内存统计方法
1 b- F( g! L, i4 M+ c总内存:free命令首行total字段。
# I9 U) o' v3 b& c  L% t空闲内存:free命令首行free字段。
% D# T  p& B+ p2 a) n缓存:free命令首行buffers字段+cached字段。
2 S- Z" @; t0 k% |修正值1:cached字段包含了共享内存和tmpfs内存文件系统占用的内存。需要减去这两部分。这两部分内存可通过ipcs -m -u和df 命令获取。
! Z+ U2 y3 n1 I3 ^修正值2:cached字段漏掉了内核slab中可以自动回收的内存,比如xxx_inode_cache和dentry_cache。这两部分的内存的计算方法是解析/proc/slabinfo。  s( C5 ]2 M& |1 q+ H9 K7 i
最终的可用内存计算方法:" n. w2 a. r5 Q
空闲内存+缓存-修正值1+修正值2" A3 d# Q6 d4 W$ d* p& V
/ n6 u7 l* K3 n8 @' F9 ?
SUSE 11可用内存统计方法
* t: D! t8 f" U4 z- y- F总内存:free命令首行total字段。0 H+ u5 [" c- v
空闲内存:free命令首行free字段。
. \0 _7 F& w) }" p缓存:free命令首行buffers字段+cached字段。* t( k( E% j, h! Y/ k+ t
修正值1:cached字段包含了共享内存和tmpfs内存文件系统占用的内存。需要减去这两部分。这两部分内存之和可通过/proc/meminfo的Shmem字段直接获取。
6 p. M. h- q$ v最终的可用内存计算方法:5 B+ U8 u! E0 K: r" t/ d& H# N9 |
空闲内存+缓存-修正值12 e. ^$ z# D. D

* A& n; D" o' Q3 A, k: p# A附1:ipcs获取共享内存占用物理内存大小- |& H+ ~5 p. K1 \
# ipcs -m -u
2 l$ ]3 M4 X  A------ Shared Memory Status --------
& p, S+ |& }1 {4 b2 Rsegments allocated 4! n8 O: O' I# L+ u  K
pages allocated 786433
( ^6 P2 m1 H/ `! t3 Apages resident 2 #使用这个字段,单位是页,X86下一个页是4kB) K$ ~) }8 s/ O) W; s; c& f% L
pages swapped 0$ x. t* _, r' G! V, A
Swap peRFormance: 0 attempts 0 successes
+ H$ v; x! S: k  u' W4 E% Y2 R& r, d* W6 ~7 W8 _. a8 @; X
附2:df获取tmpfs占用物理内存大小9 h. e' M) S1 I
# df
, t7 }! h; m: B  U7 X7 BFilesystem 1K-blocks Used Available Use% Mounted on0 Z/ C% P1 V4 ~
/dev/sda2 20972152 4427900 16544252 22% /7 F( r# x' y! D
devtmpfs 24711780 160 24711620 1% /dev #Used字段表示实际占用物理内存3 w3 i* b7 z. K: P( V4 E
tmpfs 24711780 0 24711780 0% /dev/shm #Used字段表示实际占用物理内存- J3 J/ T& A) J4 m! Z. l
/dev/sda5 1052184 59188 992996 6% /boot& Z$ v7 Z( _( z" H' @! H1 k
/dev/sda9 83888824 16852500 67036324 21% /iso
) R* X4 F' ~- x9 X$ ^/dev/sda7 10490040 1142876 9347164 11% /opt
5 L& z2 u! L. q! l. Y0 S/dev/sda10 83888824 9421200 74467624 12% /src: V" W; U( {% j' P* s- ^
/dev/sda8 20972152 3475104 17497048 17% /usr- `3 V! [2 i; |
/dev/sda6 5245016 328392 4916624 7% /var
7 H3 m, r+ H1 q1 S  I
0 e! b9 I/ K2 Z+ Z) N
2 M7 [& K9 h: @# Q/ g  c/ c2 v6 ?+ T///////////////////增加统计工具////////2011.6.29///////////////////////////////////////////////////
. H! j& v5 R. n: w( h统计工具(在SUSE10、SUSE11验证通过)
" Y* \8 D4 N) R% U( m# p9 ~$ a1. 修正可用内存多计算的情况(SUSE10 SUSE11)! W2 n% l$ H3 j1 W
如下环境,在进行了大量共享内存创建使用后,free命令统计可用内存,出现了很大的误差。: j# O" D8 @8 i9 p9 o4 X& q1 i* [1 ~
#echo 3 >/proc/sys/vm/drop_caches #清理可回收的内存
5 Q9 z$ |+ ~+ F% ]  f& F# free
. m" E: q9 {& N. `4 Ttotal used free shared buffers cached
6 y4 x. b7 y# c* nMem: 49423560 13609680 35813880 0 26764 11686608 #仍有11G不可以回收,因为是共享内存,不具备回收属性。
  ^# c" D4 A6 s0 l( x. h- v4 J-/+ buffers/cache: 1896308 47527252 # free计算可用内存,算入了共享内存,得到47G% w4 h8 j' B: f" O7 J
Swap: 2104472 0 2104472
3 v- C( @% P! S8 [# ipcs -m -u
# p. Q3 j7 N+ Y, e------ Shared Memory Status --------+ u6 i, H0 _: M% R, X
segments allocated 20/ `# h. Q, x* w9 T
pages allocated 4980737
* N& k1 E# V8 Z7 }2 E$ |* jpages resident 2883591 #共享内存有2883591 * 4 = 11534364 kB ~= 11G
3 A8 `5 ?. `0 e: b# o, r# _pages swapped 0
4 N6 z. \, E  p  W- @Swap performance: 0 attempts 0 successes
1 V0 s, x, R* d4 F( G0 F% g# afree
( E: C1 ]% j0 |3 v1 S5 |% aTotal: 49423560 kB #物理内存总计
6 [; k. J$ y  ^: G8 M. j& c. }Free: 35816908 kB #未被使用的内存
4 U- O9 J* J4 IReclaimable: 179348 kB #被使用了但是可以自动回收的内存
* j2 E* h3 T* J* `, ^- v/ s$ qAvailable: 35996256 kB #afree统计出来的可用内存去除了11G的共享内存。' u2 B/ [) D+ m6 J
( T( q0 l) f2 f1 K3 K
2. 修正可用内存少计算的情况(SUSE10) ! P4 n2 k5 X( U5 V1 C: s
如下环境,在进行了大量文件访问操作后,系统中缓存了大概600M的inode和dentry。8 ~( K/ ^# A9 W9 L$ _* F
# free  M, p3 w* k( y# |/ u2 j
total used free shared buffers cached
* h0 m9 c8 w& b- D# U$ zMem: 3987316 1080908 2906408 0 163056 163848   d9 C. m8 r( r1 p& o# D
-/+ buffers/cache: 754004 3233312 #没有统计slab中的可以回收的inode和dentry。' J3 u8 ?: Q1 r1 \
Swap: 2104472 0 2104472
' @( Q# ?+ r: x' p) B  p4 _8 Z1 \# afree: W% G1 x# z2 X) {4 g. Y
Total: 3987316 kB; n. z* b3 R7 n/ y/ U( T# j' f* T! t
Free: 2906384 kB% z/ H. i- N' D  ?
Reclaimable: 958888 kB
% x/ c8 I3 V8 l' U4 S' [4 ?; SAvailable: 3865272 kB #afree统计了slab中的可以回收的inode和dentry。: D9 j* N  ?: d0 U
# echo 3 >/proc/sys/vm/drop_caches #释放600M的inode和dentry缓存。' U4 l! }, L' @: Q0 |' b+ Q$ E
# free7 Y' g+ \  B  V  J
total used free shared buffers cached
4 K9 d3 R6 k7 W& Y: ]# L# R& [; N5 sMem: 3987316 93628 3893688 0 17780 8948  N0 J; e8 u' f" j
-/+ buffers/cache: 66900 3920416 #执行drop_caches后,增加了600M,说明cached少统计了可回收的slab缓存。这600M即使不执行drop_caches也都是可用的,系统在需要的时候会自动回收。( d9 \: L' p* {) N+ U) u

$ @; k% q  Q3 t注:afree通过解析slabinfo文件统计可回收的slab,其实suse10的内核有一个全局变量slab_reclaim_pages维护了准确的可回收数量,但没有暴露给给用户空间,因此更准确的方法其实就是写一个模块,把这个变量导出到来。/ _1 I& O' `" F9 V  r  T

# `9 K2 x$ Z% k& u8 w  K% tafree代码如下:
  I: ]# }  h/ \2 E* ~7 C! z& B* n( L% l9 P; d$ x3 m
#!/bin/sh& w- Y' U1 n% b" x/ Z
#- X6 ~, x5 c* C4 p2 l2 B
#Get accurate available memory. 4 o% e) ]! Y, ~# Y3 k2 m0 h6 g' p8 M
#
' w8 ?& n# `, g( F% p# m3 J+ C7 D# \; q
function get_meminfo()/ |9 b0 _& @( I7 a$ p
{5 o1 a" o4 [* f7 `4 d
   grep -w $1 /proc/meminfo | awk -F' ' '{print $2}'9 s1 n" i7 u5 U+ c
}# b% w. Q7 ^' z+ j2 N9 o8 |
function show_meminfo()
% B8 Z  j# n3 E- [+ Y# H{
! c7 q9 [$ E- ]  {) {   printf "%s\t%10d kB\n" "$1" "$2"0 a; u5 O& S( d) z  }
}) Y: U4 Y5 _( [0 }7 w
0 P5 b# y0 a. x! B: B& K
PAGE_SIZE=4 #kB, for x86
1 N# r' W+ J$ D9 t' Z) U- Sfunction get_shmem_from_ipcs()8 j' O: ^3 `4 S3 a7 l9 C
{
0 Y/ F/ s& r5 Q% E   local _shm=0, d, C& Y  p% T+ A' B3 \2 F
   _shm=$(ipcs -m -u | grep 'pages resident' | awk -F' ' '{print $3}')
; l: a5 C+ Z* m: t  R, M   echo $((_shm * PAGE_SIZE))
: ]0 h( g1 k" n! f}1 b8 {$ K7 f' z; }5 O# C9 d/ S
function get_tmpfs_from_df()
( v: n  `, O' j& G' v" n4 y( l  z+ F{
6 p9 B  C& o1 M3 t   local _size=""% u2 N; g* }  V
   _size=$(df -k | awk -F' ' 'BEGIN{total=0} {if ($1 == "tmpfs" || $1 == "devtmpfs" || $1 == "shm")total+=$3}END{print total}')
1 a) {+ Z! ^0 R3 T0 r# }! e   echo $_size
1 c; p5 j. m/ i' M: j}
* e/ v# _/ j- J/ a4 @# {/ S; q5 W2 x, y+ Q1 M
#inode, dentry and buffer_head is reclaimable , U  y  @$ E* f) K$ x
function get_slab_reclaimable_from_slabinfo()9 z  G" q* n9 g' Q% c
{( @. l$ ]* M; q
   local _size=""
% H4 W) ]: P/ Z: l3 H/ r   _size=$(awk -F' ' 'BEGIN{total=0} {if ($1~/inode/ || $1~"dentry" || $1 == "buffer_head")total+=($3 * $4)}END{printf "%d\n", total / 1024}' /proc/slabinfo)
4 D. l/ y" C) A# p   [ -z "$_size" ] && _size=0
2 }: [1 w+ b4 A% [   echo $_size , q5 Z( q0 @- Y
}
) d9 t" B. u* S, ~: [, [, f' `# m+ v9 M
free=$(get_meminfo MemFree)5 B+ ~$ K6 C" v- q  K# _
total=$(get_meminfo MemTotal)   E+ ~9 ^: {* ^
cached=$(get_meminfo Cached)
5 n' t7 e4 g, Y0 J7 X' x& t: Ibuffer=$(get_meminfo Buffers)
3 I! U. r& Q3 b" T* Rswapcached=$(get_meminfo SwapCached)
' A/ d9 q* D# H7 u1 M2 L1 {# Mshmem=$(get_meminfo Shmem)* I7 f" \, ?5 |
slab_reclaimable=$(get_meminfo SReclaimable)
3 u2 x+ _8 }8 t  r: infs_unstable=$(get_meminfo NFS_Unstable)
3 ?) O3 Q8 H7 K2 I; M4 ^6 y& Q' f! [: Y9 B
#the kernel does not support, no 'Shmem' field in /proc/meminfo, we use ipc and df.+ h7 s3 F* j- @7 [; p
if [ -z "$shmem" ] ; then
/ p! c6 n1 Y1 x3 k4 z' ~  N9 G3 P   shmem=$(( $(get_shmem_from_ipcs) + $(get_tmpfs_from_df) ))
' W1 N5 U/ H: s0 lfi  c0 L) @# R9 J2 m1 T* w

0 p4 T  Y; ~, T2 Q+ V#the kernel does not support, no 'SReclaimable' field in /proc/meminfo, we use /proc/slabinfo.
8 l; C( Q! ^) ^4 e% |3 c) F4 p$ t7 r! gif [ -z "$slab_reclaimable" ]; then
% {2 w" ?: q+ j   slab_reclaimable=$(get_slab_reclaimable_from_slabinfo)
: P5 Y2 X  `$ m) k6 Nfi
5 z* D, t* T7 z
, Q2 J: R5 T* W% Q7 m#the kernel does not support, no 'NFS_Unstable' field in /proc/meminfo, we use null. :)
0 A+ R1 D( U7 @$ U( _2 A2 rif [ -z "$nfs_unstable" ]; then % N5 b0 K. @& J- d" t; ~2 c" w
   nfs_unstable=0
0 ~- q& Q0 q9 \- h3 u( p# ?fi
/ ]+ Q/ P4 B5 T2 X( J* w: f9 G! I- \% \" Y- ]0 W, u

* b0 R* i6 R; ~! b8 I7 m/ Qreclaimable=$((cached + buffer + slab_reclaimable + swapcached + nfs_unstable - shmem))
; b$ Z- [2 Y( [0 Iavailable=$((free + reclaimable))
. {5 [, p2 T3 z& ^show_meminfo "Total: "        
5 a: B1 T6 u. G$ [* U8 n2 T0 z3 l0 g0 t* J* X. S, \. T# u5 D( B; D3 b
说说free命令; {( j- u) r1 X' m

4 S( k7 F+ Z* t5 Pfree是个常用命令,几乎每个接触、使用linux的用户都会用到它。但往往对它的统计输出会有一些困惑,这一方面和Linux内存管理机制有关,另一方面Linux在内存统计上也确实有些不足和问题。
6 N# Z0 {- H' c3 o9 v5 V关键在于两个字段,buffers和cached。- V- k9 L. S+ H5 }9 v$ P- ^

% q/ h8 {- D  v; I# q - e# R# a7 \7 |3 `  i( B) C1 p

5 S! @  ]0 S$ X5 S! |/ i- U你经常会发现Linux系统用了一段时间后,内存所剩无几,free命令,一看,内存全跑到 buffers和cached里面了;这个现象是正常的。访问过的磁盘文件的元数据及内容,内核都会缓存起来。这些缓存就是磁盘缓存。- C8 {! g9 ?; }

) ?9 E1 h7 z1 `  B1 P% ]0 { 3 t% a% Z; z' a2 o& o$ |
9 J# Z% H6 ?. G: q7 X
Linux磁盘缓存设计特点(设计理念):. {0 c' b  x6 l0 i- _, ?" x
除了系统运行必须的一小部分保留外,只要有剩余内存,只要需要,就会用给磁盘缓存。(没有一个参数可以让你限定缓存的上限。2.6内核之前有一个限定参数,后来给取消了)" y+ y4 z' F  q
3 i( `8 O0 ^5 S! @7 X& x
所以会经常看到内存所剩无几的现象,这是缓存机制导致的,对应用是透明,在有内存需要时,这些内存会释放。这个过程对应用是透明的,应用可以认为系统的可用内存包括buffers和cached。
; j$ y, n" h$ h. ]6 P& b1 B7 {4 X2 e- w! ?- Y. J) w1 m. H

  G. U/ o$ `: h1 F5 d  p1 g这种设计,在大多数服务器应用场景下都有比较好的性能表现。可以说是比较可取的。4 U3 h0 d  a! c, G  Z

2 g' C6 S& h, G, ~/ P  O设计本身没有问题,但free命令显示的buffers和cached并不能和磁盘缓存完全对应,这是实现细节上的不足和问题。
# ~) B8 o9 q0 C( F+ Q4 p; N0 ?: _4 m; c1 O
; T5 X  H5 \  ~( ]
1. buffers和cached包含了不属于磁盘缓存的内容。
7 J/ j- U) `6 C2 J/ K$ Q9 @. O/ p; V由于buffers和cached实际上就是内核为所有文件映射分配的物理页的总和(page cache)。
" A4 o& @; f) q/ F/ x( \% Z但内核中的“文件概念”是广泛的,不仅包含了真正位于磁盘上的文件,还包含了为特殊需要创建的虚拟文件,比如:$ D' u$ H/ c, Q/ E0 d

+ R" g! j) H: m/ f6 v% `进程间的共享内存(通过shmget API创建的内存),内核建立一个虚拟的文件和共享内存关联起来。(通过pmap命令你可以看到进程拥有的共享内存地址空间的映射字段是/SYSVXXXX字样,不是匿名的)。
# L6 b7 Y8 o1 r) t+ w3 ]
: |. A, S* v/ B5 A7 y8 K1 x
6 ]  V7 _$ v5 }1 q6 Z# a7 A非常的不幸,这些虚拟文件映射关联的page,也被算入了free命令显示的cached字段。但这部分内存没有缓存属性,在内存不足时不能按缓存的方式来回收。(使用echo 3 >/proc/sys/vm/drop_caches,无法释放掉这部分内存)1 q/ `! `6 v0 z- c5 K, ^& P

/ V1 `0 w: {* h% D* ~+ ^2 S这个问题,会带来一些麻烦。比如,按照常规理解,某产品设计内存占用过高告警的条件是,空闲内存+buffers+cached小于内存总和的20% 。一般情况下没有问题,但如果产品使用了大量的共享内存,告警将失去作用。
) b, }! N" K* ]1 F& q, |2 U3 c
2 f) n1 ]8 g6 u0 V& c" P* y& k5 W2 o, }  p
2. buffers和cached遗漏了部分属于磁盘缓存的内容。
( Y9 X+ M  k$ A4 [. n# q- S+ g5 k还 是由于buffers和cached只是内核为所有文件映射分配的物理页的总和。在文件系统方面,还有一部分缓存是不和文件映射相关联的,比如内核分配的 inode对象,在文件关闭时,并不会立即释放,具备缓存的属性。这部分不在基于文件映射的页面里,而是通过slab(内核内存池)分配的。/ A3 g% f! i+ i# Z6 v

( q& z1 y1 B7 ?6 m

该用户从未签到

2#
发表于 2020-1-10 17:53 | 只看该作者
Linux可用内存的统计方法
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-11-25 17:37 , Processed in 0.156250 second(s), 24 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表