前傳
嵌入式系統(tǒng)的內(nèi)存回收還是比較重要的,因?yàn)檫@塊涉及到程序運(yùn)行性能。
嵌入式系統(tǒng)(比如平板,手機(jī))會(huì)更加關(guān)注單機(jī)性能優(yōu)化,因而會(huì)更加重視系統(tǒng)內(nèi)存回收。
嵌入式系統(tǒng)不像互聯(lián)網(wǎng)那種大型分布式服務(wù)器系統(tǒng),他們往往內(nèi)存和存儲(chǔ)容量比較充裕,因而關(guān)注點(diǎn)在分布式方面,對(duì)單機(jī)性能不夠重視。嵌入式系統(tǒng),在有限的內(nèi)存和存儲(chǔ)空間因素制約下,會(huì)更加關(guān)注單機(jī)性能優(yōu)化。
而內(nèi)存回收這塊是比較重要的,因?yàn)閮?nèi)存回收做的不好,內(nèi)存壓力得不到釋放,最直接的是內(nèi)存壓力會(huì)轉(zhuǎn)化為IO壓力,對(duì)系統(tǒng)io性能造成影響。另外也會(huì)轉(zhuǎn)換為cpu壓力,影響程序的cpu資源使用。
所以結(jié)合我對(duì)內(nèi)存回收方面的調(diào)研,想重點(diǎn)寫(xiě)下對(duì)Linux內(nèi)核內(nèi)存回收這塊代碼的理解,也想分享下我在這塊的調(diào)研心得。
內(nèi)存回收的重要性
這個(gè)內(nèi)存回收方面的優(yōu)化對(duì)系統(tǒng)到底有怎樣的影響,我想舉幾個(gè)例子會(huì)詳細(xì)透徹地講下內(nèi)存回收方面的優(yōu)化,還有給系統(tǒng)帶來(lái)的好處。
一 、內(nèi)存回收在高負(fù)載系統(tǒng)性能方面的優(yōu)化
1 問(wèn)題現(xiàn)象:
移動(dòng)設(shè)備上(比如平板,手機(jī))后臺(tái)u盤(pán)傳輸大批量數(shù)據(jù)時(shí),前臺(tái)桌面操作卡頓。
2 原因分析:
1: u盤(pán)拷貝數(shù)據(jù)量大時(shí),會(huì)導(dǎo)致系統(tǒng)內(nèi)存里面臟頁(yè)變多,然后可用內(nèi)存變少。
2: 然后前臺(tái)平板操作時(shí),會(huì)allocate_pages去分配內(nèi)存頁(yè)。這個(gè)時(shí)候會(huì)陷入slow path,去觸發(fā)內(nèi)存回收。
由于此時(shí)有大量臟頁(yè)存在,內(nèi)存回收會(huì)比較耗時(shí),這樣前臺(tái)操作的內(nèi)存分配性能也會(huì)變差。同時(shí)回收臟頁(yè)時(shí)產(chǎn)生的io壓力對(duì)前臺(tái)操作時(shí)的io性能也會(huì)有影響。
因?yàn)榍芭_(tái)的內(nèi)存和io性能都受到了影響,所以前臺(tái)平板操作才會(huì)變卡頓。
3 一點(diǎn)性能優(yōu)化心得方面的精彩點(diǎn)評(píng):
這是一個(gè)典型的單機(jī)系統(tǒng)里面比較特色的內(nèi)存和io相互作用導(dǎo)致的性能問(wèn)題。
1) 內(nèi)存回收時(shí),不得不做很多臟頁(yè)回寫(xiě)工作
因?yàn)閱螜C(jī)系統(tǒng)比如平板,手機(jī),里面大都是buffer寫(xiě),寫(xiě)到page cache里面就認(rèn)為工作完成就成功返回了, 雖然省事,但是寫(xiě)到page cache里面必然會(huì)帶來(lái)額外的內(nèi)存消耗。
所以我們看到嵌入式系統(tǒng)里面很多場(chǎng)景下(比如上面的u盤(pán)拷貝場(chǎng)景),會(huì)造成某些時(shí)間段內(nèi)系統(tǒng)page cache占內(nèi)存多,其實(shí)是里面積累很多臟頁(yè)。
pagecache占內(nèi)存多,系統(tǒng)free內(nèi)存就會(huì)變少。而偏偏系統(tǒng)的write back線(xiàn)程又不活躍(手機(jī)ext4文件系統(tǒng)里面開(kāi)啟延遲寫(xiě)后,系統(tǒng)每隔30s才回寫(xiě)一次臟頁(yè)),這樣就會(huì)造成 [內(nèi)存分配 → 不得不做內(nèi)存回收] 這樣路徑下,才會(huì)解決pagecache占內(nèi)存多問(wèn)題,具體就是觸發(fā)做臟頁(yè)回寫(xiě)工作。
2) 問(wèn)題就在于:
系統(tǒng)的io寫(xiě)性能不好時(shí),會(huì)影響到page cache里面的臟頁(yè)釋放。臟頁(yè)一旦得不到有效釋放,系統(tǒng)free內(nèi)存越來(lái)越少,這樣內(nèi)存分配性能也會(huì)受到影響。
同時(shí)內(nèi)存分配有問(wèn)題或者回收效率不高,在處理臟頁(yè)回寫(xiě)方面有缺陷時(shí),也會(huì)產(chǎn)生額外的io壓力。所以是內(nèi)存和io相互影響,相互作用。
所以就有了內(nèi)核社區(qū)每年那個(gè)storage and mm國(guó)際會(huì)議,單機(jī)系統(tǒng)里面出現(xiàn)性能問(wèn)題時(shí),內(nèi)存和io往往需要同時(shí)去優(yōu)化。
3) 先去優(yōu)化內(nèi)存,如果不行,再去優(yōu)化io
系統(tǒng)的內(nèi)存和io相互作用導(dǎo)致的性能問(wèn)題出現(xiàn)時(shí),最好用這種優(yōu)化思路,原因很簡(jiǎn)單,內(nèi)存優(yōu)化成本低,風(fēng)險(xiǎn)小。
為什么國(guó)內(nèi)有很多技術(shù)文章或者書(shū)籍都詳細(xì)重點(diǎn)講linux內(nèi)核內(nèi)存管理,為什么文件系統(tǒng)和io這塊不去詳細(xì)講講,其中有一個(gè)重要的原因是:
因?yàn)閲?guó)內(nèi)企業(yè)普遍看到了內(nèi)存優(yōu)化成本低,不行就多殺一些進(jìn)程或者優(yōu)化下內(nèi)存分配和回收,即使是內(nèi)存出問(wèn)題,頂多重啟一下系統(tǒng),問(wèn)題就沒(méi)了。
但是io這塊本身技術(shù)方面就比較復(fù)雜,光內(nèi)核里面,從vfs到ext4具體文件系統(tǒng),再到塊設(shè)備層,再到存儲(chǔ)bsp層,再到存儲(chǔ)硬件芯片層,這些每個(gè)層優(yōu)化的不好,都會(huì)出io性能問(wèn)題,而且部署io性能優(yōu)化方案,往往又要對(duì)這些每個(gè)層都要有所熟悉,這樣才能保證優(yōu)化副作用降到最小。
這還是討論優(yōu)化技術(shù)本身的,還沒(méi)談到,如果存儲(chǔ)io出問(wèn)題,手機(jī)上重要文件損壞,輕則用戶(hù)發(fā)現(xiàn)文件丟失(這樣的損壞和丟失是你再開(kāi)機(jī)多少次都無(wú)法挽回的),重則無(wú)法開(kāi)機(jī)事故就出來(lái)了。
詳細(xì)可以見(jiàn)我的另外一篇技術(shù)文檔:《手機(jī)Android存儲(chǔ)性能優(yōu)化架構(gòu)分析》。
4 解決思路:
在社區(qū)高版本內(nèi)核上面找了一些patch,這些patch是對(duì)內(nèi)核內(nèi)存回收方面的優(yōu)化,重點(diǎn)也是優(yōu)化臟頁(yè)過(guò)多時(shí),內(nèi)存回收耗時(shí)問(wèn)題的。
優(yōu)化1:
1)優(yōu)化原理
內(nèi)存回收時(shí),提前喚醒write-back內(nèi)核線(xiàn)程,提前把所有的臟頁(yè)都寫(xiě)入到磁盤(pán)上。這樣交給專(zhuān)門(mén)的內(nèi)核回寫(xiě)線(xiàn)程來(lái)做臟頁(yè)回寫(xiě)工作,這樣效率更高。
因?yàn)閮?nèi)存回收路徑中做的臟頁(yè)回收是離散寫(xiě),具體是調(diào)用pageout → 文件系統(tǒng)writepage函數(shù),這樣每回只寫(xiě)一個(gè)page,在lru鏈表上積累的臟頁(yè)比較多時(shí),這樣回寫(xiě)效率并不高,因此也影響了內(nèi)存回收。
提前喚醒writeback線(xiàn)程后,writeback線(xiàn)程寫(xiě)會(huì)集中寫(xiě),具體是以inode為單元,會(huì)把每個(gè)inode上的臟頁(yè)數(shù)據(jù)通過(guò)ext4的writepages函數(shù),寫(xiě)到磁盤(pán)上。
如果寫(xiě)的臟頁(yè)數(shù)據(jù)量都一樣,集中寫(xiě)會(huì)做io請(qǐng)求合并,減少io請(qǐng)求處理耗時(shí),這樣顯然比內(nèi)存回收自己離散寫(xiě)臟頁(yè)性能要好多了。
具體性能優(yōu)化數(shù)據(jù)可見(jiàn)我的另外一篇文檔:那些年解的疑難性能問(wèn)題 - ext4碎片整理。
2)patch內(nèi)容
具體是社區(qū)這個(gè)patch: mm/vmscan: wake up flushers for legacy cgroups too.
在內(nèi)存回收必經(jīng)路徑shrink_inactive_list函數(shù)里面,判斷stat.nr_unqueued_dirty == nr_taken的話(huà),說(shuō)明此時(shí)內(nèi)核的inactive lru list里面積累了大量臟頁(yè),需要喚醒writeback線(xiàn)程去集中大批量地寫(xiě)一次。
優(yōu)化2:
1) 優(yōu)化原理
在內(nèi)存回收路徑里面盡量少回收臟頁(yè),少觸發(fā)io操作,這樣會(huì)降低內(nèi)存回收direct reclaim路徑的耗時(shí),也會(huì)間接優(yōu)化內(nèi)存分配slow_path的耗時(shí)。
同時(shí)為了保證多回收內(nèi)存,增加更多free page, 會(huì)多回收些干凈頁(yè)。核心工作是:
把a(bǔ)ctive list上更多的page(比如clean page)加入到inactive list里面,這樣雖然會(huì)造成比如這些active clean page被回收后,很有可能還要被重新讀入內(nèi)存。但是這個(gè)負(fù)作用比起 陷入緩慢的write back臟頁(yè)操作不能立刻滿(mǎn)足前臺(tái)內(nèi)存分配需求 要輕得多。
因?yàn)榇鎯?chǔ)芯片讀是比寫(xiě)要快很多的,所以上面雖然有那個(gè)負(fù)作用,但是不足為慮。
2) patch內(nèi)容
具體是社區(qū)這2個(gè)patch:
mm: vmscan: only write dirty pages that the scanner has seen twice.
在shrink_inactive_list函數(shù)里面,如果direct reclaim,則做以下工作:
是待回收頁(yè)是臟頁(yè)時(shí),進(jìn)一步判斷如果該頁(yè)沒(méi)有設(shè)置reclaim標(biāo)記,那么就僅僅設(shè)置下relcaim標(biāo)記,重新放回active list上,而不去回收它。然后等到第2次再碰到該頁(yè)時(shí),如果還是臟頁(yè),再去回收它。
mm: vmscan: move dirty pages out of the way until they're flushed
在lru_add_drain里面的pagevec_move_tail_fn函數(shù)里面修改,搞成不管該page是否active,都盡可能地把它放到inactive list上去。
這樣會(huì)盡量把a(bǔ)ctive list上的page往inactive list上轉(zhuǎn)移,因?yàn)樯厦鎝atch是在inactive list上碰到臟頁(yè)放到active list上的,所以再帶上這個(gè)patch,那么最終效果是更多的clean page被搬到了inactive list上去,這樣就會(huì)有更多的page被回收掉。
這樣就會(huì)充分釋放了內(nèi)存,接下來(lái)的內(nèi)存分配性能就會(huì)得到優(yōu)化。
二、 內(nèi)存回收里面的boost watermark優(yōu)化改造
改造1
1 問(wèn)題現(xiàn)象
嵌入式設(shè)備里面有時(shí)候會(huì)出現(xiàn)app熱啟動(dòng)慢,抓trace分析后,是啟動(dòng)時(shí)的io讀性能差,差的原因是很多讀不是從pagecache中讀,而是直接從磁盤(pán)上讀。
2 原因分析
有些嵌入式設(shè)備內(nèi)存并不充裕,然后從log中看到,出問(wèn)題的內(nèi)核里面都開(kāi)啟了boost watermark。這個(gè)特性一旦被開(kāi)啟,就會(huì)使得內(nèi)核內(nèi)存回收變得更加活躍,并且是只回收干凈文件頁(yè)的,臟頁(yè)和匿名頁(yè)都不回收。
這個(gè)特性推出的目的本來(lái)是為了降低系統(tǒng)內(nèi)存碎片的(詳見(jiàn)我另外一篇文檔:android內(nèi)存碎片優(yōu)化梳理),但是結(jié)果在低內(nèi)存設(shè)備上副作用更加明顯,更加大于它的收益,把a(bǔ)pp啟動(dòng)時(shí)io讀成功的文件頁(yè)都給回收了,這樣就會(huì)造成系統(tǒng)的整體io讀性能變差。
3 優(yōu)化思路
低內(nèi)存嵌入式設(shè)備上因?yàn)閕o讀性能差問(wèn)題嚴(yán)重,所以可以關(guān)閉該boost watermark優(yōu)化,高內(nèi)存設(shè)備上保留。
ps:
性能優(yōu)化有時(shí)就是這樣一種折衷,優(yōu)化有時(shí)候會(huì)難免有一些副作用,怎么針對(duì)具體問(wèn)題場(chǎng)景,做到衡量評(píng)估好收益和副作用的平衡和折衷,是關(guān)鍵的。
改造2
1 問(wèn)題現(xiàn)象
移動(dòng)設(shè)備上(比如手機(jī),平板)相機(jī)場(chǎng)景里整機(jī)內(nèi)存壓力大,會(huì)造成相機(jī)相關(guān)進(jìn)程內(nèi)存分配性能差,出現(xiàn)相機(jī)操作卡頓問(wèn)題。
2 原因分析
相機(jī)某些操作場(chǎng)景下,相機(jī)自身進(jìn)程會(huì)不可避免有高峰值內(nèi)存分配的需求。當(dāng)陡然切換到這些操作場(chǎng)景時(shí),由于系統(tǒng)沒(méi)有做好應(yīng)對(duì)準(zhǔn)備,滿(mǎn)足不了相機(jī)內(nèi)存分配需求,就會(huì)造成相機(jī)內(nèi)存性能變差。
3 優(yōu)化思路
相機(jī)場(chǎng)景下可以部署下主動(dòng)內(nèi)存回收方案,在高峰值內(nèi)存場(chǎng)景下,可以緩解相機(jī)內(nèi)存分配壓力。
主動(dòng)內(nèi)存回收必須快捷高效,能短時(shí)間內(nèi)釋放出大量可用內(nèi)存出來(lái)。這樣可以借鑒下boost watermark的思想,先回收下文件頁(yè),因?yàn)槲募?yè)比匿名頁(yè)回收要省時(shí)多了。
另外一點(diǎn)是相機(jī)自身進(jìn)程的io讀需求比較少,io讀壓力不大,壓力大的是內(nèi)存和cpu,所以為了短時(shí)間內(nèi)釋放內(nèi)存壓力,是可以多回收些文件頁(yè)的。
同時(shí)改造下boost watermark,把它綁到小核上去干活,避免和相機(jī)進(jìn)程爭(zhēng)用cpu。
三、 從系統(tǒng)全局考慮部署高效內(nèi)存回收方案
1 問(wèn)題背景
嵌入式系統(tǒng),諸如手機(jī),平板,前面提過(guò),內(nèi)存和存儲(chǔ)容量受限,但用戶(hù)對(duì)它的使用需求卻在日益膨脹,幾乎當(dāng)成電腦一樣在用。
所以單機(jī)系統(tǒng)里面,整機(jī)內(nèi)存壓力是比較大的,而內(nèi)存方面的優(yōu)化涉及面廣,拿android系統(tǒng)來(lái)說(shuō),從app到fwk, 再到native層的glibc庫(kù),再到底層內(nèi)核,都會(huì)有內(nèi)存優(yōu)化空間。
內(nèi)核內(nèi)存優(yōu)化比較麻煩耗時(shí)些,而且有些內(nèi)存性能問(wèn)題,從上層入手優(yōu)化,反而更加高效快捷些,所以需要從系統(tǒng)全局考慮出發(fā),去優(yōu)化整機(jī)內(nèi)存。
2 原生android已有的內(nèi)存回收方案
1)用戶(hù)態(tài)的lmkd + 內(nèi)核的內(nèi)存psi兩者結(jié)合,高效殺進(jìn)程
殺進(jìn)程是釋放整機(jī)內(nèi)存壓力最好的方式,系統(tǒng)整機(jī)free內(nèi)存很少時(shí),通過(guò)殺掉一些手機(jī)后臺(tái)低優(yōu)先級(jí)進(jìn)程,可以快速地騰出可用內(nèi)存,供前臺(tái)app使用。
另外基于內(nèi)存psi感知,這樣可以更靈敏地感知到程序有性能問(wèn)題時(shí),就去及時(shí)殺進(jìn)程。
2)Lmkd殺進(jìn)程缺陷
目前的缺陷是,光根據(jù)adj來(lái)進(jìn)行殺進(jìn)程優(yōu)先級(jí)排序還不夠,有些用戶(hù)經(jīng)常使用到的app還是會(huì)被頻繁殺掉,這樣會(huì)影響到用戶(hù)體驗(yàn)。
另外不區(qū)分主進(jìn)程和子進(jìn)程,殺掉主進(jìn)程就會(huì)影響到后臺(tái)app駐留。
所以還需要在fwk層做些優(yōu)化工作,因?yàn)閒wk層最能感知到app業(yè)務(wù)層的變化,在這里最能根據(jù)用戶(hù)體驗(yàn)來(lái)部署優(yōu)化方案。
3)其他的一些優(yōu)化方法
從性能優(yōu)化工作角度出發(fā),感覺(jué)到目前的linux內(nèi)核內(nèi)存管理這塊,更傾向于服務(wù)互聯(lián)網(wǎng)業(yè)務(wù)場(chǎng)景。嵌入式單機(jī)設(shè)備場(chǎng)景比如android手機(jī),想往內(nèi)核主線(xiàn)分支進(jìn)性能優(yōu)化changes,會(huì)發(fā)現(xiàn)比較困難。
所以現(xiàn)在我們看到的比較新的版本上內(nèi)核內(nèi)存回收這塊還是有很多性能問(wèn)題,有很多待優(yōu)化空間的。
所以一些諸如高通芯片原廠,在linux內(nèi)核主線(xiàn)版本上,會(huì)打上一些關(guān)于嵌入式系統(tǒng)方面的優(yōu)化(比如process reclaim等),才會(huì)交給我們使用。
Linux內(nèi)核內(nèi)存回收的一些問(wèn)題和待優(yōu)化空間
一、 內(nèi)存回收目標(biāo)和收益方面的不確定性
1 內(nèi)存回收目標(biāo)
問(wèn)題1)
direct reclaim時(shí),nr_to_reclaim是它的回收目標(biāo),但這個(gè)現(xiàn)在固定死了是32個(gè)page(詳見(jiàn)__alloc_pages_direct_reclaim → try_to_free_pages里面的 nr_to_reclaim被強(qiáng)制賦值為SWAP_CLUSTER_MAX),
那么如果內(nèi)存分配只需要1 - 4個(gè)page時(shí),陷入到slow path里面做內(nèi)存回收,客戶(hù)只需要回收1 - 4個(gè)page就行了,但是內(nèi)存回收這塊會(huì)多回收出額外的31個(gè)page出來(lái)。額外的回收工作必然會(huì)導(dǎo)致回收要多耗時(shí)點(diǎn)。
打開(kāi)底層ftrace,會(huì)經(jīng)??吹角芭_(tái)操作app時(shí),對(duì)應(yīng)的缺頁(yè)異常里面會(huì)每次只分配1到4個(gè)page,說(shuō)明在android系統(tǒng)里面分配少量page的需求還是很多的。
原因
這個(gè)地方定成32,可能是考慮到系統(tǒng)中有很多進(jìn)程在做并發(fā)direct reclaim的,所以為了權(quán)衡系統(tǒng)整體reclaim的壓力和避免更多有用內(nèi)存頁(yè)被回收掉,這個(gè)地方就定成了32.
問(wèn)題2)
內(nèi)核內(nèi)存回收direct reclaim的必經(jīng)函數(shù)shrink_node_memcg最下面,完成回收目標(biāo)后,還要做rebalance the anon lru active/inactive ratio工作,勢(shì)必會(huì)增加direct reclaim進(jìn)程的耗時(shí)。
另外shrink_inactive_list里面還要做too_many_isolated工作,這個(gè)會(huì)導(dǎo)致direct reclaim進(jìn)程睡眠。
原因
可能是為了服務(wù)器場(chǎng)景考慮的,為了優(yōu)化系統(tǒng)全局內(nèi)存狀態(tài),做一些balance工作。但是嵌入式場(chǎng)景下,該內(nèi)存回收架構(gòu)并未區(qū)分前臺(tái)后,這樣前后臺(tái)進(jìn)程在direct reclaim方面一視同仁,
都要做很多balance系統(tǒng)的工作,這樣的話(huà),會(huì)導(dǎo)致前臺(tái)app的內(nèi)存分配耗時(shí)增加。
總結(jié):
上面一些問(wèn)題的發(fā)現(xiàn),說(shuō)明內(nèi)存回收這塊目前的架構(gòu)設(shè)計(jì)是為服務(wù)器場(chǎng)景考慮的,而嵌入式場(chǎng)景,比如手機(jī),比較關(guān)注前臺(tái)app進(jìn)程的響應(yīng)性能的情況下(詳見(jiàn)我的文檔:手機(jī)前后臺(tái)io分組優(yōu)化調(diào)研),
上面內(nèi)存回收的一些耗時(shí)不確定性問(wèn)題,至少說(shuō)明在內(nèi)存回收這個(gè)地方,嵌入式設(shè)備尤其是相機(jī)場(chǎng)景大內(nèi)存分配的情況下,還是會(huì)有優(yōu)化空間的。
2 內(nèi)存回收收益計(jì)算
問(wèn)題
內(nèi)核在做內(nèi)存回收過(guò)程中,要調(diào)用shrink_slab去回收系統(tǒng)的一些緩存。但是內(nèi)核drivers/staging目錄下面一些緩存,比如ion緩存,就沒(méi)有計(jì)算這部分緩存實(shí)際被回收的page數(shù)量。
這樣會(huì)造成direct reclaim中,比如回收目標(biāo)是32個(gè)page,本來(lái)帶上ion cached部分,就達(dá)到回收目標(biāo)了,不需要再進(jìn)行新的一輪回收工作了。
但是由于漏統(tǒng)計(jì)了ion cached的回收部分,還得再多做一輪回收工作,直到回收夠32個(gè)page再結(jié)束。
原因
shrink_slab時(shí),實(shí)際回收成功的文件系統(tǒng)緩存都可以被內(nèi)核內(nèi)存回收模塊統(tǒng)計(jì)到,但是ion cached這塊,可能是位于staging分支的緣故,內(nèi)核主線(xiàn)代碼對(duì)它的管理不是很到位而造成的。
二、 rmap查找的耗時(shí)
這個(gè)是目前內(nèi)核內(nèi)存回收中一個(gè)比較頭疼的性能問(wèn)題。
1 問(wèn)題現(xiàn)象
在android系統(tǒng)里面,用perf工具抓下kswapd的性能profile, 會(huì)發(fā)現(xiàn)內(nèi)核內(nèi)存回收這塊有很多工作花在了 page_referenced -> page_vma_mapped_walk這個(gè)地方了。
之前XXX的系統(tǒng)高負(fù)載時(shí)匿名頁(yè)回收性能調(diào)查博客里面,也提到了相機(jī)app進(jìn)程會(huì)卡在shrink_page_list → page_referenced函數(shù)里面時(shí)間還比較長(zhǎng)。
2 原因分析
1)內(nèi)核內(nèi)存回收搞了個(gè)二次機(jī)會(huì)法,增加了rmap反查耗時(shí)。
二次機(jī)會(huì)法意味著所有的內(nèi)存頁(yè)在進(jìn)入inactive list后,幾乎都要做一遍page_check_references工作,這樣才能被真正回收掉。
這個(gè)地方的工作確實(shí)有必要,因?yàn)閘ru list只是保證了用戶(hù)進(jìn)程先分配使用的內(nèi)存頁(yè)放到tail,后分配使用的內(nèi)存頁(yè)放到了head.
但是在系統(tǒng)運(yùn)行過(guò)程中,如果放到了inactive list tail的內(nèi)存頁(yè)被進(jìn)程再次訪問(wèn)到,那么就會(huì)重新變回active,那么就得靠二次機(jī)會(huì)法來(lái)把它移到active list里面。
二次機(jī)會(huì)法靠這個(gè)page_check_references工作,來(lái)保證內(nèi)核內(nèi)存回收的都是最近不經(jīng)常被使用到的page.
2) android系統(tǒng)的共享頁(yè)很多,也增加了rmap反查耗時(shí)。
android里面的app進(jìn)程都是從zygote進(jìn)程fork出來(lái)的,并且app進(jìn)程自身也會(huì)fork出很多線(xiàn)程。這樣造成了android里面共享頁(yè)的確很多,同一個(gè)page被共享進(jìn)程的數(shù)目也很多。
這樣就會(huì)帶來(lái)page_referenced的反查耗時(shí)工作。
3) lru active list上也加了page_referenced的反查工作。
這部分反查工作一方面是為了recent_rotated計(jì)數(shù),最后是為了get_scan_count里面的確定file list和anon list的查找傾斜度。
高內(nèi)存壓力場(chǎng)合,get_scan_count里面也推薦用SCAN_EQUAL,而不是SCAN_FRACT,所以這種場(chǎng)合還是否有必要做上面傾斜度確定工作,需要再看看是否能進(jìn)一步優(yōu)化下。
另外一方面主要是為了在active page被降級(jí)加入到inactive lru list之前,做下清除映射該page的各用戶(hù)態(tài)進(jìn)程pte的referenced標(biāo)記工作。
因?yàn)橐尤氲絠nactive list上,至少說(shuō)明此時(shí)該page已經(jīng)是stale page, 已經(jīng)被降級(jí)下來(lái)準(zhǔn)備要被回收了,所以應(yīng)該清除下映射該page的pte里面的referenced標(biāo)記了。
內(nèi)核內(nèi)存回收方面的一些梳理:
其實(shí)從這些內(nèi)存回收設(shè)計(jì)方面可以看出,linux內(nèi)存回收這塊還是基于一些經(jīng)驗(yàn)主義的判斷,不可能做到特別準(zhǔn)確地識(shí)別出最久不被使用的內(nèi)存頁(yè)。
就是靠不斷地做rmap反查和一些經(jīng)驗(yàn)主義判斷(文件頁(yè)先放到inactive list上,匿名頁(yè)先放到active list,內(nèi)核認(rèn)為匿名頁(yè)都是比文件頁(yè)更活躍的)
來(lái)最大程度上保證hot page(即每個(gè)進(jìn)程的working-set內(nèi)存頁(yè))放到active list上,cold page(進(jìn)程的非woking set內(nèi)存頁(yè))放到inactive list上去。
其中在每個(gè)page被回收之前,匿名頁(yè)一般被反查2次,文件頁(yè)一般被反查3次,反查的確比較耗時(shí)。
不過(guò)linux kernel在性能方面是針對(duì)通用系統(tǒng),重點(diǎn)是為服務(wù)器系統(tǒng)設(shè)計(jì)的,所以嵌入式方面還是有不少特殊場(chǎng)景(比如相機(jī)高內(nèi)存峰值出現(xiàn)場(chǎng)景)可以做內(nèi)核內(nèi)存回收方面的性能優(yōu)化的。
3 優(yōu)化方法
或者用app compact回收單進(jìn)程,或者借鑒下lwn上的這篇優(yōu)化文章:The multi-generational LRU,講的rmap反查的弊端和優(yōu)化方法。
或者我現(xiàn)在正在調(diào)研的思路:相機(jī)場(chǎng)景下,高效內(nèi)存回收思路。
如果要回收的內(nèi)存數(shù)量多,比如回收1 - 2G,那么其實(shí)可以識(shí)別出相機(jī)自身相關(guān)進(jìn)程使用的內(nèi)存page后,對(duì)非相機(jī)使用的page可以減少上面rmap的反查工作, 理由如下:
1) 因?yàn)榉窍鄼C(jī)進(jìn)程即使發(fā)生page refault,也是僅僅影響的是非相機(jī)進(jìn)程的性能。
2) 在主動(dòng)內(nèi)存回收之前,內(nèi)核的全局lru鏈表中已經(jīng)大概率是一個(gè)比較成熟,比較完美地冷熱內(nèi)存頁(yè)分離的狀況了(hot page放在active list,cold page放在inactive list上),這樣充分利用內(nèi)核的lru list,先回收冷頁(yè)。
3) 最重要此時(shí)相機(jī)操作需要大量?jī)?nèi)存,而系統(tǒng)處于內(nèi)存緊缺狀況,需要短時(shí)間內(nèi)回收出大量?jī)?nèi)存出來(lái)。
而此時(shí)結(jié)合下面的調(diào)研,就算做上面耗時(shí)的rmap反查工作,也會(huì)很容易出現(xiàn)比如幾秒鐘前才被訪問(wèn)過(guò)的頁(yè)面可能都不會(huì)被視作活躍,而被回收掉(因?yàn)榇藭r(shí)回收過(guò)程中頁(yè)掃描會(huì)出現(xiàn)地異常頻繁,會(huì)加速頁(yè)面老化)。
那還不如不做rmap反查,直接回收算了。
三 、內(nèi)核匿名頁(yè)回收冷熱分離做的還不夠好,導(dǎo)致回收效率低
1匿名內(nèi)存和文件內(nèi)存的各自特點(diǎn)
1)匿名內(nèi)存
內(nèi)核認(rèn)為匿名內(nèi)存大部分都是active的,不會(huì)有太多used once或者short-lived的匿名內(nèi)存。
就是說(shuō)內(nèi)核假定:匿名內(nèi)存,比如malloc,一旦被用戶(hù)創(chuàng)建出來(lái)分配好對(duì)應(yīng)的物理內(nèi)存,那么該塊物理內(nèi)存就會(huì)被頻繁使用,如果不被用的話(huà),用戶(hù)會(huì)及時(shí)free該內(nèi)存的(否則的話(huà),就算是內(nèi)存泄漏了)。
但是相機(jī)場(chǎng)景下,不好說(shuō),應(yīng)該會(huì)存在一些并不是被頻繁使用的匿名內(nèi)存,用戶(hù)創(chuàng)建出來(lái)后,是為全局考慮的,會(huì)出現(xiàn)有一段時(shí)間會(huì)頻繁使用,使用完并不釋放,而是過(guò)一段稍微長(zhǎng)時(shí)間后,還會(huì)繼續(xù)用它。
2)文件內(nèi)存
內(nèi)核假定文件內(nèi)存里面有相當(dāng)一部分是used-once或者short-lived內(nèi)存(因?yàn)槲募A(yù)讀會(huì)引入很多不必要內(nèi)存頁(yè)),當(dāng)然也有另外一部分是被頻繁使用的內(nèi)存,兩部分交織在一塊。
2 內(nèi)核對(duì)文件頁(yè)和匿名頁(yè)在inactive/active分離方面的處理方式
1)匿名內(nèi)存
因?yàn)閮?nèi)核認(rèn)為匿名內(nèi)存普通都是hot/active的,只要該塊內(nèi)存存在,就會(huì)被頻繁用到的,所以把新創(chuàng)建的匿名頁(yè)加入到了active list上。
同時(shí)二次機(jī)會(huì)法也不適用它,在inactive list上,只要匿名page被用戶(hù)進(jìn)程訪問(wèn)到了,就會(huì)被立刻晉升到active list上去。
同時(shí)inactive_list_is_low函數(shù)里面也規(guī)定了active list長(zhǎng)度要大于inactive的,另外重要的是匿名頁(yè)也沒(méi)有做working-set識(shí)別,轉(zhuǎn)換和保護(hù)。
2)文件內(nèi)存
上面說(shuō)的其實(shí)是文件內(nèi)存里面都是inactive和active內(nèi)存頁(yè)混合在一起,所以?xún)?nèi)核認(rèn)為文件內(nèi)存不一定都是active的,所以新創(chuàng)建的文件內(nèi)存就會(huì)被加入到inactive list上去,
然后進(jìn)行進(jìn)一步地通過(guò)二次機(jī)會(huì)法來(lái)進(jìn)行篩選,把頻繁訪問(wèn)的,即active內(nèi)存晉升到active list上去,inactive的則被留下在內(nèi)存不足時(shí)優(yōu)先被回收掉。
這樣隨著事件的推移,進(jìn)程的working-set內(nèi)存頁(yè)就會(huì)幾乎全部集中在active list,而不會(huì)出現(xiàn)used-once/short-lived這種inactive內(nèi)存跑到active list上,給進(jìn)程內(nèi)存頁(yè)的冷熱分離帶來(lái)困難。
這樣進(jìn)程的working-set內(nèi)存頁(yè)就會(huì)被建立起來(lái),并且不容易被內(nèi)存不足時(shí)回收掉。
另外進(jìn)程的working-set在特定時(shí)間段內(nèi)是固定的,但是時(shí)間長(zhǎng)了,肯定會(huì)切換到另外一個(gè)working-set里面。
在working-set切換時(shí),文件頁(yè)有refault distance算法來(lái)防止出現(xiàn)切換后,頻繁出現(xiàn)page thrashing.
3 最后結(jié)論
文件頁(yè)回收有很多優(yōu)化算法在保證盡量做到冷熱分離和減小page thrashing,但是匿名頁(yè)卻沒(méi)有這些算法保證,所以匿名頁(yè)回收效率感覺(jué)比文件頁(yè)低,導(dǎo)致的一個(gè)不好影響就是匿名頁(yè)發(fā)生的page thrashing應(yīng)該比較多于文件頁(yè)。
具體僅僅這里是通過(guò)代碼分析發(fā)現(xiàn)的,還得通過(guò)實(shí)驗(yàn)數(shù)據(jù)驗(yàn)證。
四、 內(nèi)核的內(nèi)存回收從來(lái)都是被動(dòng)的回收
1 具體特點(diǎn)
1) 內(nèi)存回收都是內(nèi)存分配不能滿(mǎn)足需求時(shí),才不得不陷入到slow path分配中做回收,這樣對(duì)內(nèi)存分配性能是會(huì)有差的影響的。
2) 就算陷入到slow path中,觸發(fā)的kswapd線(xiàn)程,也是在balance式地回收,只要內(nèi)存水位稍微達(dá)到一定閥值,內(nèi)存處于balanced狀態(tài)后,就停止工作了。
3) 內(nèi)存頁(yè)面的老化,只有在lru list被scan時(shí),才會(huì)老化。也就是說(shuō)LRU里面的老化時(shí)間流逝跟自然時(shí)間是沒(méi)有關(guān)系的。
掃描才是推動(dòng)歷史車(chē)輪前進(jìn)的動(dòng)力。而掃描又是由于達(dá)不到balanced而被觸發(fā)的,可見(jiàn)頁(yè)面老化的速度跟系統(tǒng)中內(nèi)存的緊缺程度是相關(guān)的。
內(nèi)存緊缺的時(shí)候,1分鐘前才被訪問(wèn)過(guò)的頁(yè)面可能都不會(huì)被視作活躍;反過(guò)來(lái),如果內(nèi)存不緊缺,長(zhǎng)期不需要進(jìn)行回收,那么幾小時(shí)前訪問(wèn)過(guò)的頁(yè)面又可能都會(huì)被視作活躍,并且在這段時(shí)間內(nèi)被訪問(wèn)過(guò)一次和被訪問(wèn)過(guò)多次的頁(yè)面會(huì)被同等對(duì)待
看過(guò)內(nèi)存回收二次機(jī)會(huì)法,才會(huì)對(duì)這個(gè)點(diǎn)有深入理解。
4) 目前的防止出現(xiàn)內(nèi)存thrashing而做的workingset保護(hù),只是保護(hù)了文件頁(yè),匿名頁(yè)還未進(jìn)行保護(hù)(好像內(nèi)核5.9版本上才有)。其實(shí)匿名頁(yè)也需要進(jìn)行workingset保護(hù),減小thrashing。
2 導(dǎo)致的問(wèn)題
移動(dòng)設(shè)備相機(jī)場(chǎng)景下,經(jīng)常有突發(fā)性的高峰值內(nèi)存分配需求出現(xiàn)。如果還是上面這種內(nèi)存回收特點(diǎn)的話(huà),是會(huì)導(dǎo)致相機(jī)內(nèi)存分配性能差,從而出現(xiàn)用戶(hù)操作卡頓,殺后臺(tái)現(xiàn)象比較嚴(yán)重。
3 解決方法
制定相機(jī)場(chǎng)景下的殺進(jìn)程管理策略,還有主動(dòng)內(nèi)存回收策略,從而釋放整機(jī)內(nèi)存壓力,優(yōu)化相機(jī)內(nèi)存分配性能。
審核編輯:劉清
-
嵌入式系統(tǒng)
+關(guān)注
關(guān)注
41文章
3648瀏覽量
130117 -
cpu
+關(guān)注
關(guān)注
68文章
10951瀏覽量
213948 -
LINUX內(nèi)核
+關(guān)注
關(guān)注
1文章
316瀏覽量
21866
原文標(biāo)題:使用內(nèi)存回收技術(shù)對(duì)嵌入式系統(tǒng)進(jìn)行性能優(yōu)化
文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
嵌入式系統(tǒng)測(cè)試必備:9大理由解析報(bào)告與可追溯性的重要性(附工具推薦TESSY)

