|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
! V. Z9 _9 J, C$ A* u- v6 w
背景
$ _( e6 D- ^5 ~& _& N由于Linux缓存机制的设计,系统对缓存的使用是非常狠的,所以经常会看到某些环境内存只剩几十兆了,而应用只用了不到一半。所以在计算可用内存的时候,一定要算上缓存的部分。% G; ]+ z4 Z: N& r+ S
通常方法,就是通过free命令首行free+cached+buffers计算,或者直接使用第二行的free字段。但这个方法有时仍然会造成比较大的误差,导致性能监控等方面的问题。
: c& v' B7 W" M, d$ m; I比如系统中使用了大量的共享内存会造成多计算可用内存;再比如对大量的文件做了查询(find / ?!!!),会导致少计算可用内存。对于这点我在《说说free命令》中有详细的说明。这里就不再赘述了。
6 c8 F1 y1 W4 Q' F
# M; X! C/ h0 ^! n8 S4 t4 NSUSE11 SP1基于2.6.32内核,内核暴露了更多的统计接口给用户空间,把slab分为可回收和不可回收两类指标来统计。free命令也对应做了修改。解决了free命令少计算可用内存的问题。但多计算的问题还是存在。* A3 o* }+ Q; U4 B8 w) [3 i# ?6 z0 O/ |% ?
因此,在这里对统计可用内存的方法做了个总结。供需要的同学参考。(后面有空可能会开发一个统计工具)! Y9 Z8 x( F+ }! d) A/ |
其中SUSE10由于内核版本过低(2.6.16)暴露信息不足,下面的方法仍然不能很精确,但相比通过free命令简单统计而言,一般不会造成比较大的误差。! ^: V2 n) Y2 e* Y8 o, \2 G n
; P* |- d. a/ e i* O- Y
可用内存定义5 Y" B8 G( P( h* X
包括未被使用的空闲内存,以及已经被使用但用作缓存可以自动回收的部分。
0 g/ S/ I. d0 @. l- V3 z0 C$ t
5 Y# w) c* F* n5 Q9 j A3 NSUSE 10可用内存统计方法
: J: R4 J7 r2 b总内存:free命令首行total字段。
' T+ T2 t' ~, u5 q* q/ e空闲内存:free命令首行free字段。
/ y5 W! j( q4 a. w: a缓存:free命令首行buffers字段+cached字段。
& }1 m" N4 s; S* k! S0 K2 K7 o修正值1:cached字段包含了共享内存和tmpfs内存文件系统占用的内存。需要减去这两部分。这两部分内存可通过ipcs -m -u和df 命令获取。 0 K6 a0 @- }; I+ w d7 x) K3 A5 K
修正值2:cached字段漏掉了内核slab中可以自动回收的内存,比如xxx_inode_cache和dentry_cache。这两部分的内存的计算方法是解析/proc/slabinfo。
' \/ l8 W/ s# @2 K最终的可用内存计算方法:
9 O( u, y1 r$ ?5 s3 H空闲内存+缓存-修正值1+修正值2/ Y0 Y9 z$ c/ q9 n+ Y0 D
- H( N9 n l, V' `1 YSUSE 11可用内存统计方法' M/ U/ C4 d5 C# r4 n
总内存:free命令首行total字段。0 v/ v2 x3 t- h
空闲内存:free命令首行free字段。" b& ? `/ y( v0 H' f- a; B" O) m
缓存:free命令首行buffers字段+cached字段。
- Y0 s! R+ L5 w( o% J修正值1:cached字段包含了共享内存和tmpfs内存文件系统占用的内存。需要减去这两部分。这两部分内存之和可通过/proc/meminfo的Shmem字段直接获取。
& f, n M7 n# F) k! }最终的可用内存计算方法:
5 Z0 U. W* `+ \' x" L. D, R+ ?空闲内存+缓存-修正值10 M" N9 ~% w" D. z( ?) R7 u
4 w8 I$ k. a3 i8 U6 \ Q, X/ p附1:ipcs获取共享内存占用物理内存大小! r3 w+ T# Y8 |
# ipcs -m -u% |3 \/ [6 P' F k; [, |( ~
------ Shared Memory Status --------
: q E: t$ R, O# g# d) Msegments allocated 4
5 T; S! P- a- E: U' [$ L' X% K" Bpages allocated 786433
a0 u7 f3 {2 H8 L$ D! V! `/ s" H9 Fpages resident 2 #使用这个字段,单位是页,X86下一个页是4kB
$ x: d% z, ~5 x6 K; X5 a% t Opages swapped 0
2 N8 B, Z8 I5 J5 `Swap peRFormance: 0 attempts 0 successes: X' C! Y+ L+ o! A- O' g" Z, T
3 u$ g" M" _2 C. |2 \; M$ v* v附2:df获取tmpfs占用物理内存大小/ Z' C% _. } S
# df
/ @7 H- [' J: ]. t1 D7 |Filesystem 1K-blocks Used Available Use% Mounted on9 [( x) t1 U! G2 D
/dev/sda2 20972152 4427900 16544252 22% /" u7 Y. k( d) i3 f1 e5 G
devtmpfs 24711780 160 24711620 1% /dev #Used字段表示实际占用物理内存
. W1 w( Q z; n3 R7 \8 _tmpfs 24711780 0 24711780 0% /dev/shm #Used字段表示实际占用物理内存
; D; S/ e G& ]) m" g5 T9 ]( L* s4 S/dev/sda5 1052184 59188 992996 6% /boot( M! m& b- f2 Y$ m' m) O+ [- m: D4 ^
/dev/sda9 83888824 16852500 67036324 21% /iso0 ^6 j+ g5 Z" h/ p) f3 x* x
/dev/sda7 10490040 1142876 9347164 11% /opt
* p) r9 O- |# \( N* ^2 N0 v- v7 Q. r7 M/dev/sda10 83888824 9421200 74467624 12% /src- L0 s$ t& r3 ~1 F# p& i4 S4 E% Z# b
/dev/sda8 20972152 3475104 17497048 17% /usr
& k8 H E2 h/ E( w6 H8 c- `/dev/sda6 5245016 328392 4916624 7% /var
3 z) ?! |5 Q- s4 Z) P9 E- x9 f5 `& s s$ R. `
3 E* P$ t& Y8 e' y///////////////////增加统计工具////////2011.6.29///////////////////////////////////////////////////; W& L* W2 ^: U
统计工具(在SUSE10、SUSE11验证通过)
' h$ n0 N F/ u5 N7 }1. 修正可用内存多计算的情况(SUSE10 SUSE11)
# N4 w7 f! {" w& X! M' c9 [如下环境,在进行了大量共享内存创建使用后,free命令统计可用内存,出现了很大的误差。
# T9 N1 D. X. ~#echo 3 >/proc/sys/vm/drop_caches #清理可回收的内存6 \2 ~: w! {8 @8 W$ \& v$ k
# free
& S$ q! g& j/ x& Qtotal used free shared buffers cached
. A K" p" y7 L% {% k) I1 IMem: 49423560 13609680 35813880 0 26764 11686608 #仍有11G不可以回收,因为是共享内存,不具备回收属性。" v1 E3 K* A: c6 v
-/+ buffers/cache: 1896308 47527252 # free计算可用内存,算入了共享内存,得到47G
' f" y9 w5 l9 P1 A; D0 BSwap: 2104472 0 2104472
A+ ~5 S( ]2 D: T# ipcs -m -u: e- R1 h+ S. z! A. o) K/ C6 f( J- a8 z0 G
------ Shared Memory Status --------
( g4 X# h; v: d R- _segments allocated 20
7 j# {( E6 h: ^& w4 I; cpages allocated 49807370 u- f( v7 S* v6 S5 A0 ]8 q; t/ a
pages resident 2883591 #共享内存有2883591 * 4 = 11534364 kB ~= 11G
A, U$ D2 h5 g3 e1 ]pages swapped 0
" {: o' \0 {: }0 g, iSwap performance: 0 attempts 0 successes
* x) Z) r- g7 |0 E5 c8 u' s4 K6 ^# afree( W* C4 Y4 |1 K" ?1 z& _
Total: 49423560 kB #物理内存总计! L n! {! @3 j l0 [2 t
Free: 35816908 kB #未被使用的内存
. ^; s8 C! T6 j4 V7 R& ^Reclaimable: 179348 kB #被使用了但是可以自动回收的内存4 N" J* ^1 ?' o$ v& t- j2 W! b
Available: 35996256 kB #afree统计出来的可用内存去除了11G的共享内存。# r1 H5 k/ c# Q# _" ^2 N' s. _
* S( ^) y. d/ _# [2. 修正可用内存少计算的情况(SUSE10) ; @7 t& j1 c/ ]; j
如下环境,在进行了大量文件访问操作后,系统中缓存了大概600M的inode和dentry。
* q+ Q% c$ n6 T( K: {# free
2 I3 ]& S3 O2 E1 `total used free shared buffers cached8 Z* r3 }1 ]& j2 P; `* T! R1 K
Mem: 3987316 1080908 2906408 0 163056 163848
: `! a3 n$ a& p-/+ buffers/cache: 754004 3233312 #没有统计slab中的可以回收的inode和dentry。
0 n. ?' e8 ^' u5 R' H8 [7 Q: A7 X2 DSwap: 2104472 0 2104472# C/ s3 n8 P, L+ }( p
# afree
; S2 M0 s5 t6 o3 V+ `$ [* ?% }Total: 3987316 kB
9 g4 b% ~ F/ y* j1 ^3 }6 SFree: 2906384 kB0 w/ ]6 |, P8 x8 P* T
Reclaimable: 958888 kB
" r( e5 e, e6 Z& SAvailable: 3865272 kB #afree统计了slab中的可以回收的inode和dentry。
6 J. _" W) o# s2 s# echo 3 >/proc/sys/vm/drop_caches #释放600M的inode和dentry缓存。5 i4 h# a3 x! b- z
# free% P0 U8 g5 i4 z7 f. `
total used free shared buffers cached/ k, D. B5 H4 @2 x) t, @
Mem: 3987316 93628 3893688 0 17780 8948
5 C& ]! O a$ u0 z$ i- l-/+ buffers/cache: 66900 3920416 #执行drop_caches后,增加了600M,说明cached少统计了可回收的slab缓存。这600M即使不执行drop_caches也都是可用的,系统在需要的时候会自动回收。
+ r- s- }) k/ a8 r3 B" Z. v$ }/ a$ q3 D1 T
注:afree通过解析slabinfo文件统计可回收的slab,其实suse10的内核有一个全局变量slab_reclaim_pages维护了准确的可回收数量,但没有暴露给给用户空间,因此更准确的方法其实就是写一个模块,把这个变量导出到来。 |7 B: r* b' S; A( _3 X
* k( U6 Z6 O& D" F- x
afree代码如下:
. ?& [6 I* |. s: ]
8 F+ C* o' Z' P5 O* b1 j* B+ B#!/bin/sh+ v: s! G$ d8 x. f
#
7 ?$ t- z0 b: y3 }#Get accurate available memory.
9 B& G5 r( `/ c6 z' P#. S9 P5 u: f9 [# s' ]" f' e
8 D9 ]3 ?) B. n9 Qfunction get_meminfo()
1 k5 Z( _; _8 N{
e1 u2 s# A2 R1 Y8 ]; n3 w6 ] grep -w $1 /proc/meminfo | awk -F' ' '{print $2}'# I; ^" F; t! ~- g9 h$ l( ]5 g
}% p6 t& a: j$ |+ S
function show_meminfo()
( i4 }6 z2 s3 u v7 v) z" E{. F1 r3 F) X3 W. @+ c
printf "%s\t%10d kB\n" "$1" "$2"
' ~9 o" P& S# ~: ?0 k m}
/ q7 c5 ^1 L& i% i; S: K! n
/ O; B8 h- K! B% r. r+ g. }PAGE_SIZE=4 #kB, for x86 7 [9 `0 D1 V# }8 D0 N
function get_shmem_from_ipcs()
/ b! \7 Q8 v# W: [" z# T/ W! b7 _{4 s' @& Q; {4 ^3 |/ d7 u+ \
local _shm=0+ G7 r+ B; _' U; B
_shm=$(ipcs -m -u | grep 'pages resident' | awk -F' ' '{print $3}'); l a& q9 n+ m9 X) @
echo $((_shm * PAGE_SIZE))) h( b1 g4 j* d' j$ M& m- m
}
+ T y+ Y+ ?+ ^5 Yfunction get_tmpfs_from_df(). s# @# S# W$ l& E
{
1 ?4 v3 i2 c) d9 f local _size=""
% j" V/ x3 V/ [+ A- B( E! k7 T _size=$(df -k | awk -F' ' 'BEGIN{total=0} {if ($1 == "tmpfs" || $1 == "devtmpfs" || $1 == "shm")total+=$3}END{print total}'), N$ b; `' v3 }9 t2 D% `
echo $_size 7 q! g; d& J) F3 g# [2 b
}
5 w0 {) O9 L9 ]6 F( C% ]- G
0 G* a9 f. d) a& w4 M#inode, dentry and buffer_head is reclaimable
/ K" h s/ J' L. r, g7 s9 ^function get_slab_reclaimable_from_slabinfo()
2 i/ c- P7 p" b8 W- S{4 q+ U) e, u; _5 V4 @$ D
local _size="" n2 L% f3 @ S, }" \* M
_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)' T& S/ g; j+ K @
[ -z "$_size" ] && _size=0! S, t9 j: O. x' J" g- g' d
echo $_size / T) V# X' q; e- F5 a2 b+ h# P# p
}! C; ?0 Z& M3 l+ O5 {
1 C* N* P- G# n! }' n% V1 g4 p- c) `free=$(get_meminfo MemFree)
- V- \9 l$ ?7 w2 u$ ^; J$ itotal=$(get_meminfo MemTotal) 4 z7 ]! u6 |- f" g
cached=$(get_meminfo Cached): W/ b4 M! Z1 W+ S ]5 i, C1 P
buffer=$(get_meminfo Buffers)2 |, r# T6 i: N ^/ j, @8 Z R# e
swapcached=$(get_meminfo SwapCached)2 T1 @$ Y% n2 t J( L
shmem=$(get_meminfo Shmem)
8 V3 D& o6 |2 Uslab_reclaimable=$(get_meminfo SReclaimable)
" |$ E7 G+ x# h4 Ynfs_unstable=$(get_meminfo NFS_Unstable)
; L7 N G6 m! C4 v5 s
. D- K, L: V5 u; r8 n#the kernel does not support, no 'Shmem' field in /proc/meminfo, we use ipc and df.0 K) x9 D3 u( } H$ V; w% K
if [ -z "$shmem" ] ; then
1 e9 A! |( N1 T8 S& p3 }& f shmem=$(( $(get_shmem_from_ipcs) + $(get_tmpfs_from_df) ))
, N+ {2 W% `2 Z7 @+ a# Vfi
* D; I4 E* r9 N6 W b S2 z8 K# J* y1 _+ r* @. @* L) }
#the kernel does not support, no 'SReclaimable' field in /proc/meminfo, we use /proc/slabinfo.
% P( c" a' ^2 Dif [ -z "$slab_reclaimable" ]; then, L p* \/ J# d4 ^
slab_reclaimable=$(get_slab_reclaimable_from_slabinfo), [2 k, O ?* O+ u% \ w. ]
fi
2 E0 y. @+ [" Q; k4 d" y' V8 K* [: K, e
#the kernel does not support, no 'NFS_Unstable' field in /proc/meminfo, we use null. :)
8 Q2 f# T K1 L* n& Hif [ -z "$nfs_unstable" ]; then
: Y7 D6 M7 J* @' r! E nfs_unstable=05 K# ^" X. B# e" x' m
fi
0 h4 d5 Q/ o+ _5 I2 o4 |4 N6 Z3 {& @% O/ g# _! I, E4 k7 R: m( G
- H- ]. Z; N% a' Jreclaimable=$((cached + buffer + slab_reclaimable + swapcached + nfs_unstable - shmem))
# O( W* `. x% i0 t3 B1 kavailable=$((free + reclaimable))
/ N: z, e( ~4 l/ ?4 \) [! a cshow_meminfo "Total: "
4 q5 ^# _( @8 X+ R+ O
- x5 T" ]7 M0 M0 w' N4 K( J: V) A说说free命令0 {2 m9 T3 R i' y* Z# b
6 C$ x* ^0 ~& P0 P! s2 w
free是个常用命令,几乎每个接触、使用linux的用户都会用到它。但往往对它的统计输出会有一些困惑,这一方面和Linux内存管理机制有关,另一方面Linux在内存统计上也确实有些不足和问题。& ]" z; R! f% A& s, ^# C5 e
关键在于两个字段,buffers和cached。
9 r% B$ }% K4 D- e" m4 i) |. m1 J4 T: \% l1 k, J
2 v! s, A: p% W: v( f) X
/ {* S, e" }# L0 g
你经常会发现Linux系统用了一段时间后,内存所剩无几,free命令,一看,内存全跑到 buffers和cached里面了;这个现象是正常的。访问过的磁盘文件的元数据及内容,内核都会缓存起来。这些缓存就是磁盘缓存。/ H1 ~0 {" | Y* G9 L; K* q0 m3 Q
) J# m& |+ x, b+ i* o+ H. _8 e
, H) v# H5 \& j) |# J7 P4 p* X& l
9 [1 k8 O, D) |) w: rLinux磁盘缓存设计特点(设计理念): x& B# P# B* S/ g
除了系统运行必须的一小部分保留外,只要有剩余内存,只要需要,就会用给磁盘缓存。(没有一个参数可以让你限定缓存的上限。2.6内核之前有一个限定参数,后来给取消了)8 l5 p) O; a" Z9 B
" t5 W0 L% d4 p$ r; Y" l所以会经常看到内存所剩无几的现象,这是缓存机制导致的,对应用是透明,在有内存需要时,这些内存会释放。这个过程对应用是透明的,应用可以认为系统的可用内存包括buffers和cached。
% l# q! d' W! _/ y1 f: u3 {5 M* P, @$ X4 `
5 o' D/ D" P3 _$ F2 p1 X
这种设计,在大多数服务器应用场景下都有比较好的性能表现。可以说是比较可取的。
. x8 k% x1 c" ]& k. s t) u( i1 Y. f" \0 a0 Y* y% G
设计本身没有问题,但free命令显示的buffers和cached并不能和磁盘缓存完全对应,这是实现细节上的不足和问题。. `& e& p8 W2 x0 X+ t
9 t- F+ t$ W1 Y0 o5 }) @+ o4 L
: C* R; F6 S/ @% [0 Y1. buffers和cached包含了不属于磁盘缓存的内容。
! u$ ~" H2 `4 ~, Q由于buffers和cached实际上就是内核为所有文件映射分配的物理页的总和(page cache)。
$ P `9 @; M! t1 D但内核中的“文件概念”是广泛的,不仅包含了真正位于磁盘上的文件,还包含了为特殊需要创建的虚拟文件,比如:
+ t& n+ [0 A. i% U" j% ~8 J8 P O( }; D9 ~# x& D* {8 F A
进程间的共享内存(通过shmget API创建的内存),内核建立一个虚拟的文件和共享内存关联起来。(通过pmap命令你可以看到进程拥有的共享内存地址空间的映射字段是/SYSVXXXX字样,不是匿名的)。
3 o7 z9 c! ?. k3 z. Q+ C$ d5 D, M% G: V' ~
! o' H8 y4 I) c! w- n5 }, \+ r U, s
非常的不幸,这些虚拟文件映射关联的page,也被算入了free命令显示的cached字段。但这部分内存没有缓存属性,在内存不足时不能按缓存的方式来回收。(使用echo 3 >/proc/sys/vm/drop_caches,无法释放掉这部分内存)
, J1 |1 i5 I$ }/ [. _, X0 w+ y2 G2 z% W7 a; v% q; j
这个问题,会带来一些麻烦。比如,按照常规理解,某产品设计内存占用过高告警的条件是,空闲内存+buffers+cached小于内存总和的20% 。一般情况下没有问题,但如果产品使用了大量的共享内存,告警将失去作用。
- y$ l6 J" u+ E+ |* @- `' a2 L1 a- V6 Q, }& }: @8 B
, n4 ~# ~ q( j8 f
2. buffers和cached遗漏了部分属于磁盘缓存的内容。 V1 H" I8 H3 |; {8 n4 Q
还 是由于buffers和cached只是内核为所有文件映射分配的物理页的总和。在文件系统方面,还有一部分缓存是不和文件映射相关联的,比如内核分配的 inode对象,在文件关闭时,并不会立即释放,具备缓存的属性。这部分不在基于文件映射的页面里,而是通过slab(内核内存池)分配的。0 }1 L3 O/ m9 s2 N; q) s3 G5 g
, G$ R4 h2 H, M9 I& f
|
|