. 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