查找/var/log/messages,發現有錯誤信息:XFS: possible memory allocation deadlock in kmem_alloc (mode:0x250)
查找linux內核,搜索"kernel: XFS: possible memory allocation deadlock in kmem_alloc (mode:0x250)"
發現源代碼如下:
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);
}
|
分析代碼:
- 當嘗試申請到內存時,直接返回;
- 申請不到內存會等待HZ/50 的jiffies后,繼續嘗試,直到申請成功才會退出;
- 每嘗試申請失敗100次后,會打印possible memory allocation deadlock in %s (mode:0x%x)
- 說明申請不到內存時,會卡在這里一直循環下去
mode的含義是所申請的內存標記為, GFP_IO|GFP_WAIT|GFP_NOWARN,如下
結合日志來看,有很長一段時間都在打印“XFS: possible memory allocation deadlock in kmem_alloc (mode:0x250)”
之前只是偶爾打印“XFS: possible memory allocation deadlock in kmem_alloc (mode:0x250)”
用到的文件系統是XFS,查找到一段資料,里面描述:
我們可以看到這里比較大塊的連續的page 是基本沒有的. 因為在xfs 的申請內存操作里面我們看到有這種連續的大塊的內存申請的操作的請求, 比如:
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、2、4、8、16、32、64、128、256、512、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:
查看內存碎片情況,發現有明顯改善。