EDA365电子论坛网

标题: 转一篇大神给咱们提供的Linux可用内存的统计方法 [打印本页]

作者: mytomorrow    时间: 2020-1-10 10:36
标题: 转一篇大神给咱们提供的Linux可用内存的统计方法

. H! y: [" T0 N- ~背景# ~3 l" V- o6 x1 d2 `. J
由于Linux缓存机制的设计,系统对缓存的使用是非常狠的,所以经常会看到某些环境内存只剩几十兆了,而应用只用了不到一半。所以在计算可用内存的时候,一定要算上缓存的部分。  }: y  u# ~9 K( K
通常方法,就是通过free命令首行free+cached+buffers计算,或者直接使用第二行的free字段。但这个方法有时仍然会造成比较大的误差,导致性能监控等方面的问题。
% o; ^& Z( V# v9 e  b比如系统中使用了大量的共享内存会造成多计算可用内存;再比如对大量的文件做了查询(find / ?!!!),会导致少计算可用内存。对于这点我在《说说free命令》中有详细的说明。这里就不再赘述了。, z2 [- ]3 h" a9 M& }
0 U% j5 b/ H8 \0 h9 ^5 G
SUSE11 SP1基于2.6.32内核,内核暴露了更多的统计接口给用户空间,把slab分为可回收和不可回收两类指标来统计。free命令也对应做了修改。解决了free命令少计算可用内存的问题。但多计算的问题还是存在。
" ?  {" a% @% M+ z0 o3 i因此,在这里对统计可用内存的方法做了个总结。供需要的同学参考。(后面有空可能会开发一个统计工具)
! t7 c- C  O8 N4 c/ O1 G' e其中SUSE10由于内核版本过低(2.6.16)暴露信息不足,下面的方法仍然不能很精确,但相比通过free命令简单统计而言,一般不会造成比较大的误差。
) [9 y8 s3 o5 q+ h2 B$ }$ A  _) D2 V
- C, X5 s* V8 z6 j; _可用内存定义
+ P* L0 h+ p# _# Z5 ]0 c* J7 n! l4 J4 B包括未被使用的空闲内存,以及已经被使用但用作缓存可以自动回收的部分。2 B( b; q: W; m5 v& M) m
- p8 W7 @% W8 ?+ O9 v+ f/ ]6 M( y
SUSE 10可用内存统计方法- ^  |* z+ U. X
总内存:free命令首行total字段。
  P+ [( V6 n; p) F空闲内存:free命令首行free字段。  |6 s! O) ^: W2 c& c8 {9 S& Z
缓存:free命令首行buffers字段+cached字段。
& W) e8 K/ m2 L. p$ D修正值1:cached字段包含了共享内存和tmpfs内存文件系统占用的内存。需要减去这两部分。这两部分内存可通过ipcs -m -u和df 命令获取。 2 ^' x2 a. T, o; w! Y
修正值2:cached字段漏掉了内核slab中可以自动回收的内存,比如xxx_inode_cache和dentry_cache。这两部分的内存的计算方法是解析/proc/slabinfo。! v. E! l7 ]$ e* x7 W9 `: H" n
最终的可用内存计算方法:
! Q) j* o; P7 o空闲内存+缓存-修正值1+修正值2
5 @/ }: s; n: O; w* `! S" W' t: ?& ^4 Y$ b4 i+ x
SUSE 11可用内存统计方法3 U0 r5 m* q9 N8 `* M8 y
总内存:free命令首行total字段。0 u. K/ x+ s! W; F* x0 ~+ ]" L7 s
空闲内存:free命令首行free字段。
2 |6 {5 q1 L+ a缓存:free命令首行buffers字段+cached字段。
# [; @- [  t$ A. D2 g0 f  J修正值1:cached字段包含了共享内存和tmpfs内存文件系统占用的内存。需要减去这两部分。这两部分内存之和可通过/proc/meminfo的Shmem字段直接获取。
! x7 k8 B. `! W9 K% q最终的可用内存计算方法:
8 m; E# X6 C4 z7 q6 \空闲内存+缓存-修正值1
! J3 M7 z/ L8 h; K2 v0 X! N3 j4 w- m1 z5 ^: b# a' Z2 r
附1:ipcs获取共享内存占用物理内存大小4 t; {3 W6 u$ P: ?: N
# ipcs -m -u# }' K, ]7 }+ |* R% V7 {9 K
------ Shared Memory Status --------
0 E  q7 ]: s0 |segments allocated 4
5 i) e6 n, f4 z4 i1 I: x$ H" ?! p$ Spages allocated 786433- H# m' R$ }  ^) n. D# i7 l$ r
pages resident 2 #使用这个字段,单位是页,X86下一个页是4kB
3 b4 C+ D$ H7 l, O( ~2 ^pages swapped 0
* l. I& m* b) m( G  k: MSwap performance: 0 attempts 0 successes
3 W' v7 T1 Y. z. Y
- K. m$ F4 P4 I$ P5 T: c1 b附2:df获取tmpfs占用物理内存大小7 `+ M2 ~3 @! i2 z: b4 G
# df
; K9 j  m" O. {6 p+ `+ X/ O8 J% qFilesystem 1K-blocks Used Available Use% Mounted on6 h, ~  _9 z5 ]1 {: X) k
/dev/sda2 20972152 4427900 16544252 22% /
7 f) `8 Y5 [/ C: ]1 V  m, z; pdevtmpfs 24711780 160 24711620 1% /dev #Used字段表示实际占用物理内存: V2 b6 H' [& s
tmpfs 24711780 0 24711780 0% /dev/shm #Used字段表示实际占用物理内存
6 ~) r0 c0 u6 d2 J9 J- r% [/dev/sda5 1052184 59188 992996 6% /boot
2 P/ Z* I/ }  ^9 A+ ]: i' i  W) q/dev/sda9 83888824 16852500 67036324 21% /iso
% f1 Z: V; B5 q  X  h/dev/sda7 10490040 1142876 9347164 11% /opt: I/ M5 `" t2 I9 T# R0 _/ [+ r1 V
/dev/sda10 83888824 9421200 74467624 12% /src
* L* o& i7 S3 i3 X2 H7 k4 ]/dev/sda8 20972152 3475104 17497048 17% /usr
3 v% o, j9 F: O" g" R: u- R/dev/sda6 5245016 328392 4916624 7% /var
! [& J6 L4 m: ]" |8 E7 K1 s- H% _3 B* Z. r. ^# ?
9 `; i  l; c  O- a; x. ?
///////////////////增加统计工具////////2011.6.29///////////////////////////////////////////////////) `" l" I* y4 H4 k
统计工具(在SUSE10、SUSE11验证通过): e- X% @) A* D, k8 i8 ?9 {$ b
1. 修正可用内存多计算的情况(SUSE10 SUSE11)
# M- V' v0 N, c% s* s如下环境,在进行了大量共享内存创建使用后,free命令统计可用内存,出现了很大的误差。
% n4 O3 v. W& a4 c. ]9 h#echo 3 >/proc/sys/vm/drop_caches #清理可回收的内存
; X$ ?3 N" j. O: u7 W% F) W# free0 E5 j+ s6 C  l3 L
total used free shared buffers cached" W& C7 w2 M* T# M9 C* S
Mem: 49423560 13609680 35813880 0 26764 11686608 #仍有11G不可以回收,因为是共享内存,不具备回收属性。( N! u% P  A& k) X+ \
-/+ buffers/cache: 1896308 47527252 # free计算可用内存,算入了共享内存,得到47G
# n6 \4 Z; [- G" nSwap: 2104472 0 2104472! j2 H+ u9 H& h
# ipcs -m -u
: I4 ]! u  V5 w------ Shared Memory Status --------
8 q! |! G5 g) R" I* X+ w( g7 M7 ?segments allocated 20
/ M7 P! Q7 P5 I$ _! i1 ^9 Upages allocated 4980737
- B* k, D: T# d9 R5 npages resident 2883591 #共享内存有2883591 * 4 = 11534364 kB ~= 11G
7 p1 a: W+ u+ P: w  Dpages swapped 0& q# U  B. }) A; z. V# U# p, {; b+ p
Swap performance: 0 attempts 0 successes1 ]. u2 P2 v6 f+ o/ d. Q) Y
# afree1 t3 s; E6 h3 D8 B/ Z# ~. b  T* ?
Total: 49423560 kB #物理内存总计. a: ^& C0 _' S, [9 `) o0 v$ I
Free: 35816908 kB #未被使用的内存9 |/ Y- I9 Y5 E8 S; K
Reclaimable: 179348 kB #被使用了但是可以自动回收的内存5 p# g8 e. U, ]" |
Available: 35996256 kB #afree统计出来的可用内存去除了11G的共享内存。& W' f! {# L; M. q+ D
4 o0 L; n5 G& v+ A# l
2. 修正可用内存少计算的情况(SUSE10)
# M" P0 G$ ~4 u6 {0 H9 R, z, k* t如下环境,在进行了大量文件访问操作后,系统中缓存了大概600M的inode和dentry。
, E: I! ^/ X) Y: d# free9 o! W" g" l: {- g9 `1 V( R# L
total used free shared buffers cached2 v) z+ L, U0 ]4 T
Mem: 3987316 1080908 2906408 0 163056 163848 ; b  U  g0 k9 c* g/ i; y: ?0 ^
-/+ buffers/cache: 754004 3233312 #没有统计slab中的可以回收的inode和dentry。) V' Q3 m) [, u6 r5 R* {
Swap: 2104472 0 21044727 \8 m& Q( }; I( Q# X) p/ b& o/ c
# afree
7 K3 }- d8 u' v) S# N. t# XTotal: 3987316 kB$ O8 |1 z! w, P* Y1 ?8 @5 s+ K# u
Free: 2906384 kB
3 Y& X# z( [' u  ]0 vReclaimable: 958888 kB" w+ q5 D1 W) q
Available: 3865272 kB #afree统计了slab中的可以回收的inode和dentry。! ^$ X% x8 }2 G( h7 }) q
# echo 3 >/proc/sys/vm/drop_caches #释放600M的inode和dentry缓存。$ Z' c$ X+ d! @6 _1 T6 @! v# Y
# free
: t6 ]2 W2 V& X4 e% Q  i  i9 ptotal used free shared buffers cached. Z* L% C( h7 w" L$ ]- j9 z5 f
Mem: 3987316 93628 3893688 0 17780 8948
: C% X6 h. z# E$ Y" X) t' N-/+ buffers/cache: 66900 3920416 #执行drop_caches后,增加了600M,说明cached少统计了可回收的slab缓存。这600M即使不执行drop_caches也都是可用的,系统在需要的时候会自动回收。
- k3 ~# w8 ~1 _  }+ t8 M9 Q5 N, z* f- z
注:afree通过解析slabinfo文件统计可回收的slab,其实suse10的内核有一个全局变量slab_reclaim_pages维护了准确的可回收数量,但没有暴露给给用户空间,因此更准确的方法其实就是写一个模块,把这个变量导出到来。( y2 h- j! `6 ]% e# _1 Y: K

4 e3 L+ ]/ ^# |; p. Xafree代码如下:
0 S* }9 r; `0 `. h6 d/ |9 N- h4 C0 \+ E3 B! s2 W2 T5 t! U" }
#!/bin/sh1 H* E1 S, E! r7 J: R5 ~
#
. q- j; u$ s: [9 d/ Z#Get accurate available memory.
% ^  ~0 \' G5 b$ C## f$ G. M6 A+ G3 y
# |( F$ K8 B9 Z% p2 h/ `
function get_meminfo()
# x, q0 w! X  f9 E7 F{
" z: |; F( S. m5 O   grep -w $1 /proc/meminfo | awk -F' ' '{print $2}'
5 T8 \$ O: \8 W. }}2 T% z  K/ p- J2 B/ W
function show_meminfo()8 N: O, z  \+ P5 R. ~' _
{; \! e8 j$ ~- X  ]" X. M# ~. \
   printf "%s\t%10d kB\n" "$1" "$2"/ G( `  E# j, ]) f  h" b
}, o1 v- V3 D$ j" O
1 S. }$ M. s  F' a
PAGE_SIZE=4 #kB, for x86 : j$ \# I5 d5 y4 n1 h  W  ?  t) T
function get_shmem_from_ipcs()3 E! g' K% q0 W, W
{* ?$ y% s3 M. ]( y' x4 w( {7 Q# i6 x
   local _shm=0
" R. I( D" ^0 j$ p% Q   _shm=$(ipcs -m -u | grep 'pages resident' | awk -F' ' '{print $3}')
& m% l# ]4 R5 w& @+ p   echo $((_shm * PAGE_SIZE))
. i5 r' K9 ~, M: S: x}- c. S& D3 U7 w" s* O/ j
function get_tmpfs_from_df()' N4 ?' O. c$ C! l) R' r
{
3 j( W0 i) _4 H1 r6 h   local _size=""
( r$ K& j  \7 D- `0 T3 K   _size=$(df -k | awk -F' ' 'BEGIN{total=0} {if ($1 == "tmpfs" || $1 == "devtmpfs" || $1 == "shm")total+=$3}END{print total}')5 K  g# O& O3 D" k( u0 a% `
   echo $_size
7 o- v. p! I* s. ]1 [- {}
' |6 n0 W! L" d) D9 \, \; P5 c: |9 B2 B+ m! a: ~" W
#inode, dentry and buffer_head is reclaimable
" Z" v7 y1 d+ T; B7 Tfunction get_slab_reclaimable_from_slabinfo()6 N& O: B. U- y
{
7 k2 o9 [1 Q* c% ~   local _size=""8 ~- ?" [2 P/ b  n/ q9 X4 C; A* D8 ^
   _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)- F: X, P& v" ~. A. v, _9 h. ?
   [ -z "$_size" ] && _size=01 F, q' `! I& H) q5 _! b
   echo $_size * h2 l8 j& N  F* w/ r
}
" Q% _! A5 \. u  l* }
7 C2 B9 U- e+ c! Xfree=$(get_meminfo MemFree)* Y  W# R3 Z) \' E7 F+ @& h
total=$(get_meminfo MemTotal)
+ i: [9 g7 K4 B* l/ b6 Ocached=$(get_meminfo Cached)1 y9 C2 ^* H, d' L6 A+ ?# G
buffer=$(get_meminfo Buffers)5 H% P/ C& R( n3 m2 B3 Z, Y
swapcached=$(get_meminfo SwapCached)
9 l  U8 d, X2 vshmem=$(get_meminfo Shmem)& E7 _, T! g, F
slab_reclaimable=$(get_meminfo SReclaimable)( c" c# H0 o, b+ M7 ~
nfs_unstable=$(get_meminfo NFS_Unstable)
, E5 ^+ a- Z/ |+ m
' D6 X8 e& }/ m" h+ t4 G2 R#the kernel does not support, no 'Shmem' field in /proc/meminfo, we use ipc and df.
( {2 A( R% w; f7 F! R  ]! K3 eif [ -z "$shmem" ] ; then
1 \& ^$ Y5 I2 q# s   shmem=$(( $(get_shmem_from_ipcs) + $(get_tmpfs_from_df) ))' O% B, O* X3 S" _6 ]) ^
fi
8 R. }! g: z# E- a9 z# R- l# J: r/ _! \/ x8 j
#the kernel does not support, no 'SReclaimable' field in /proc/meminfo, we use /proc/slabinfo.) h. W) ?9 L( J' i
if [ -z "$slab_reclaimable" ]; then3 L# a, t; |. G" j( W* S# g- F5 C& n
   slab_reclaimable=$(get_slab_reclaimable_from_slabinfo)' o2 n  x" ~2 t7 u6 `. V
fi' X; g$ d" j" `' e& X

( G  t4 s, O% h1 N+ c#the kernel does not support, no 'NFS_Unstable' field in /proc/meminfo, we use null. :)
; x$ C1 h9 V/ [( bif [ -z "$nfs_unstable" ]; then
* |, R3 l2 v1 \2 V   nfs_unstable=0; ^9 o* B' p2 C
fi
) J  ~% @  o$ o( a  G, Z9 e! _

* I& S/ |8 d$ Treclaimable=$((cached + buffer + slab_reclaimable + swapcached + nfs_unstable - shmem))
+ y- B7 b/ K' g+ Z- ~available=$((free + reclaimable))
4 |  N- p. ^% [5 D5 Tshow_meminfo "Total: "        
4 t7 y- n8 Y/ w4 }5 v5 Z1 ]% {+ v  w, v2 X2 h! \( G
说说free命令* ^) l1 ]0 w' N0 c* H# E: A7 U
9 O1 M. A! C) Y8 n
free是个常用命令,几乎每个接触、使用linux的用户都会用到它。但往往对它的统计输出会有一些困惑,这一方面和Linux内存管理机制有关,另一方面Linux在内存统计上也确实有些不足和问题。8 A3 v7 L  X- r6 i1 E; |+ Z8 `
关键在于两个字段,buffers和cached。  P( \0 t% d5 m0 S/ ]1 T+ N

, b* I3 i. [8 Y9 C% j, q 6 {/ S0 h! n! z8 D& }6 ^

* k0 x" |8 D4 c1 \" h6 r2 `你经常会发现Linux系统用了一段时间后,内存所剩无几,free命令,一看,内存全跑到 buffers和cached里面了;这个现象是正常的。访问过的磁盘文件的元数据及内容,内核都会缓存起来。这些缓存就是磁盘缓存。& Q( Y7 n8 @' R% b. V0 N( F6 @2 K
" h1 L7 H% U* G, U+ p& A- L

6 O; A% |" V3 G( h6 k& e2 _
) W. U  A( Z8 Z$ d  U/ F4 x; q; @4 ]1 |Linux磁盘缓存设计特点(设计理念):
+ m1 Y- r5 T' s除了系统运行必须的一小部分保留外,只要有剩余内存,只要需要,就会用给磁盘缓存。(没有一个参数可以让你限定缓存的上限。2.6内核之前有一个限定参数,后来给取消了)
0 l2 G1 g! @/ E6 y3 w: D' X, b4 U6 V, ~4 r* X
所以会经常看到内存所剩无几的现象,这是缓存机制导致的,对应用是透明,在有内存需要时,这些内存会释放。这个过程对应用是透明的,应用可以认为系统的可用内存包括buffers和cached。
+ z# l. v  F6 I) k8 I
8 t/ _* D" O$ f
0 D: N- U+ p% L4 _% h/ q' V% T这种设计,在大多数服务器应用场景下都有比较好的性能表现。可以说是比较可取的。
: k' \; Q6 {8 x1 _& e' q- M  a5 p1 I% p9 f- {9 I( @9 h2 O
设计本身没有问题,但free命令显示的buffers和cached并不能和磁盘缓存完全对应,这是实现细节上的不足和问题。0 T, N3 s2 ~$ x% W1 H8 X$ E% {

, Y0 m2 e, x! j+ o  @8 z7 \& O* R2 }
0 U$ c8 Q4 z7 {1. buffers和cached包含了不属于磁盘缓存的内容。: U6 O6 J8 i. ]$ b* v
由于buffers和cached实际上就是内核为所有文件映射分配的物理页的总和(page cache)。2 A) S9 D/ J9 t2 E
但内核中的“文件概念”是广泛的,不仅包含了真正位于磁盘上的文件,还包含了为特殊需要创建的虚拟文件,比如:
  z0 q7 X! J% K0 p: \. x3 Q$ D% P; {. o
进程间的共享内存(通过shmget API创建的内存),内核建立一个虚拟的文件和共享内存关联起来。(通过pmap命令你可以看到进程拥有的共享内存地址空间的映射字段是/SYSVXXXX字样,不是匿名的)。
1 |" O4 X* Y; D! \7 V1 C8 W: R0 y6 I' g0 X( b
9 E) |; k, V9 O. _7 r: f4 z
非常的不幸,这些虚拟文件映射关联的page,也被算入了free命令显示的cached字段。但这部分内存没有缓存属性,在内存不足时不能按缓存的方式来回收。(使用echo 3 >/proc/sys/vm/drop_caches,无法释放掉这部分内存)3 s3 t- b/ a" A0 |3 j

, I, z  w, @% @1 q! i3 ~: i/ O这个问题,会带来一些麻烦。比如,按照常规理解,某产品设计内存占用过高告警的条件是,空闲内存+buffers+cached小于内存总和的20% 。一般情况下没有问题,但如果产品使用了大量的共享内存,告警将失去作用。
/ l2 q+ _" Z% y+ [/ w3 {8 s: d' h$ y- ~! |3 ^! X! z( E

; h( C8 _) ]; ~8 H  B% p. S7 K2. buffers和cached遗漏了部分属于磁盘缓存的内容。/ l& \5 `, }0 x4 N( U( t
还 是由于buffers和cached只是内核为所有文件映射分配的物理页的总和。在文件系统方面,还有一部分缓存是不和文件映射相关联的,比如内核分配的 inode对象,在文件关闭时,并不会立即释放,具备缓存的属性。这部分不在基于文件映射的页面里,而是通过slab(内核内存池)分配的。# q$ K' R' d  G1 ]

( y% c5 e/ U) I/ w+ O
作者: xixihahaheihei    时间: 2020-1-10 17:53
Linux可用内存的统计方法




欢迎光临 EDA365电子论坛网 (https://bbs.eda365.com/) Powered by Discuz! X3.2