查找/var/log/messages,發現有錯誤信息:XFS: possible memory allocation deadlock in kmem_alloc (mode:0x250)html
查找linux內核,搜索"kernel: XFS: possible memory allocation deadlock in kmem_alloc (mode:0x250)"linux
發現源代碼以下:算法
void
*
kmem_alloc(
size_t
size, xfs_km_flags_t flags)
{
int
retries = 0;
gfp_t lflags = kmem_flags_convert(flags);
void
*ptr;
do
{
ptr = kmalloc(size, lflags);
if
(ptr || (flags & (KM_MAYFAIL|KM_NOSLEEP)))
return
ptr;
if
(!(++retries % 100))
xfs_err(NULL,
"possible memory allocation deadlock in %s (mode:0x%x)"
,
__func__, lflags);
congestion_wait(BLK_RW_ASYNC, HZ/50);
}
while
(1);
}
|
分析代碼:docker
- 當嘗試申請到內存時,直接返回;
- 申請不到內存會等待HZ/50 的jiffies後,繼續嘗試,直到申請成功纔會退出;
- 每嘗試申請失敗100次後,會打印possible memory allocation deadlock in %s (mode:0x%x)
- 說明申請不到內存時,會卡在這裏一直循環下去
mode的含義是所申請的內存標記爲, GFP_IO|GFP_WAIT|GFP_NOWARN,以下ssh
結合日誌來看,有很長一段時間都在打印「XFS: possible memory allocation deadlock in kmem_alloc (mode:0x250)」url
以前只是偶爾打印「XFS: possible memory allocation deadlock in kmem_alloc (mode:0x250)」spa
用到的文件系統是XFS,查找到一段資料,裏面描述:.net
咱們能夠看到這裏比較大塊的連續的page 是基本沒有的. 由於在xfs 的申請內存操做裏面咱們看到有這種連續的大塊的內存申請的操做的請求, 好比:debug
6000: map = kmem_alloc(subnex * sizeof(*map), KM_MAYFAIL | KM_NOFS);
所以比較大的多是線上雖然有少許的空閒內存, 可是這些內存很是的碎片, 所以只要有一個稍微大的的連續內存的請求都沒法知足。日誌
因爲已經作了echo 1 > /proc/sys/vm/drop_caches 操做,查看下另外一臺機器的狀況:
/proc/buddyinfo是linuxbuddy系統管理物理內存的debug信息。
在linux中使用buddy算法解決物理內存的外碎片問題,其把全部空閒的內存,以2的冪次方的形式,分紅11個塊鏈表,分別對應爲一、二、四、八、1六、3二、6四、12八、25六、5十二、1024個頁塊。
而Linux支持NUMA技術,對於NUMA設備,NUMA系統的結點一般是由一組CPU和本地內存組成,每個節點都有相應的本地內存,所以buddyinfo 中的Node0表示節點ID;而每個節點下的內存設備,又能夠劃分爲多個內存區域(zone),所以下面的顯示中,對於Node0的內存,又劃分類DMA、Normal、HighMem區域。然後面則是表示空閒的區域。
此處以Normal區域進行分析,第二列值爲100,表示當前系統中normal區域,可用的連續兩頁的內存大小爲100*2*PAGE_SIZE;第三列值爲52,表示當前系統中normal區域,可用的連續四頁的內存大小爲52*2^2*PAGE_SIZE
cat /proc/buddyinfo
Node 0, zone DMA 23 15 4 5 2 3 3 2 3 1 0
Node 0, zone Normal 149 100 52 33 23 5 32 8 12 2 59
Node 0, zone HighMem 11 21 23 49 29 15 8 16 12 2 142
能夠看到從第5列開始,只剩下44*16*PAGE_SIZE的頁塊,後面剩下的分別是1 * 32 *PAGE_SIZE, 1 * 64 *PAGE_SIZE, 2 *128 * PAGE_SIZE,剩下256,512的頁塊都沒有了
所以推測,致使這個問題出現的時候,該機器的內存碎片不少,當某個應用執行時,在xfs 的申請內存中有這種連續的大塊的內存申請的操做的請求, 好比:
6000: map = kmem_alloc(subnex * sizeof(*map), KM_MAYFAIL | KM_NOFS);
就會致使內存一直分配不到。例如執行docker ps,docker exec這些命令時,會一直阻塞在kmem_alloc的循環中,反覆申請內存,因爲內存碎片沒有被組合,所以就一直申請不到,執行這些命令也會卡住,這也就驗證了執行某些命令如ls,ssh都不會失敗(由於須要內存的PAGE不是那麼大)。
而執行echo 1 > /proc/sys/vm/drop_caches 操做會把碎片化的PAGE從新分配,以後在申請大塊的PAGE內存就能夠申請到,不阻塞了。
然而不能老是輸入這個命令解決問題,因而找到了下面的方法:
參考
http://www.cnblogs.com/itfriend/archive/2011/12/14/2287160.html
Linux 提供了這樣一個參數min_free_kbytes,用來肯定系統開始回收內存的閥值,控制系統的空閒內存。值越高,內核越早開始回收內存,空閒內存越高。
設置/proc/sys/vm/min_free_kbytes的值爲4G bytes
echo 4194304 > /proc/sys/vm/min_free_kbytes
目前爲90M bytes:
執行後的效果:
查看內存碎片狀況,發現有明顯改善。
按此方法修改後,再沒有出現docker ps命令掛住的現象。