前段時間在作播放器的時候,遇到個問題,花了很長時間,作個記錄,但願對有須要的人有所幫助:算法
播放器的播視頻的時候,不管是手動切換視頻仍是到視頻播放完成,自動切換視頻,必定次數後均出現黑屏現象,偶爾有聲音,問題出現後,不可恢復,Kernel輸出以下Log:緩存
DMA free:71672kB min:616kB low:768kB high:924kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB isolateds性能
lowmem_reserve[]: 0 1244 1593 1593測試
Normal free:1249804kB min:4212kB low:5264kB high:6316kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB oorm
lowmem_reserve[]: 0 0 2794 2794視頻
HighMem free:223000kB min:348kB low:640kB high:936kB active_anon:39248kB inactive_anon:140kB active_file:61020kB inactive_file:34108kB unevio進程
lowmem_reserve[]: 0 0 0 0內存
DMA: 22*4kB 90*8kB 21*16kB 82*32kB 7*64kB 137*128kB 69*256kB 3*512kB 8*1024kB 5*2048kB 3*4096kB 0*8192kB 0*16384kB 0*32768kB = 71672kB開發
Normal: 1*4kB 53*8kB 16*16kB 6*32kB 6*64kB 4*128kB 1*256kB 1*512kB 2*1024kB 2*2048kB 3*4096kB 2*8192kB 4*16384kB 35*32768kB = 1249772kB產品
HighMem: 14*4kB 10*8kB 437*16kB 386*32kB 374*64kB 27*128kB 10*256kB 7*512kB 8*1024kB 7*2048kB 4*4096kB 2*8192kB 1*16384kB 3*32768kB = 223000B
23829 total pagecache pages
0 pages in swap cache
Swap cache stats: add 0, delete 0, find 0/0
Free swap = 0kB
Total swap = 0kB
524288 pages of RAM
387015 free pages
73287 reserved pages
1169 slab pages
8545 pages shared
0 pages swap cached
Physical memory allocation error!
Physical memory allocation error!
經過Log,定位到輸出的位置是解碼驅動中,驅動在DMA請求失敗後,輸出以上信息。針對這個現象,按如下幾個思路進行的調查:
1. 內存泄露
在驅動中增長了一些Log,隨着視頻切換,分配的地址會必定程度的變大,直到最後分配失敗。但仔細研究發現分配與釋放是配對的,驅動中記錄的內存使用信息徹底正確,沒有任何跡象代表有內存泄露發生。
2. 內存碎片
官論上有一樣的問題登陸,但無明確結論,官方應該是沒重現。民間有人指出問題在於驅動請求的連續內存塊比較大,但系統已無相應的內存可供分配,即內存碎片了。同時有人給出倆個方案:緩存每次的請求的內存,分配後,再也不返還給系統,這個方案我測試後,發現會致使其它視頻驅動出現一樣問題,不可用;另外一個方案是認爲致使內存碎片的緣由在於,文件系統的Cache機制,要解決這個問題,須要清除被Cache佔用的內存,方法以下:
echo 3 > /proc/sys/vm/drop_caches
試驗以後,果真有效,測試了手中的測試用例幾千次後,發了測試軟件,結果悲劇,開發部的測試用例幾回就測出一樣的問題,該方案水土不服。
3. 調查Linux內存使用
從內存碎片這個思路出發,分析什麼致使了驅動的DMA空間碎片化。查了一些資料發現,Linux的內存劃分分紅3塊:DMA,Low Memory和High Memory。通常32位PC的配置是DMA Zone位於0x00010000-0x00ffffff,大約16MB,Low Memory是0x01000000-0x37bfdfff,約800多MB,再往上的空間給High Memory,這部分的訪問要經過跳轉,性能不如DMA Zone和Low Memory Zone快。而內存分配的策略是從上往下分配,一旦高位的內存空間不足,則向低地址的空間請求內存。在咱們的產品上,總的內存大小是512MB,某些驅動預留128MB,DMA Zone的配置是184MB,那麼實際剩下的Low Memory Zone只有200MB,而這200MB要運行UI,媒體管理,互聯等等一系列進程。一旦Low Memory Zone的內存不夠用,系統會向DMA Zone請求內存,從而致使的DMA空間的內存碎片化!而這時,經過free命令是看不到正確的內存信息的,由於看到的空間還有,但一是被Cache佔用,二是計算的是總數,而不是真正的連續內存,這時查看內存應該使用系統提供的Buddy Info來看真正剩餘的內存塊:
$cat /proc/buddyinfo
Node 0, zone DMA 3 1 3 4 1 2 2 0 1 2 0 3 2 2
Node 0, zone Normal 74 36 29 8 6 4 3 2 1 2 3 2 0 2
輸出的數據從左側開始是4K內存塊的個數,依次乘2到最右側的32M;在出問題時,系統基本上只有小塊的內存,而問題的緣由就是一些文件訪問的操做致使DMA Zone的空間被Cache使用了。
4. 控制內存分配算法
找到問題的緣由後,調查如何能禁止系統從DMA Zone分配內存。查找到能夠設置lowmem_reserve_ratio,來告訴系統,DMA Zone的預留空間。其參數有3個(系統默認:256,32,32),與DMA Zone有關的是第一個,其值是除數,用DMA Zone的大小除以256,獲得的基本就是預留的大小了。作了幾個測試後,發現只有設置爲1纔能有效保護DMA Zone不被佔用,:(!設置爲1後,視頻再也不出現黑屏,但其餘問題很快出現,由於系統可用的內存減小,內存的回收算法被頻繁執行,性能急劇降低。
5. 最終方案
因爲產品的總內存過小,而預留的DMA Zone太大,因此須要調整DMA Zone預留的空間,但lowmem_reserve_ratio已無調整空間,因此考慮從調整Kernel上下手。最後,將Kernel中的DMA Zone總大小由184MB調整爲原來的3/4,即138MB,獲得一個近似合理的配置,保證的DMA和Low Memory均可以申請到足夠的空間,長時間測試後,未再現相關問題。