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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
+ T2 `6 `5 E- J
背景6 g: L* p( H4 @" |3 E% l" l/ b
由于Linux缓存机制的设计,系统对缓存的使用是非常狠的,所以经常会看到某些环境内存只剩几十兆了,而应用只用了不到一半。所以在计算可用内存的时候,一定要算上缓存的部分。
' U" i: M6 M6 j$ X9 w- |' D1 w( Q通常方法,就是通过free命令首行free+cached+buffers计算,或者直接使用第二行的free字段。但这个方法有时仍然会造成比较大的误差,导致性能监控等方面的问题。
5 A& ~" d  v# [5 E) V比如系统中使用了大量的共享内存会造成多计算可用内存;再比如对大量的文件做了查询(find / ?!!!),会导致少计算可用内存。对于这点我在《说说free命令》中有详细的说明。这里就不再赘述了。/ m* E: q" p; S  z& R* R3 N$ F* g

5 b& {4 a: W9 r4 x1 vSUSE11 SP1基于2.6.32内核,内核暴露了更多的统计接口给用户空间,把slab分为可回收和不可回收两类指标来统计。free命令也对应做了修改。解决了free命令少计算可用内存的问题。但多计算的问题还是存在。) e9 a3 q7 _* \' b9 g
因此,在这里对统计可用内存的方法做了个总结。供需要的同学参考。(后面有空可能会开发一个统计工具)
* ?  @( L' q$ P6 c. _其中SUSE10由于内核版本过低(2.6.16)暴露信息不足,下面的方法仍然不能很精确,但相比通过free命令简单统计而言,一般不会造成比较大的误差。
& n# e, X6 b; M, u: f; y
8 J: z- \4 b6 J$ Z: S, E8 m* ]可用内存定义+ L4 l. ^; [" h4 A2 m7 `1 k
包括未被使用的空闲内存,以及已经被使用但用作缓存可以自动回收的部分。) I/ H! `; v! c! c; f

6 K7 G; J) D! L* iSUSE 10可用内存统计方法
% A. ^1 [; D) L: g: M0 Q总内存:free命令首行total字段。2 ?  p1 z) E! Z  M4 m
空闲内存:free命令首行free字段。
# M, N$ d0 F& `. f  H7 ]( b缓存:free命令首行buffers字段+cached字段。
3 O0 \) D' z+ W2 U. g* f; W* o修正值1:cached字段包含了共享内存和tmpfs内存文件系统占用的内存。需要减去这两部分。这两部分内存可通过ipcs -m -u和df 命令获取。
' x5 m9 l1 d2 z0 N# G5 v- I  q' m修正值2:cached字段漏掉了内核slab中可以自动回收的内存,比如xxx_inode_cache和dentry_cache。这两部分的内存的计算方法是解析/proc/slabinfo。" F2 O+ j" M- m- m
最终的可用内存计算方法:
" q3 u4 E, i4 G* y$ T空闲内存+缓存-修正值1+修正值2
& @' a4 _# d- p. J0 M& L+ o! P% \8 _8 Y8 W
SUSE 11可用内存统计方法9 _1 m% ?* ~, o  r
总内存:free命令首行total字段。, {* K3 |1 I9 M! }
空闲内存:free命令首行free字段。! e. |9 d" ^) [/ {# d0 x
缓存:free命令首行buffers字段+cached字段。' C% S( D6 ?; u
修正值1:cached字段包含了共享内存和tmpfs内存文件系统占用的内存。需要减去这两部分。这两部分内存之和可通过/proc/meminfo的Shmem字段直接获取。
- \# k5 I1 g: n% X. L% N4 y最终的可用内存计算方法:  o$ A) r9 k1 ~7 P; ]5 b+ O
空闲内存+缓存-修正值19 `. |4 g* z$ u2 m8 R; k

5 ~( m% B5 z9 q6 S附1:ipcs获取共享内存占用物理内存大小2 D* o  g  r, g6 B( B. ^
# ipcs -m -u6 r8 [8 m4 _, P
------ Shared Memory Status --------* g/ T( Z, w; s6 A. X' O
segments allocated 4
0 u( }$ G7 b' c( V: z0 hpages allocated 786433
" Y1 G6 o+ a$ A* p; a9 mpages resident 2 #使用这个字段,单位是页,X86下一个页是4kB8 K  @% v1 j0 M4 |& Z9 T
pages swapped 03 t8 I0 {: J& n5 f1 d: e6 i
Swap peRFormance: 0 attempts 0 successes' m: a$ ]9 D& i
' E) o# E; b  y
附2:df获取tmpfs占用物理内存大小
  m0 l7 t  y0 V0 I" O- Y# df
; S6 t. P" {& R( l# xFilesystem 1K-blocks Used Available Use% Mounted on
( b7 b% Y# p& N1 M6 G$ k) w/dev/sda2 20972152 4427900 16544252 22% /
8 N& \  l+ F# v+ d$ w" U. v) fdevtmpfs 24711780 160 24711620 1% /dev #Used字段表示实际占用物理内存
0 y+ n' u. p5 M2 G' Wtmpfs 24711780 0 24711780 0% /dev/shm #Used字段表示实际占用物理内存3 m2 ~+ v$ z$ Y& s: w+ ~* |
/dev/sda5 1052184 59188 992996 6% /boot7 S5 B" K# C8 j6 ^! n& f+ x
/dev/sda9 83888824 16852500 67036324 21% /iso
0 q" U; V5 a$ ]3 G7 h: T5 t/dev/sda7 10490040 1142876 9347164 11% /opt
! F. T$ `8 `( Z/dev/sda10 83888824 9421200 74467624 12% /src
) R+ {  O' P1 D; m9 V/dev/sda8 20972152 3475104 17497048 17% /usr
9 Z' A: L( T8 |" \/dev/sda6 5245016 328392 4916624 7% /var
9 M# _4 i, S  s0 y; z. @9 n
7 W" D) E) z& E; n3 I! A$ E* J6 }
1 k; C' L- G' @/ s///////////////////增加统计工具////////2011.6.29///////////////////////////////////////////////////' |' s: G, Z1 s9 V8 ^0 U
统计工具(在SUSE10、SUSE11验证通过)# e) a7 {  k8 N9 A' Y5 e
1. 修正可用内存多计算的情况(SUSE10 SUSE11), ?3 o* U/ b1 f8 C3 A+ R" [( y3 F
如下环境,在进行了大量共享内存创建使用后,free命令统计可用内存,出现了很大的误差。
4 I) A) [# n  m4 U! ^' z#echo 3 >/proc/sys/vm/drop_caches #清理可回收的内存( Z' G6 P1 \: y% d+ `% o
# free  Q6 f( s1 Z1 V1 H3 X% i1 C0 l
total used free shared buffers cached  `) |7 }  ^  ^+ j
Mem: 49423560 13609680 35813880 0 26764 11686608 #仍有11G不可以回收,因为是共享内存,不具备回收属性。4 k% c4 r) i9 J# v1 }: F( q
-/+ buffers/cache: 1896308 47527252 # free计算可用内存,算入了共享内存,得到47G
9 A: Y; [. r8 J3 j8 H2 u" gSwap: 2104472 0 2104472
0 Y1 J6 b" T  f$ ?" g- G# ipcs -m -u$ F6 F1 x. i/ P8 C
------ Shared Memory Status --------
& U/ t/ ?* F3 A6 S) f5 [segments allocated 20
, Q- f. U2 g9 o- b& e0 epages allocated 4980737
% T( \+ X2 {9 v& Z/ qpages resident 2883591 #共享内存有2883591 * 4 = 11534364 kB ~= 11G 5 \3 G5 G# n, Q1 i+ v: b7 r& P
pages swapped 0. z, C4 ~8 v5 v$ O! k5 L! r
Swap performance: 0 attempts 0 successes
" L/ V, w7 N; B% R0 _# afree
6 C- n" K9 Q: G, L4 o+ ^1 kTotal: 49423560 kB #物理内存总计
) x- e  _- X' t* V( aFree: 35816908 kB #未被使用的内存
  j' D- W- J" d: RReclaimable: 179348 kB #被使用了但是可以自动回收的内存8 M$ P- K" Z& B9 P: K
Available: 35996256 kB #afree统计出来的可用内存去除了11G的共享内存。
$ p: D  ?* N3 v: k/ c8 d9 a+ {3 ?, i! z/ s- p
2. 修正可用内存少计算的情况(SUSE10)
5 ^0 u9 `$ m, l& m* @如下环境,在进行了大量文件访问操作后,系统中缓存了大概600M的inode和dentry。
6 m" u9 {2 G9 `- u5 Y# free2 X' f1 Q: w1 S3 ^  f; c
total used free shared buffers cached, q6 ?/ q/ Y: r$ j, F
Mem: 3987316 1080908 2906408 0 163056 163848
0 l; I4 S/ |. D1 [-/+ buffers/cache: 754004 3233312 #没有统计slab中的可以回收的inode和dentry。4 r/ Y+ M1 H' g% m8 j$ U  @# m% F
Swap: 2104472 0 21044725 I$ A+ S& W& n+ ]0 K
# afree) G. m  Y* C, `
Total: 3987316 kB' W$ B# e+ `; g  i6 ~+ A$ W* `" W
Free: 2906384 kB
3 ~4 X1 H; P% t$ p! t4 Z" uReclaimable: 958888 kB
# n" t7 ~: t2 ~( Y) ]! eAvailable: 3865272 kB #afree统计了slab中的可以回收的inode和dentry。
) ~5 ?- [+ B1 t! S0 f6 Z# echo 3 >/proc/sys/vm/drop_caches #释放600M的inode和dentry缓存。# x( x+ n5 m8 s6 O. X
# free
/ ~" `: n- c: t. a$ d0 l5 Ntotal used free shared buffers cached5 [  l! q8 g: z# L! i* s
Mem: 3987316 93628 3893688 0 17780 8948
, |! j4 Q6 d; Q' R1 k  ?7 @-/+ buffers/cache: 66900 3920416 #执行drop_caches后,增加了600M,说明cached少统计了可回收的slab缓存。这600M即使不执行drop_caches也都是可用的,系统在需要的时候会自动回收。% x9 I9 s  X0 S; |
( G" x% X; ]' ~9 C5 v9 w
注:afree通过解析slabinfo文件统计可回收的slab,其实suse10的内核有一个全局变量slab_reclaim_pages维护了准确的可回收数量,但没有暴露给给用户空间,因此更准确的方法其实就是写一个模块,把这个变量导出到来。
9 q8 g! e) Q+ z! D  \7 a6 i  J. b; M
afree代码如下:
, I; x8 T5 d0 Z5 ~" F, d% ]% x5 i$ W6 y5 o6 I8 m
#!/bin/sh
  ~* {- B' c& D4 R4 U& x  ^& p#' o* H9 c  ]( m
#Get accurate available memory. 6 {0 a: ?+ f+ d, C/ o8 a
#7 s9 d$ X" @) n& @! m! a

; b8 H1 ?( ?& K) K: z) W) Afunction get_meminfo()
4 ^. c6 {6 {: h+ b0 N" j9 i- H{7 b- x7 z0 A/ H: R
   grep -w $1 /proc/meminfo | awk -F' ' '{print $2}'
7 D7 R7 v8 A7 T4 J/ _: ^, A}
  _4 t/ S9 x/ M, r0 afunction show_meminfo()8 r# a) `* }. H$ O. ^# J9 T
{
% Y9 a' A1 w: q/ t& p5 w   printf "%s\t%10d kB\n" "$1" "$2"3 S1 a# U; Y4 X/ V7 k* E6 R
}& F- M. F( c7 P' U8 L* o4 E

9 J! y! ?* U! ?" BPAGE_SIZE=4 #kB, for x86
. x, d2 r, {' r7 e' {function get_shmem_from_ipcs()9 \4 A, ~! i# F
{) Z2 m" C  ^6 d- v# q
   local _shm=0# V0 k" [( t* b+ p$ j; Q4 J5 ?
   _shm=$(ipcs -m -u | grep 'pages resident' | awk -F' ' '{print $3}')
0 p7 b: ?2 M% k" y4 i2 x& S   echo $((_shm * PAGE_SIZE))
( m0 l: M+ _) ^( r* j) J! ]}( z6 g3 X+ Z6 D- J$ a! A% B
function get_tmpfs_from_df()% b! v$ n! s  A$ `. ]
{* B9 b2 G; w9 Q) ]  f
   local _size=""# O' V$ S- G8 U2 X1 |
   _size=$(df -k | awk -F' ' 'BEGIN{total=0} {if ($1 == "tmpfs" || $1 == "devtmpfs" || $1 == "shm")total+=$3}END{print total}')# j2 i4 a* f8 |
   echo $_size
/ G# \. z" x; W% L) n}
3 ~. f' D0 y( j- V1 f6 i0 Z7 P. k
: c6 Z6 Y) D; s. [#inode, dentry and buffer_head is reclaimable
8 |7 x9 F5 v: [2 D3 v  T# s4 O( Xfunction get_slab_reclaimable_from_slabinfo()- h, L' u% l1 h2 z5 m- D5 [" `1 r
{! S( x) e/ V8 U. K4 w1 |( ^- a* }
   local _size=""
+ |  p" x0 i; A3 O5 I% Z( w   _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)
0 T7 ~; _/ `- F2 x% r! {   [ -z "$_size" ] && _size=0: ]' Z& h! k$ U9 }) L
   echo $_size # }" q( q0 B+ K: d
}
& Y( q7 E1 F) e$ X
6 `$ e$ @- f2 j' F* b( Ufree=$(get_meminfo MemFree)
( Y. m) r/ H' K0 ?total=$(get_meminfo MemTotal)
7 t& L# F1 q/ I0 H/ ncached=$(get_meminfo Cached)
6 i7 }8 h3 k7 T2 V: _9 Nbuffer=$(get_meminfo Buffers)/ \( @" k7 c, l. ^) B# B3 z
swapcached=$(get_meminfo SwapCached)
6 l& Q- B' o) J, E& b" cshmem=$(get_meminfo Shmem)1 W7 I* V! V1 t
slab_reclaimable=$(get_meminfo SReclaimable)6 |+ u& z! b% {5 t
nfs_unstable=$(get_meminfo NFS_Unstable)2 X, L2 J" c) I" `7 k
6 ?# Y+ }4 U. u% E) r% ~
#the kernel does not support, no 'Shmem' field in /proc/meminfo, we use ipc and df.3 Q0 \, C4 @# S& m0 `9 F1 [
if [ -z "$shmem" ] ; then) x1 C' w7 _$ k' M3 x' N8 O5 s! ^
   shmem=$(( $(get_shmem_from_ipcs) + $(get_tmpfs_from_df) ))% E% Y( m3 ?! V, X, A# W
fi! @* w" A9 {3 G' O. p$ Q6 S
; G6 j! T5 ~4 y' s. a# s$ i8 j% d" Q) f
#the kernel does not support, no 'SReclaimable' field in /proc/meminfo, we use /proc/slabinfo.3 v$ e# e2 x+ P! d' [: V$ p
if [ -z "$slab_reclaimable" ]; then
- ~6 V4 V# r/ d9 R7 o  x   slab_reclaimable=$(get_slab_reclaimable_from_slabinfo)
4 Z6 @6 i2 O, H7 Cfi
& j5 a( f( Z3 u! j% }% o, \* ~, y1 a
#the kernel does not support, no 'NFS_Unstable' field in /proc/meminfo, we use null. :)
; W8 W- g7 }: Y- @5 n( q+ dif [ -z "$nfs_unstable" ]; then . R. T7 \* w% G
   nfs_unstable=0' O1 B  Y6 S/ J' c& @
fi+ V" D$ s( O& c' M* z" {5 G
: J  j, \) J& e. Z2 R

' u; i. ?& q/ g# mreclaimable=$((cached + buffer + slab_reclaimable + swapcached + nfs_unstable - shmem))
$ m, j$ Y2 b, s( b; B. U6 h0 s) Cavailable=$((free + reclaimable))/ z; G# N% d) q
show_meminfo "Total: "        ) z& K$ o# H* f) z7 P4 N

2 c4 s# f; S6 e5 l说说free命令, S" E# w/ Y% O0 N' q- o' R+ t+ ^

9 O) z  X0 z- i& y! ^, qfree是个常用命令,几乎每个接触、使用linux的用户都会用到它。但往往对它的统计输出会有一些困惑,这一方面和Linux内存管理机制有关,另一方面Linux在内存统计上也确实有些不足和问题。  W; w/ C9 R1 s9 x" y& b# k
关键在于两个字段,buffers和cached。
# c# ?* J& f, w
! [- ?$ n& o* Q! E' `# I
( O6 U+ I" Y/ u5 ^1 u
! Q$ K/ W$ f$ Q& ]3 J你经常会发现Linux系统用了一段时间后,内存所剩无几,free命令,一看,内存全跑到 buffers和cached里面了;这个现象是正常的。访问过的磁盘文件的元数据及内容,内核都会缓存起来。这些缓存就是磁盘缓存。2 l: \3 w- N4 x8 Q  k
, E0 S" i( ?$ C0 x$ C' |8 @

1 N. D) L6 G0 X( E% n  Q/ i3 `4 a8 g8 f1 y: L
Linux磁盘缓存设计特点(设计理念):
# {' O0 x* _3 |% k7 b除了系统运行必须的一小部分保留外,只要有剩余内存,只要需要,就会用给磁盘缓存。(没有一个参数可以让你限定缓存的上限。2.6内核之前有一个限定参数,后来给取消了)3 f* u6 R8 \6 M2 ~4 b

- F. K) b; F( w. f$ }6 \3 P所以会经常看到内存所剩无几的现象,这是缓存机制导致的,对应用是透明,在有内存需要时,这些内存会释放。这个过程对应用是透明的,应用可以认为系统的可用内存包括buffers和cached。
) g' c, g, m# B4 c# k& X  `0 y- \$ [  L' p+ t, p; r5 H
  K# R5 H8 e( A% C; E
这种设计,在大多数服务器应用场景下都有比较好的性能表现。可以说是比较可取的。
8 P! @1 J+ C- g' F/ H
5 ]3 T% }( Q( p. [设计本身没有问题,但free命令显示的buffers和cached并不能和磁盘缓存完全对应,这是实现细节上的不足和问题。
. g0 Q" Y  E$ L, T! E
  q- E& K5 J0 z' w
7 c; T% Y# k9 U* A- @* b" U1. buffers和cached包含了不属于磁盘缓存的内容。
" x3 ?3 n! ?4 y5 I$ E由于buffers和cached实际上就是内核为所有文件映射分配的物理页的总和(page cache)。" ]; @9 l3 O- X# k- H
但内核中的“文件概念”是广泛的,不仅包含了真正位于磁盘上的文件,还包含了为特殊需要创建的虚拟文件,比如:
3 Z# e( z' h. @, x' M5 @. k! K/ Z8 w7 q: Q. d5 j! B" F4 ^, z4 k
进程间的共享内存(通过shmget API创建的内存),内核建立一个虚拟的文件和共享内存关联起来。(通过pmap命令你可以看到进程拥有的共享内存地址空间的映射字段是/SYSVXXXX字样,不是匿名的)。9 ^4 B0 [, b. W& B/ U
0 r; u; x5 L# l& w$ j- {) q' j

& q2 M4 A( m5 z; k0 j. s非常的不幸,这些虚拟文件映射关联的page,也被算入了free命令显示的cached字段。但这部分内存没有缓存属性,在内存不足时不能按缓存的方式来回收。(使用echo 3 >/proc/sys/vm/drop_caches,无法释放掉这部分内存)0 L7 c+ U8 P2 Q4 H1 I7 ?& |
/ [8 v$ ~1 g( Q: Y. I- I
这个问题,会带来一些麻烦。比如,按照常规理解,某产品设计内存占用过高告警的条件是,空闲内存+buffers+cached小于内存总和的20% 。一般情况下没有问题,但如果产品使用了大量的共享内存,告警将失去作用。
# K) H) ?, d* m0 [+ L" v$ }7 s* `

& _$ ~# Y. y8 w+ V2 d/ U5 {2. buffers和cached遗漏了部分属于磁盘缓存的内容。
6 u3 H8 _1 X* J  K/ R/ f% y还 是由于buffers和cached只是内核为所有文件映射分配的物理页的总和。在文件系统方面,还有一部分缓存是不和文件映射相关联的,比如内核分配的 inode对象,在文件关闭时,并不会立即释放,具备缓存的属性。这部分不在基于文件映射的页面里,而是通过slab(内核内存池)分配的。
$ s5 A4 P& F5 i3 ]) U( x  T/ j" j! s- P1 X

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-11-25 18:43 , Processed in 0.171875 second(s), 23 queries , Gzip On.

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

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

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