嵌入式工程師都在找的【Linux內(nèi)核調(diào)試技術(shù)】建議收藏!
ARM嵌入式系統(tǒng)中內(nèi)存對(duì)齊的重要性

嵌入式 Linux 操作系統(tǒng)配置
嵌入式Hypervisor:架構(gòu)、原理與應(yīng)用 閱讀體驗(yàn) +分離內(nèi)核的嵌入式Hyperviso

【「嵌入式Hypervisor:架構(gòu)、原理與應(yīng)用」閱讀體驗(yàn)】+第7-8章學(xué)習(xí)心得
【「嵌入式Hypervisor:架構(gòu)、原理與應(yīng)用」閱讀體驗(yàn)】+第一二章讀后感
嵌入式linux開(kāi)發(fā)的基本步驟有哪些?
嵌入式linux開(kāi)發(fā)板怎么操作
嵌入式linux開(kāi)發(fā)板芯片的工作原理
學(xué)習(xí)hypervisor嵌入式產(chǎn)品安全設(shè)計(jì)
《嵌入式機(jī)電一體化系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)》讀后感
【出版發(fā)行】嵌入式系統(tǒng)原理與開(kāi)發(fā)——基于RISC-V和Linux系統(tǒng)

專(zhuān)家力薦|《嵌入式系統(tǒng)原理與開(kāi)發(fā)——基于RISC-V和Linux系統(tǒng)》新書(shū)發(fā)售

評(píng)論