天天看點

轉一篇Linux可用記憶體的統計方法

背景

由于Linux緩存機制的設計,系統對緩存的使用是非常狠的,是以經常會看到某些環境記憶體隻剩幾十兆了,而應用隻用了不到一半。是以在計算可用記憶體的時候,一定要算上緩存的部分。

通常方法,就是通過free指令首行free+cached+buffers計算,或者直接使用第二行的free字段。但這個方法有時仍然會造成比較大的誤差,導緻性能監控等方面的問題。

SUSE11 SP1基于2.6.32核心,核心暴露了更多的統計接口給使用者空間,把slab分為可回收和不可回收兩類名額來統計。free指令也對應做了修改。解決了free指令少計算可用記憶體的問題。但多計算的問題還是存在。

是以,在這裡對統計可用記憶體的方法做了個總結。供需要的同學參考。(後面有空可能會開發一個統計工具)

其中SUSE10由于核心版本過低(2.6.16)暴露資訊不足,下面的方法仍然不能很精确,但相比通過free指令簡單統計而言,一般不會造成比較大的誤差。

可用記憶體定義

包括未被使用的空閑記憶體,以及已經被使用但用作緩存可以自動回收的部分。

SUSE 10可用記憶體統計方法

總記憶體:free指令首行total字段。

空閑記憶體:free指令首行free字段。

緩存:free指令首行buffers字段+cached字段。

修正值1:cached字段包含了共享記憶體和tmpfs記憶體檔案系統占用的記憶體。需要減去這兩部分。這兩部分記憶體可通過ipcs -m -u和df 指令擷取。 

修正值2:cached字段漏掉了核心slab中可以自動回收的記憶體,比如xxx_inode_cache和dentry_cache。這兩部分的記憶體的計算方法是解析/proc/slabinfo。

最終的可用記憶體計算方法:

空閑記憶體+緩存-修正值1+修正值2

SUSE 11可用記憶體統計方法

修正值1:cached字段包含了共享記憶體和tmpfs記憶體檔案系統占用的記憶體。需要減去這兩部分。這兩部分記憶體之和可通過/proc/meminfo的Shmem字段直接擷取。

空閑記憶體+緩存-修正值1

附1:ipcs擷取共享記憶體占用實體記憶體大小

# ipcs -m -u

------ Shared Memory Status --------

segments allocated 4

pages allocated 786433

pages resident 2 #使用這個字段,機關是頁,X86下一個頁是4kB

pages swapped 0

Swap performance: 0 attempts 0 successes

附2:df擷取tmpfs占用實體記憶體大小

# df

Filesystem 1K-blocks Used Available Use% Mounted on

/dev/sda2 20972152 4427900 16544252 22% /

devtmpfs 24711780 160 24711620 1% /dev #Used字段表示實際占用實體記憶體

tmpfs 24711780 0 24711780 0% /dev/shm #Used字段表示實際占用實體記憶體

/dev/sda5 1052184 59188 992996 6% /boot

/dev/sda9 83888824 16852500 67036324 21% /iso

/dev/sda7 10490040 1142876 9347164 11% /opt

/dev/sda10 83888824 9421200 74467624 12% /src

/dev/sda8 20972152 3475104 17497048 17% /usr

/dev/sda6 5245016 328392 4916624 7% /var

///////////////////增加統計工具////////2011.6.29///////////////////////////////////////////////////

統計工具(在SUSE10、SUSE11驗證通過)

1. 修正可用記憶體多計算的情況(SUSE10 SUSE11)

如下環境,在進行了大量共享記憶體建立使用後,free指令統計可用記憶體,出現了很大的誤差。

#echo 3 >/proc/sys/vm/drop_caches #清理可回收的記憶體

# free

total used free shared buffers cached

Mem: 49423560 13609680 35813880 0 26764 11686608 #仍有11G不可以回收,因為是共享記憶體,不具備回收屬性。

-/+ buffers/cache: 1896308 47527252 # free計算可用記憶體,算入了共享記憶體,得到47G

Swap: 2104472 0 2104472

segments allocated 20

pages allocated 4980737

pages resident 2883591 #共享記憶體有2883591 * 4 = 11534364 kB ~= 11G 

# afree

Total: 49423560 kB #實體記憶體總計

Free: 35816908 kB #未被使用的記憶體

Reclaimable: 179348 kB #被使用了但是可以自動回收的記憶體

Available: 35996256 kB #afree統計出來的可用記憶體去除了11G的共享記憶體。

2. 修正可用記憶體少計算的情況(SUSE10) 

如下環境,在進行了大量檔案通路操作後,系統中緩存了大概600M的inode和dentry。

Mem: 3987316 1080908 2906408 0 163056 163848 

-/+ buffers/cache: 754004 3233312 #沒有統計slab中的可以回收的inode和dentry。

Total: 3987316 kB

Free: 2906384 kB

Reclaimable: 958888 kB

Available: 3865272 kB #afree統計了slab中的可以回收的inode和dentry。

# echo 3 >/proc/sys/vm/drop_caches #釋放600M的inode和dentry緩存。

Mem: 3987316 93628 3893688 0 17780 8948

-/+ buffers/cache: 66900 3920416 #執行drop_caches後,增加了600M,說明cached少統計了可回收的slab緩存。這600M即使不執行drop_caches也都是可用的,系統在需要的時候會自動回收。

