|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
0 ^! |4 T7 K2 W( t
背景" P: P& a/ E3 W
由于Linux缓存机制的设计,系统对缓存的使用是非常狠的,所以经常会看到某些环境内存只剩几十兆了,而应用只用了不到一半。所以在计算可用内存的时候,一定要算上缓存的部分。
* X P {* s5 t9 K: H# U& ~% I. m通常方法,就是通过free命令首行free+cached+buffers计算,或者直接使用第二行的free字段。但这个方法有时仍然会造成比较大的误差,导致性能监控等方面的问题。5 i$ P' F% e5 a, Z; K3 e' r4 D
比如系统中使用了大量的共享内存会造成多计算可用内存;再比如对大量的文件做了查询(find / ?!!!),会导致少计算可用内存。对于这点我在《说说free命令》中有详细的说明。这里就不再赘述了。4 A; \( N/ J- O1 ]. e" |1 H# O' R
/ g5 V4 @. s: L2 i+ q4 t
SUSE11 SP1基于2.6.32内核,内核暴露了更多的统计接口给用户空间,把slab分为可回收和不可回收两类指标来统计。free命令也对应做了修改。解决了free命令少计算可用内存的问题。但多计算的问题还是存在。
7 d0 h9 y% \$ k$ e. r, C4 m因此,在这里对统计可用内存的方法做了个总结。供需要的同学参考。(后面有空可能会开发一个统计工具) A8 Z% c! Z& [6 c( z
其中SUSE10由于内核版本过低(2.6.16)暴露信息不足,下面的方法仍然不能很精确,但相比通过free命令简单统计而言,一般不会造成比较大的误差。3 l E& @9 q$ `- N, q( m
: X! J+ b' n) L& ]可用内存定义
, B Z2 [% F/ B6 x1 {1 ^包括未被使用的空闲内存,以及已经被使用但用作缓存可以自动回收的部分。! ]0 f6 X* d6 I( m; E2 l0 Q2 Q
) N+ ^7 {+ @8 ?# M
SUSE 10可用内存统计方法
. Z R0 r: f7 C. s, x W8 R* [总内存:free命令首行total字段。
( o/ M% z1 J0 `6 I/ |# U0 K空闲内存:free命令首行free字段。
4 `6 n' Z8 y* s) i- {9 ?缓存:free命令首行buffers字段+cached字段。
6 E* J/ V! G$ F: r8 U. b( c修正值1:cached字段包含了共享内存和tmpfs内存文件系统占用的内存。需要减去这两部分。这两部分内存可通过ipcs -m -u和df 命令获取。
2 Q: A0 t" F$ \/ m; j修正值2:cached字段漏掉了内核slab中可以自动回收的内存,比如xxx_inode_cache和dentry_cache。这两部分的内存的计算方法是解析/proc/slabinfo。
- Q# t* C' u$ M4 w! c最终的可用内存计算方法:2 `1 N" @8 i- n+ E
空闲内存+缓存-修正值1+修正值2
9 I S/ v- `1 K( D2 v$ Q
$ l0 U7 F( b3 S d' ^SUSE 11可用内存统计方法
8 z4 @8 ?" T' x }& ~总内存:free命令首行total字段。
" h3 o6 W' q7 X$ Y空闲内存:free命令首行free字段。; j9 `9 s3 Q- X
缓存:free命令首行buffers字段+cached字段。
# `' V; d+ u# [修正值1:cached字段包含了共享内存和tmpfs内存文件系统占用的内存。需要减去这两部分。这两部分内存之和可通过/proc/meminfo的Shmem字段直接获取。; z3 n5 R+ o; y5 j) @1 r
最终的可用内存计算方法:4 `% p" r4 ]0 f/ |9 ?
空闲内存+缓存-修正值1! o7 l) [7 m! |9 o9 ]# Y* i
+ u( R s$ s5 K附1:ipcs获取共享内存占用物理内存大小& l# k% Y5 x ^( S$ R/ L4 w: t8 B( d
# ipcs -m -u
- i/ u* ?6 t; l9 e* |8 A------ Shared Memory Status --------
- R$ U* r3 z* h( |segments allocated 42 [& F* `8 Q; K3 a8 C s7 A
pages allocated 786433* b/ @% @( D( U" k) O# ^& G+ Z
pages resident 2 #使用这个字段,单位是页,X86下一个页是4kB
" c. s+ ^; F! p! L* vpages swapped 0
& e: [" a& h! r/ w% y" rSwap peRFormance: 0 attempts 0 successes) K# n3 \0 F5 d6 u2 [' h! W
) n- ?) C5 g2 J! P" o; E+ J附2:df获取tmpfs占用物理内存大小
& q0 ~+ t7 r, Y7 o# df3 V, X% r7 J& d9 f9 |! r" c
Filesystem 1K-blocks Used Available Use% Mounted on
- v# J9 @+ L) l/dev/sda2 20972152 4427900 16544252 22% /9 ^# v& B/ w8 t' p6 S9 e5 e1 B
devtmpfs 24711780 160 24711620 1% /dev #Used字段表示实际占用物理内存: x5 y9 N/ A* X1 K! h1 ^7 c
tmpfs 24711780 0 24711780 0% /dev/shm #Used字段表示实际占用物理内存- E( l f5 Z D" r& T
/dev/sda5 1052184 59188 992996 6% /boot
; M" B; E6 `4 Y O, m: B5 K4 @/dev/sda9 83888824 16852500 67036324 21% /iso$ l% T e g. f( n9 z5 y) M. r
/dev/sda7 10490040 1142876 9347164 11% /opt
3 U- t# n; ^1 w i/dev/sda10 83888824 9421200 74467624 12% /src
. P6 ?0 n5 h' B, R' y2 I/dev/sda8 20972152 3475104 17497048 17% /usr
5 e: f' H3 t1 {! R1 c% x/dev/sda6 5245016 328392 4916624 7% /var
# w, Z5 y% \. F* x' [- H( [
& E) D: j3 H4 g5 j7 I
& k1 x) ?8 m' _9 {) {% S# z///////////////////增加统计工具////////2011.6.29///////////////////////////////////////////////////
3 s; ^( M3 z3 Z7 w统计工具(在SUSE10、SUSE11验证通过)
6 `* h8 A, D& F' M3 T0 R% Z1. 修正可用内存多计算的情况(SUSE10 SUSE11)
# v d2 o4 k3 }7 Y7 l1 W* S如下环境,在进行了大量共享内存创建使用后,free命令统计可用内存,出现了很大的误差。, ?5 ~. }% M! C- @6 T- \
#echo 3 >/proc/sys/vm/drop_caches #清理可回收的内存
) w+ I3 b: P, }- n# free" s$ F% a" X3 B+ u7 K! r8 r% p0 S( x& g
total used free shared buffers cached7 H; P3 {' d. j3 k2 V3 j$ L
Mem: 49423560 13609680 35813880 0 26764 11686608 #仍有11G不可以回收,因为是共享内存,不具备回收属性。+ l$ C1 J2 E* @. s
-/+ buffers/cache: 1896308 47527252 # free计算可用内存,算入了共享内存,得到47G) u& F' D: G9 |- h- V9 {
Swap: 2104472 0 21044720 U" f! M, E s/ T' E" G
# ipcs -m -u
w+ i5 f6 E3 B) l5 n------ Shared Memory Status --------
( K& K5 d; b ?! Vsegments allocated 20
, ^& S. B- G! ?7 L' a$ Cpages allocated 4980737) [! I \- q$ ?5 l' F
pages resident 2883591 #共享内存有2883591 * 4 = 11534364 kB ~= 11G
! L3 e" v$ T7 f: rpages swapped 0
, O9 r& z3 n& g1 `: PSwap performance: 0 attempts 0 successes$ k# T* H: F) C( v/ e! e
# afree) q G& t0 G; R, J
Total: 49423560 kB #物理内存总计
- o! Z, E) A1 P1 s2 B& q1 t9 VFree: 35816908 kB #未被使用的内存
5 I% G$ i8 l4 K: e; j YReclaimable: 179348 kB #被使用了但是可以自动回收的内存8 M, l: k" Y7 r3 r8 l
Available: 35996256 kB #afree统计出来的可用内存去除了11G的共享内存。1 L" `5 A) r/ i+ K" S
5 w/ |: K$ Y* l) i9 y
2. 修正可用内存少计算的情况(SUSE10) * ^! R+ V! ~! D! M# l9 b2 J' E
如下环境,在进行了大量文件访问操作后,系统中缓存了大概600M的inode和dentry。
) P% w% y* j3 V/ x# Q/ U# free
0 L- H3 V8 g1 K8 R+ B7 [2 {total used free shared buffers cached
5 m4 M; U- ?7 _3 ~* AMem: 3987316 1080908 2906408 0 163056 163848
6 k5 O$ h$ E" \2 x& ~+ k-/+ buffers/cache: 754004 3233312 #没有统计slab中的可以回收的inode和dentry。
3 X5 e( j' d$ W. x- l" USwap: 2104472 0 2104472" s% x8 t9 S, E2 V/ |) u" Q
# afree# ]9 X0 x0 x( ]; g# D; w# Q: T. D
Total: 3987316 kB
8 x9 z% A [7 _3 a. [$ Y% lFree: 2906384 kB; X4 J1 ]+ c0 `4 m
Reclaimable: 958888 kB
$ y; v, x0 k. E% B4 h7 J8 G! o6 N5 k y) yAvailable: 3865272 kB #afree统计了slab中的可以回收的inode和dentry。( e$ |7 G7 \8 x( Z. P
# echo 3 >/proc/sys/vm/drop_caches #释放600M的inode和dentry缓存。' e: n; J5 k( ~2 u
# free
% L6 Q* u l0 _3 P% s$ h4 t4 Ctotal used free shared buffers cached" I" o2 b/ _( ~; P! b) X& H8 B# d
Mem: 3987316 93628 3893688 0 17780 8948* U R9 U2 \2 y6 \2 F6 ~! M
-/+ buffers/cache: 66900 3920416 #执行drop_caches后,增加了600M,说明cached少统计了可回收的slab缓存。这600M即使不执行drop_caches也都是可用的,系统在需要的时候会自动回收。, ~& o6 @5 R5 Q! m
& e% d0 h" K& O) C; H+ h
注:afree通过解析slabinfo文件统计可回收的slab,其实suse10的内核有一个全局变量slab_reclaim_pages维护了准确的可回收数量,但没有暴露给给用户空间,因此更准确的方法其实就是写一个模块,把这个变量导出到来。
6 P* S. ?% _, a4 H- a* z
: Z, p$ j1 D! eafree代码如下:
8 D% h1 G1 F; R6 [4 f3 L5 M& k: }3 X+ G# C
#!/bin/sh
2 u3 h5 Q7 u/ f! d#
' F6 j7 G; Z* r( F#Get accurate available memory. * I4 X0 x. X* {
#& F/ D/ X+ a6 Z! r% n( f6 c
& T% C+ k6 @. r1 J, g
function get_meminfo()
; f3 j' j/ ]( o A4 l! K$ J{
; f2 a% Z2 @$ |2 ?! \ grep -w $1 /proc/meminfo | awk -F' ' '{print $2}'
, E# d5 x* i6 \: | D$ R5 ^/ l}& p( ]7 o2 M* v( d" G( T) z
function show_meminfo()
' w. T; R) C% x5 w4 r+ d{
& W' _: q: V/ M* b printf "%s\t%10d kB\n" "$1" "$2"8 `* F6 e$ O2 {6 {, {5 Y* `
}) w( q! Y4 y3 s# v
! q U. L3 [7 @8 A1 P& Z
PAGE_SIZE=4 #kB, for x86 . A! {* ^5 G# t6 _0 j( q, a' A' D
function get_shmem_from_ipcs()8 j8 N8 {; O/ @
{
1 ~* r I/ ~, D2 I3 g9 ^ local _shm=08 V3 S) q5 p) g( t9 ~1 v( U
_shm=$(ipcs -m -u | grep 'pages resident' | awk -F' ' '{print $3}')
5 L( U- G* h- i# E! ^+ C echo $((_shm * PAGE_SIZE))
( A; b# i# g( q2 I, {7 X}
! U2 {# G" n. e4 ?* X5 z, J( {% Nfunction get_tmpfs_from_df()" ]" d3 Q0 n& \$ `5 t
{
1 L/ Q7 i ]! I, D( s local _size=""( a% @1 Y f$ @( w7 a1 n+ W
_size=$(df -k | awk -F' ' 'BEGIN{total=0} {if ($1 == "tmpfs" || $1 == "devtmpfs" || $1 == "shm")total+=$3}END{print total}'): n3 E L |; U% J$ c/ K. v" u+ F8 ?
echo $_size ! i7 f& r2 a& T- t6 K* z
}' {) c7 j" |; N' v; n |' T
6 l# _* `; @, k. P4 X* b* O# X) q#inode, dentry and buffer_head is reclaimable
1 Q/ g% y! A6 Y! \/ S9 S" Ofunction get_slab_reclaimable_from_slabinfo()
3 u7 l, J% p: X{0 e6 l: T' @& u
local _size=""
/ r# a0 p. V8 B _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)
$ Z, D6 f! T# y9 z/ i' Z; ^2 ~ [ -z "$_size" ] && _size=0: Y* g7 r6 V( H
echo $_size + y. c( G6 B& d- K2 B( y
}( c; \$ ~( \6 B2 d! @1 Q! I, g5 u
) W4 P" K/ o9 k n1 D+ g. Efree=$(get_meminfo MemFree)/ P( A. z8 ~+ s9 t1 A3 M, L
total=$(get_meminfo MemTotal) : H$ F; W0 ]! _/ r2 O+ l% V. p: z
cached=$(get_meminfo Cached)
8 U" B! \& w/ l8 i1 M# ebuffer=$(get_meminfo Buffers)
8 Z0 v" a* `$ j$ h, tswapcached=$(get_meminfo SwapCached)' w- x" j: k1 e% _5 C, x
shmem=$(get_meminfo Shmem): ^- M0 j" D9 Q& _
slab_reclaimable=$(get_meminfo SReclaimable). W( }% _9 c X5 }0 @8 I- s: |& o' r
nfs_unstable=$(get_meminfo NFS_Unstable)! {- j* }/ a0 W4 y% n, ?
7 z4 f/ a' J- [* g J: l#the kernel does not support, no 'Shmem' field in /proc/meminfo, we use ipc and df.5 e# t: ^( y, K2 v
if [ -z "$shmem" ] ; then. S0 k* Y" O* o
shmem=$(( $(get_shmem_from_ipcs) + $(get_tmpfs_from_df) ))0 h6 `3 N2 w" T/ q4 H' H
fi
% \; l1 ^; A& N( d @9 \/ U: W+ H0 U5 n8 q0 R5 o- ^% e
#the kernel does not support, no 'SReclaimable' field in /proc/meminfo, we use /proc/slabinfo.3 h" n0 J, o# Q( v7 j8 E
if [ -z "$slab_reclaimable" ]; then( G/ b X# B- [/ T" W
slab_reclaimable=$(get_slab_reclaimable_from_slabinfo)# P1 J |! W4 N/ V$ u% I e
fi
9 F! h8 }4 y$ W" K4 _& L
8 T- e0 v% U o) q C#the kernel does not support, no 'NFS_Unstable' field in /proc/meminfo, we use null. :)
' u4 j' K2 f! Q: I2 Gif [ -z "$nfs_unstable" ]; then
1 O0 N* ?- C" e" h4 s, u nfs_unstable=0
4 Q2 n- L/ h! R0 v: j7 t. Wfi- k$ ^" G2 Z- e/ K3 C& f" O
( ]% {. F9 n' C: ]. L6 m
4 o( G/ k3 E5 Dreclaimable=$((cached + buffer + slab_reclaimable + swapcached + nfs_unstable - shmem))
& Q+ y6 [ s7 O) O) [: t/ Zavailable=$((free + reclaimable))
' A6 \* G% R6 c' O* B; {show_meminfo "Total: "
5 }9 B9 ~- v6 Y2 B# o
+ f2 u3 c3 ^; P7 x9 O1 A, g- N说说free命令
% I' R [ E) W# H6 i3 r A" O% n Y; S( F7 l4 G0 O4 K: l
free是个常用命令,几乎每个接触、使用linux的用户都会用到它。但往往对它的统计输出会有一些困惑,这一方面和Linux内存管理机制有关,另一方面Linux在内存统计上也确实有些不足和问题。* G0 z' F. _$ ~, a4 l+ ^6 p
关键在于两个字段,buffers和cached。- {2 t! P4 T" |4 @5 o
$ Y( @/ q: z* s' \% A' b. ^6 N
: Z3 `2 C: v J: j' S% U. ?, c0 v( ?: ]7 j; D5 y
你经常会发现Linux系统用了一段时间后,内存所剩无几,free命令,一看,内存全跑到 buffers和cached里面了;这个现象是正常的。访问过的磁盘文件的元数据及内容,内核都会缓存起来。这些缓存就是磁盘缓存。, m" X+ |; X1 U6 |6 s" v( M% }. u' ?
+ {$ i4 l* z0 X& e6 N0 G8 }/ Z * s8 `4 \+ L# F8 k7 I' [
. \7 }7 U1 z$ N" j0 ALinux磁盘缓存设计特点(设计理念):7 C, M8 z3 I5 b
除了系统运行必须的一小部分保留外,只要有剩余内存,只要需要,就会用给磁盘缓存。(没有一个参数可以让你限定缓存的上限。2.6内核之前有一个限定参数,后来给取消了)3 V* b ?+ R' v9 f, z6 a- x
1 {' Y/ o+ F' V: V0 B7 F. h8 _4 c5 w所以会经常看到内存所剩无几的现象,这是缓存机制导致的,对应用是透明,在有内存需要时,这些内存会释放。这个过程对应用是透明的,应用可以认为系统的可用内存包括buffers和cached。. n* j6 V3 C3 Z6 h; f# X. E
- d- n: R6 q: i. C. ^
- K" `) m1 q' m* \7 `这种设计,在大多数服务器应用场景下都有比较好的性能表现。可以说是比较可取的。5 v, ]/ ~1 S6 N2 A5 j6 @
2 e9 @& q" i x设计本身没有问题,但free命令显示的buffers和cached并不能和磁盘缓存完全对应,这是实现细节上的不足和问题。
9 R9 V- z7 }1 o4 Y/ k2 g5 P9 B9 O! V; M
) F; U7 j4 b+ Z9 o; _; ^1. buffers和cached包含了不属于磁盘缓存的内容。2 h! @4 M5 a ^$ l' Q
由于buffers和cached实际上就是内核为所有文件映射分配的物理页的总和(page cache)。
9 K. J7 W9 h5 F9 K0 {6 ~/ i但内核中的“文件概念”是广泛的,不仅包含了真正位于磁盘上的文件,还包含了为特殊需要创建的虚拟文件,比如:1 Y6 T( u5 r) m8 ~9 v% X
( Z7 K4 G5 C7 x2 z: R/ g进程间的共享内存(通过shmget API创建的内存),内核建立一个虚拟的文件和共享内存关联起来。(通过pmap命令你可以看到进程拥有的共享内存地址空间的映射字段是/SYSVXXXX字样,不是匿名的)。
% a6 m, w' `5 C7 _; l
" V- j$ G, A4 m# w$ t, T" c1 W
0 I( G- a0 P9 D, ~3 L- n非常的不幸,这些虚拟文件映射关联的page,也被算入了free命令显示的cached字段。但这部分内存没有缓存属性,在内存不足时不能按缓存的方式来回收。(使用echo 3 >/proc/sys/vm/drop_caches,无法释放掉这部分内存)
; h$ a7 `* X: V9 V8 t
W5 {' V% \+ }5 n$ I% d这个问题,会带来一些麻烦。比如,按照常规理解,某产品设计内存占用过高告警的条件是,空闲内存+buffers+cached小于内存总和的20% 。一般情况下没有问题,但如果产品使用了大量的共享内存,告警将失去作用。
* L" t, |- o+ j/ x/ u9 ^4 K4 l9 `0 @! P% s* b0 z5 S1 n. c! S" _$ Z
8 e- _! A+ s# a2 @9 h. \* i* ^2. buffers和cached遗漏了部分属于磁盘缓存的内容。9 ]' l+ [* ~& j- q
还 是由于buffers和cached只是内核为所有文件映射分配的物理页的总和。在文件系统方面,还有一部分缓存是不和文件映射相关联的,比如内核分配的 inode对象,在文件关闭时,并不会立即释放,具备缓存的属性。这部分不在基于文件映射的页面里,而是通过slab(内核内存池)分配的。
7 {" L& T) y: j' ?! b0 o) s, Q$ c& h8 M
|
|