前段時間寫的一個轉發模塊在現網應用後
幾臺設備出現了不同程度的內存泄露
大約4-15天設備內存耗盡
泄露速度因業務壓力和網絡丟包情況而不同
經歷了N次的代碼review和一個不眠之夜後
終於找到了原因
在一處釋放skb的地方
本應該使用kfree_skb()的,鬼使神差的被我敲成了kfree()
教訓很深刻
遂仔細查看了相關函數
以SLUB分配方式爲例
alloc_skb()位於include/linux/skbuff.h中
是對__alloc_skb()的內聯封裝函數,快速克隆標誌爲0
在__alloc_skb()中根據fclone標誌從緩存skbuff_fclone_cache或skbuff_head_cache中分配struct sk_buff控制結構
隨後根據size分配按緩存線對齊的線性區長度空間
skbuff_fclone_cache和skbuff_head_cache由skb_init()調用kmem_cache_create()初始化
kfree_skb()位於net/core/skbuff.c中
進行必要的判斷後根據情況調用__kfree_skb()
在__kfree_skb()中會調用skb_release_all()和kfree_skbmem()函數
skb_release_all()中執行路由、連接跟蹤、析構函數、網橋控制等清理工作
並釋放skb下的頁數據、分片skb、線性區buffer
在kfree_skbmem()中使用kmem_cache_free()回收控制結構skb本身
而在kmem_cache_free()調用slab_free()函數
kfree()位於mm/slub.c中
參數爲void *類型
gcc中void *和其他類型指針字段隱式轉換,沒有編譯警告
然後調用了slab_free()
如此一看,內存泄露無疑
因爲kfree()並不會釋放skb的線性區及分片等
各函數的詳細分析在 git://github.com/kernel-digger/linux.git 的comments分支
歡迎clone查看交流