注:afree通過解析slabinfo檔案統計可回收的slab,其實suse10的核心有一個全局變量slab_reclaim_pages維護了準确的可回收數量,但沒有暴露給給使用者空間,是以更準确的方法其實就是寫一個子產品,把這個變量導出到來。

afree代碼如下:

#!/bin/sh

#

#Get accurate available memory. 

function get_meminfo()

{

   grep -w $1 /proc/meminfo | awk -F' ' '{print $2}'

}

function show_meminfo()

   printf "%s\t%10d kB\n" "$1" "$2"

PAGE_SIZE=4 #kB, for x86 

function get_shmem_from_ipcs()

   local _shm=0

   _shm=$(ipcs -m -u | grep 'pages resident' | awk -F' ' '{print $3}')

   echo $((_shm * PAGE_SIZE))

function get_tmpfs_from_df()

   local _size=""

   _size=$(df -k | awk -F' ' 'BEGIN{total=0} {if ($1 == "tmpfs" || $1 == "devtmpfs" || $1 == "shm")total+=$3}END{print total}')

   echo $_size 

#inode, dentry and buffer_head is reclaimable 

function get_slab_reclaimable_from_slabinfo()

   _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 "$_size" ] && _size=0

free=$(get_meminfo MemFree)

total=$(get_meminfo MemTotal) 

cached=$(get_meminfo Cached)

buffer=$(get_meminfo Buffers)

swapcached=$(get_meminfo SwapCached)

shmem=$(get_meminfo Shmem)

slab_reclaimable=$(get_meminfo SReclaimable)

nfs_unstable=$(get_meminfo NFS_Unstable)

#the kernel does not support, no 'Shmem' field in /proc/meminfo, we use ipc and df.

if [ -z "$shmem" ] ; then

   shmem=$(( $(get_shmem_from_ipcs) + $(get_tmpfs_from_df) ))

fi

#the kernel does not support, no 'SReclaimable' field in /proc/meminfo, we use /proc/slabinfo.

if [ -z "$slab_reclaimable" ]; then

   slab_reclaimable=$(get_slab_reclaimable_from_slabinfo)

#the kernel does not support, no 'NFS_Unstable' field in /proc/meminfo, we use null. :)

if [ -z "$nfs_unstable" ]; then 

   nfs_unstable=0

reclaimable=$((cached + buffer + slab_reclaimable + swapcached + nfs_unstable - shmem))

available=$((free + reclaimable))

show_meminfo "Total: "        

說說free指令

關鍵在于兩個字段,buffers和cached。

你經常會發現Linux系統用了一段時間後,記憶體所剩無幾,free指令,一看,記憶體全跑到 buffers和cached裡面了;這個現象是正常的。通路過的磁盤檔案的中繼資料及内容,核心都會緩存起來。這些緩存就是磁盤緩存。

Linux磁盤緩存設計特點(設計理念):

除了系統運作必須的一小部分保留外,隻要有剩餘記憶體,隻要需要,就會用給磁盤緩存。(沒有一個參數可以讓你限定緩存的上限。2.6核心之前有一個限定參數,後來給取消了)

是以會經常看到記憶體所剩無幾的現象,這是緩存機制導緻的,對應用是透明,在有記憶體需要時,這些記憶體會釋放。這個過程對應用是透明的,應用可以認為系統的可用記憶體包括buffers和cached。

這種設計,在大多數伺服器應用場景下都有比較好的性能表現。可以說是比較可取的。

設計本身沒有問題,但free指令顯示的buffers和cached并不能和磁盤緩存完全對應,這是實作細節上的不足和問題。

1. buffers和cached包含了不屬于磁盤緩存的内容。

由于buffers和cached實際上就是核心為所有檔案映射配置設定的實體頁的總和(page cache)。

但核心中的“檔案概念”是廣泛的,不僅包含了真正位于磁盤上的檔案,還包含了為特殊需要建立的虛拟檔案,比如:

程序間的共享記憶體(通過shmget API建立的記憶體),核心建立一個虛拟的檔案和共享記憶體關聯起來。(通過pmap指令你可以看到程序擁有的共享記憶體位址空間的映射字段是/SYSVXXXX字樣,不是匿名的)。

非常的不幸,這些虛拟檔案映射關聯的page,也被算入了free指令顯示的cached字段。但這部分記憶體沒有緩存屬性,在記憶體不足時不能按緩存的方式來回收。(使用echo 3 >/proc/sys/vm/drop_caches,無法釋放掉這部分記憶體)

這個問題,會帶來一些麻煩。比如,按照正常了解,某産品設計記憶體占用過高告警的條件是,空閑記憶體+buffers+cached小于記憶體總和的20% 。一般情況下沒有問題,但如果産品使用了大量的共享記憶體,告警将失去作用。

2. buffers和cached遺漏了部分屬于磁盤緩存的内容。

還 是由于buffers和cached隻是核心為所有檔案映射配置設定的實體頁的總和。在檔案系統方面,還有一部分緩存是不和檔案映射相關聯的,比如核心配置設定的 inode對象,在檔案關閉時,并不會立即釋放,具備緩存的屬性。這部分不在基于檔案映射的頁面裡,而是通過slab(核心記憶體池)配置設定的。

本文轉自feisky部落格園部落格,原文連結:http://www.cnblogs.com/feisky/archive/2012/04/14/2447503.html,如需轉載請自行聯系原作者

繼續閱讀