=4個(gè)頁連續(xù)內(nèi)存,理想當(dāng)中我們希望內(nèi)存沒有外部碎片,如下圖所示: 內(nèi)核并未為此目標(biāo)設(shè)計(jì)新的內(nèi)存分配算法(伙伴系統(tǒng)足夠簡單和高效),其選擇在" />

99精品伊人亚洲|最近国产中文炮友|九草在线视频支援|AV网站大全最新|美女黄片免费观看|国产精品资源视频|精彩无码视频一区|91大神在线后入|伊人终合在线播放|久草综合久久中文

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux內(nèi)核內(nèi)存規(guī)整總結(jié)

Linux閱碼場 ? 來源:Linux閱碼場 ? 2023-11-11 11:17 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

1.前言

伙伴系統(tǒng)作為內(nèi)核最基礎(chǔ)的物理頁內(nèi)存分配器,具有高效、實(shí)現(xiàn)邏輯簡介等優(yōu)點(diǎn),其原理頁也盡可能降低內(nèi)存外部碎片產(chǎn)生,但依然無法杜絕碎片問題。外部碎片帶來的最大影響就是內(nèi)存足夠,但是卻無法滿足內(nèi)存分配需求,如下圖所示:

5909c6cc-8026-11ee-939d-92fbcf53809c.png

內(nèi)存外部碎片導(dǎo)致實(shí)際占用物理頁不多,但是已無法申請(qǐng)>=4個(gè)頁連續(xù)內(nèi)存,理想當(dāng)中我們希望內(nèi)存沒有外部碎片,如下圖所示:

5913e31e-8026-11ee-939d-92fbcf53809c.png

內(nèi)核并未為此目標(biāo)設(shè)計(jì)新的內(nèi)存分配算法(伙伴系統(tǒng)足夠簡單和高效),其選擇在伙伴系統(tǒng)基礎(chǔ)上根據(jù)內(nèi)存使用需求進(jìn)行內(nèi)存分配,將不可移動(dòng)內(nèi)存和可移動(dòng)內(nèi)存歸類,在內(nèi)存碎片問題出現(xiàn)時(shí),嘗試進(jìn)行內(nèi)存規(guī)整(compact),移動(dòng)可移動(dòng)的頁面,騰出更多連續(xù)內(nèi)存,如下圖簡述:

591a9506-8026-11ee-939d-92fbcf53809c.png

上圖中將一個(gè)頁移動(dòng)到另一個(gè)頁的過程叫頁遷移,這并不是一件輕松的事情,數(shù)據(jù)的拷貝、進(jìn)程映射信息更改等等都很耗時(shí)并且也是個(gè)復(fù)雜邏輯,這注定內(nèi)存規(guī)整的過程是一個(gè)重負(fù)載的過程。事實(shí)上,頁遷移是內(nèi)存管理的獨(dú)立邏輯,內(nèi)核對(duì)此單獨(dú)封裝接口migrate_pages,內(nèi)存規(guī)整只是其中一個(gè)應(yīng)用場景,類似場景還有NUMA Balance、Memory hotplug及CMA內(nèi)存等等。本文聚焦內(nèi)存規(guī)整,不描述內(nèi)存遷移邏輯。

站在開發(fā)者角度有了內(nèi)存遷移基礎(chǔ)能力,那么就有實(shí)現(xiàn)內(nèi)存規(guī)整基礎(chǔ),但依然有值得思考的問題,比如內(nèi)存規(guī)整的范圍,何時(shí)進(jìn)行內(nèi)存規(guī)整等等。

對(duì)于內(nèi)存規(guī)整范圍問題,內(nèi)核通常選擇以zone為單位進(jìn)行規(guī)整(實(shí)際范圍受到參數(shù)影響可能為zone一部分),并為此封裝compact_zone接口,作為內(nèi)存規(guī)整核心接口(alloc_contig_range例外)。

對(duì)于何時(shí)觸發(fā)問題,屬于觸發(fā)策略和場景問題,內(nèi)核當(dāng)前引入直接內(nèi)存規(guī)整、被動(dòng)內(nèi)存規(guī)整、預(yù)應(yīng)性內(nèi)存規(guī)整及主動(dòng)內(nèi)存規(guī)整四種策略場景,這些場景最終都會(huì)通過compact_zone進(jìn)行內(nèi)存規(guī)整,但是他們觸發(fā)的時(shí)機(jī)不同、目標(biāo)不同、規(guī)整范圍不同、規(guī)整退出條件不同,規(guī)整強(qiáng)度不同等等?;A(chǔ)能力和策略分離設(shè)計(jì)是內(nèi)核的基礎(chǔ)設(shè)計(jì)理念。

591e321a-8026-11ee-939d-92fbcf53809c.png

如上圖所示,內(nèi)存規(guī)整是基于內(nèi)存遷移實(shí)現(xiàn)的功能,內(nèi)核根據(jù)策略在不同實(shí)際觸發(fā)內(nèi)存規(guī)整,用于緩解內(nèi)存外部碎片問題,可以分層分析看待內(nèi)存規(guī)整。

2.內(nèi)存規(guī)整場景

前言中已說明內(nèi)核當(dāng)前觸發(fā)內(nèi)存規(guī)整的策略有四種,為便于查看和直觀理解,優(yōu)先羅列四種場景特點(diǎn),見下表:

592a13b4-8026-11ee-939d-92fbcf53809c.png

上述表格中各規(guī)整策略詳見2.1~2.4節(jié)描述,compact_control各種含義,詳見3.1節(jié)描述。

FAQ:

(1)直接內(nèi)存規(guī)整較為特殊,內(nèi)存分配過程中如果觸發(fā)直接內(nèi)存規(guī)整依然無法分配內(nèi)存,那么有可能循環(huán)調(diào)用并且提高內(nèi)存規(guī)整的級(jí)別,因此出現(xiàn)首次和重試之分。

(2)規(guī)整頁類型中不包含不可回收頁,除非通過sysctl_compact_unevictable_allowed進(jìn)行設(shè)置。

(3)內(nèi)存規(guī)整中有一個(gè)特例就是alloc_contig_range函數(shù),該函數(shù)用于分配指定地址區(qū)域內(nèi)存,若這部分內(nèi)存被占用,會(huì)嘗試對(duì)這段內(nèi)存進(jìn)行規(guī)整遷移,其并非針對(duì)zone的規(guī)整,而是針對(duì)指定內(nèi)存區(qū)域的規(guī)整,它的規(guī)整類型與主動(dòng)內(nèi)存規(guī)整類似,其實(shí)現(xiàn)核心是內(nèi)存規(guī)整機(jī)制,本文不對(duì)此邏輯進(jìn)行說明。

2.1 直接內(nèi)存規(guī)整(direct compaction)

2.1.1 直接內(nèi)存規(guī)整觸發(fā)條件

伙伴系統(tǒng)分配內(nèi)存時(shí),會(huì)先以low水線為基準(zhǔn)調(diào)用get_page_from_freelist函數(shù)嘗試進(jìn)行內(nèi)存分配,如果失敗則會(huì)進(jìn)入慢速內(nèi)存分配流程,即__alloc_pages_slowpath函數(shù),我們對(duì)此函數(shù)邏輯稍作刪減,內(nèi)容如下:

59353794-8026-11ee-939d-92fbcf53809c.png

慢速內(nèi)存分配,會(huì)嘗試喚醒kswapd進(jìn)行內(nèi)存回收,但并不會(huì)等待內(nèi)存回收的結(jié)果,而是直接先調(diào)用get_page_from_freelist函數(shù)嘗試內(nèi)存分配,但這次不同的是使用min水線進(jìn)行嘗試,如果依然失敗,那么將會(huì)根據(jù)gfp標(biāo)識(shí)確認(rèn)當(dāng)前分配是否支持直接內(nèi)存回收,若支持,將會(huì)調(diào)用__alloc_pages_direct_compact嘗試第一次直接內(nèi)存規(guī)整以及內(nèi)存分配。如果依然失敗,則進(jìn)入喚醒kswapd、get_page_from_freelist、__alloc_pages_direct_reclaim及__alloc_pages_direct_compact循環(huán)調(diào)用流程里面來,當(dāng)然這之中存在眾多條件判斷隨時(shí)可能返回頁分配失敗、頁分配成功、重試甚至是觸發(fā)OOM。值得注意的是在慢速內(nèi)存分配邏輯中,首次調(diào)用直接內(nèi)存規(guī)整時(shí)其優(yōu)先級(jí)設(shè)置為INIT_COMPACT_PRIORITY,這將影響內(nèi)存規(guī)整觸發(fā)頁遷移的類型,比如INIT_COMPACT_PRIORITY對(duì)應(yīng)的就是MIGRATE_ASYNC即異步遷移類型代表頁遷移時(shí)不會(huì)阻塞,當(dāng)然這樣帶來的效果就是規(guī)整或遷移的能力較弱。慢速內(nèi)存分配邏輯中后續(xù)直接內(nèi)存規(guī)整調(diào)用其規(guī)整優(yōu)先級(jí)可能會(huì)逐步降低(越低對(duì)應(yīng)規(guī)整強(qiáng)度越高)從而提升內(nèi)存規(guī)整效用,但是內(nèi)存規(guī)整可能變?yōu)樽枞?guī)整,這是相互對(duì)應(yīng)邏輯。

通過上述描述,可以初步了解直接內(nèi)存規(guī)整起到的作用,也可以感受到內(nèi)核內(nèi)存分配進(jìn)入到慢速分配邏輯后性能的代價(jià)。

另一方面,直接內(nèi)存規(guī)整實(shí)際是由于伙伴系統(tǒng)無法分配內(nèi)存時(shí)觸發(fā),因此直接內(nèi)存規(guī)整目標(biāo)并也并非消除整個(gè)zone的外部碎片,而只是通過內(nèi)存規(guī)整遷移出目標(biāo)階連續(xù)內(nèi)存。

2.1.2 直接內(nèi)存規(guī)整邏輯說明

__alloc_pages_direct_compact函數(shù)是直接內(nèi)存規(guī)整運(yùn)行入口,該函數(shù)核心內(nèi)容如下:

593bed0a-8026-11ee-939d-92fbcf53809c.png

try_to_compact_pages函數(shù)將會(huì)進(jìn)一步調(diào)用compact相關(guān)流程進(jìn)行規(guī)整,規(guī)整完成后調(diào)用get_page_from_freelist進(jìn)行內(nèi)存分配。try_to_compact_pages核心代碼邏輯如下:

594479c0-8026-11ee-939d-92fbcf53809c.png

try_to_compact_pages函數(shù)核心,遍歷規(guī)定范圍內(nèi)zone,針對(duì)每個(gè)zone調(diào)用compaction_deferred確認(rèn)其是否合適進(jìn)行規(guī)整,若合適進(jìn)一步調(diào)用compact_zone_order函數(shù)進(jìn)行規(guī)整,規(guī)整成功則直接內(nèi)存規(guī)整將會(huì)直接返回。在try_to_compact_pages函數(shù)中我們重點(diǎn)說明一下zone延遲判斷邏輯,這部分邏輯同樣適用于后續(xù)kcompactd對(duì)于zone的判斷。

2.1.2.1 延遲規(guī)整

compaction_deferred函數(shù)用于判斷當(dāng)前zone是否需要進(jìn)行延遲處理,延遲的目的是避免頻繁或無效的內(nèi)存規(guī)整,其引入兩個(gè)機(jī)制用于延遲,一個(gè)是內(nèi)存規(guī)整失敗階判斷,另一個(gè)是內(nèi)存規(guī)整延遲次數(shù)判斷(這更像一種計(jì)時(shí)器)。

5947fcbc-8026-11ee-939d-92fbcf53809c.png

A) 規(guī)整失敗階的判斷(compact_order_failed

如果當(dāng)前規(guī)整階大于等于zone的最大規(guī)整失敗階,那么代表當(dāng)前再去規(guī)整失敗的可能性很高,建議延遲對(duì)當(dāng)前zone規(guī)整。

B) 內(nèi)存規(guī)整延遲次數(shù)超閾值判斷

如果失敗階判斷滿足,那么會(huì)對(duì)延遲次數(shù)進(jìn)行判斷,compact_considered記錄了當(dāng)前zone延遲次數(shù),compaction_deferred每次調(diào)用時(shí)compact_considered都會(huì)累加,如果其小于閾值,那么建議zone不進(jìn)行規(guī)整,標(biāo)識(shí)近期可能已經(jīng)進(jìn)行過規(guī)整。

594de924-8026-11ee-939d-92fbcf53809c.png

可以想象到,延遲判斷的這些參數(shù)會(huì)動(dòng)態(tài)變化,實(shí)際如上圖所示。

A) 當(dāng)內(nèi)存規(guī)整成功時(shí),調(diào)用compaction_defer_reset函數(shù)清空compact_considered延遲計(jì)數(shù),清空compact_defer_shift延遲計(jì)數(shù)閾值(defer_limit = compact_defer_shift << 1),同時(shí)如果當(dāng)前order大于等于compact_defer_shift ,則更新compact_order_failed最大規(guī)整失敗階。

總結(jié),當(dāng)規(guī)整成功時(shí),會(huì)降低此zone延遲標(biāo)準(zhǔn),讓后續(xù)對(duì)zone規(guī)整判斷變得更為容易。

B) 當(dāng)內(nèi)存規(guī)整失敗時(shí),依然會(huì)將compact_considered清零,若order大于更新更新compact_order_failed最大規(guī)整失敗階,增大compact_defer_shift延遲計(jì)數(shù)閾值。

總結(jié),當(dāng)規(guī)整失敗時(shí),會(huì)增大此zone延遲標(biāo)準(zhǔn),讓后續(xù)對(duì)zone規(guī)整將會(huì)延遲更多次。

通過上述延遲方案,確保對(duì)于某個(gè)zone不做重復(fù)規(guī)整、不做成功率低的規(guī)整,當(dāng)一次對(duì)zone規(guī)整失敗時(shí),內(nèi)核將會(huì)盡量給與zone足夠時(shí)間然后再進(jìn)行嘗試。zone延遲判斷機(jī)制適用于直接內(nèi)存規(guī)整以及kcompactd內(nèi)存規(guī)整機(jī)制,這兩種機(jī)制對(duì)于耗時(shí)較為敏感,其它場景內(nèi)存規(guī)整通常不需要此機(jī)制。

2.1.2.2 capture_control說明

再次回到try_to_compact_pages函數(shù),一個(gè)zone在通過延遲判斷后,將會(huì)調(diào)用compact_zone_order函數(shù),該函數(shù)核心是定義compact_control并調(diào)用compact_zone完成規(guī)整。但是這里引入了一個(gè)很有意思的機(jī)制capture_control,因此需要額外進(jìn)行說明,這筆修改可見如下內(nèi)容:

595f18a2-8026-11ee-939d-92fbcf53809c.png

通常的邏輯通過內(nèi)存規(guī)整遷移出目標(biāo)階內(nèi)存塊,再進(jìn)行內(nèi)存分配,而為了提效capture_control的思路則是在內(nèi)存規(guī)整的過程中就將內(nèi)存分配出來,只不過這個(gè)分配更像是截胡,在直接內(nèi)存規(guī)整的過程中,若發(fā)生內(nèi)存釋放,則在伙伴系統(tǒng)內(nèi)存釋放邏輯中截胡合適的內(nèi)存,下面詳細(xì)說明這個(gè)過程。

596b5efa-8026-11ee-939d-92fbcf53809c.png

在compact_zone_order函數(shù)會(huì)填充capture_control變量,并將其賦值給當(dāng)前進(jìn)程上下文,標(biāo)志著當(dāng)前進(jìn)程進(jìn)入到直接規(guī)整邏輯里面??梢韵胂笤趦?nèi)存規(guī)整過程中涉及內(nèi)存釋放,此時(shí)capture開始行動(dòng),代碼如下:

59720886-8026-11ee-939d-92fbcf53809c.png

內(nèi)存釋放流程中通過compaction_capture嘗試捕獲已釋放的內(nèi)存,compaction_capture函數(shù)代碼實(shí)現(xiàn)如下:

5975941a-8026-11ee-939d-92fbcf53809c.png

釋放內(nèi)存階必須與直接內(nèi)存規(guī)整階相等才有可能捕獲,同時(shí)需要強(qiáng)調(diào)如果當(dāng)前釋放的是MIGRATE_MOVABLE類型頁盡量不去捕獲,避免污染可移動(dòng)頁面,因?yàn)橛|發(fā)直接規(guī)整的有可能是不可移動(dòng)的內(nèi)存請(qǐng)求。

2.1.3 直接內(nèi)存規(guī)整特點(diǎn)

597bf47c-8026-11ee-939d-92fbcf53809c.png

(1)指定了規(guī)整目標(biāo)階,降低規(guī)整范圍和難度;

(2)遷移頁掃描器和空閑頁掃描器,使用快速掃描能力;

(3)其指定highest_zoneidx和目標(biāo)階,因此存在水線判斷。

(4)direct_compaction設(shè)置直接規(guī)整標(biāo)識(shí);

直接內(nèi)存規(guī)整優(yōu)先級(jí)COMPACT_PRIO_ASYNC逐步升高,內(nèi)存規(guī)整強(qiáng)度將會(huì)增強(qiáng),內(nèi)容如下:

(5)隨著規(guī)整失敗,規(guī)整模式MIGRATE_ASYNC變?yōu)镸IGRATE_SYNC_LIGHT,即直接內(nèi)存規(guī)整可能是不阻塞也可能是阻塞模式;

(6)隨著規(guī)整失敗,規(guī)整范圍從根據(jù)上次規(guī)整結(jié)果制定范圍變?yōu)橥暾鹺one地址范圍;

(7)隨著規(guī)整失敗,pageblock將會(huì)被重新掃描,不會(huì)根據(jù)標(biāo)記skip,逐步加強(qiáng)規(guī)整強(qiáng)度;

(8)隨著規(guī)整失敗,空閑頁掃描器將變得嚴(yán)格,空閑頁必須來自于MIGRATE_MOVABLE和MIGRATE_CMA可移動(dòng)的頁面;

上述compact_control結(jié)構(gòu)體參數(shù)含義見3.1節(jié);

2.2 被動(dòng)內(nèi)存規(guī)整(kcompactd)

內(nèi)核在啟動(dòng)過程中會(huì)調(diào)kcompactd_init函數(shù),為每個(gè)node啟動(dòng)一個(gè)kcompactd內(nèi)核線程,并且kcompactd線程會(huì)運(yùn)行在與node相對(duì)應(yīng)的CPU核上,在合適的時(shí)機(jī)kcompactd將會(huì)被喚醒進(jìn)行內(nèi)存規(guī)整,這就是被動(dòng)內(nèi)存規(guī)整邏輯。一個(gè)特殊場景是若開啟proactive compaction功能,那么kcompactd會(huì)被周期性喚醒。

本節(jié)主要從三個(gè)方面說明,分別是kcompactd喚醒條件、kcompactd運(yùn)行條件、以及kcompactd內(nèi)存規(guī)整特點(diǎn)(kcompactd被喚醒不一定會(huì)進(jìn)行內(nèi)存規(guī)整)。

2.2.1 kcompactd喚醒條件

內(nèi)存規(guī)整模塊向內(nèi)核提供wakeup_kcompactd口用于喚醒node對(duì)應(yīng)的kcompactd線程,內(nèi)核中kcompactd喚醒與kswapd強(qiáng)相關(guān),總結(jié)如下場景會(huì)被被動(dòng)喚醒:

598d7c4c-8026-11ee-939d-92fbcf53809c.png

FAQ:這里指的觸發(fā)內(nèi)存規(guī)整,指的是調(diào)用wakeup_kcompactd函數(shù),未必真的進(jìn)行內(nèi)存規(guī)整,wakeup_kcompactd還存在諸多判斷;

2.2.1.1 kswapd運(yùn)行前觸發(fā)內(nèi)存規(guī)整

5997974a-8026-11ee-939d-92fbcf53809c.png

當(dāng)內(nèi)存分配失敗時(shí)經(jīng)各種判斷后,會(huì)進(jìn)?內(nèi)存慢速分配過程,此時(shí)伙伴系統(tǒng)將嘗試喚醒內(nèi)存回收,在這個(gè)過程中,有如下關(guān)鍵代碼:

599de0b4-8026-11ee-939d-92fbcf53809c.png

上述代碼為未喚醒kswapd前進(jìn)行內(nèi)存規(guī)整的條件判斷,其意圖如下:

(1)kswapd內(nèi)存回收失敗多次;

(2)根據(jù)pgdat_balanced函數(shù)判斷當(dāng)前水位安全,即存在?夠可?內(nèi)存并且未出現(xiàn)”偷“內(nèi)存情況;本質(zhì)在于,當(dāng)前內(nèi)存無法分配的原因并非低內(nèi)存,此時(shí)內(nèi)存回收可能已經(jīng)無法解決此問題時(shí),wakeup_kswapd函數(shù)將會(huì)提前進(jìn)行內(nèi)存規(guī)整。這里還需要說明的是,內(nèi)存分配指定不支持直接內(nèi)存回收時(shí)上述邏輯才能生效,這是因?yàn)槿糁С种苯觾?nèi)存規(guī)整,則可以借助直接內(nèi)存回收來進(jìn)行改善并且通常直接內(nèi)存規(guī)整有更好的性能表現(xiàn)。

2.2.1.2 kswapd運(yùn)行中觸發(fā)內(nèi)存規(guī)整

59a1990c-8026-11ee-939d-92fbcf53809c.png

watermark_boost_factor導(dǎo)致的內(nèi)存規(guī)整,歸類為kswapd運(yùn)行中觸發(fā)內(nèi)存規(guī)整稍有牽強(qiáng),不過其確實(shí)是在kswapd內(nèi)存規(guī)整核心邏輯中觸發(fā)。這里簡單介紹?下watermark_boost_factor特性,當(dāng)分配內(nèi)存時(shí)如果在對(duì)應(yīng)migrate type上沒有分配到內(nèi)存,那么系統(tǒng)將會(huì)從fall_back的migrate type進(jìn)行內(nèi)存分配,有時(shí)將其叫做”偷“,由于分配了不匹配遷移類型的內(nèi)存,內(nèi)核會(huì)認(rèn)為這可能存在外部碎片的風(fēng)險(xiǎn),所以當(dāng)出現(xiàn)這種”偷“時(shí)內(nèi)核會(huì)提前進(jìn)行內(nèi)存回收及規(guī)整,從而降低后續(xù)”偷“行為的發(fā)生,避免內(nèi)存碎片問題,提升內(nèi)存分配的效率,這就是watermark_boost_factor特性。

59ae6380-8026-11ee-939d-92fbcf53809c.png

steal_suitable_fallback函數(shù)是從其它遷移類型上分配內(nèi)存的核心邏輯,此函數(shù)中會(huì)設(shè)置?個(gè)ZONE_BOOSTED_WATERMARK標(biāo)志位,這個(gè)標(biāo)志位只能被kswapd清除,伙伴系統(tǒng)在內(nèi)存分配成功后,如果發(fā)現(xiàn)ZONE_BOOSTED_WATERMARK被置位,將會(huì)喚醒kswapd線程。

59c4b7ca-8026-11ee-939d-92fbcf53809c.png

kswapd函數(shù)通過調(diào)用balance_pgdat進(jìn)行內(nèi)存回收,而balance_pgdat函數(shù)會(huì)在整體內(nèi)存回收結(jié)束后,嘗試喚醒kcompactd線程,當(dāng)然前提是本次內(nèi)存回收是boosted屬性的內(nèi)存回收。

59c84fe8-8026-11ee-939d-92fbcf53809c.png

boost特性引起內(nèi)存規(guī)整,其規(guī)整階為pageblock對(duì)應(yīng)階,在后續(xù)compact_zone函數(shù)介紹中將會(huì)說明其影響。上述僅僅簡單描述watermark_boost_factor特性,其還涉及到內(nèi)存回收時(shí)boost導(dǎo)致水線提升等特性,由于其與內(nèi)存回收關(guān)系較大,本文不深入分析,大家可通過如下合入記錄進(jìn)?步了解:

59d8f2ee-8026-11ee-939d-92fbcf53809c.png

2.2.1.3 kswapd運(yùn)行后觸發(fā)內(nèi)存規(guī)整

59dc590c-8026-11ee-939d-92fbcf53809c.png

kswapd內(nèi)核線程,每次在內(nèi)存回收完畢后將會(huì)調(diào)?kswapd_try_to_sleep嘗試休眠,此函數(shù)會(huì)在休眠前調(diào)用reset_isolation_suitable函數(shù)清空遷移掃描器和空閑頁掃描器掃描過程中產(chǎn)生的緩存數(shù)據(jù)(比如某個(gè)pageblock是否需要跳過信息等等),隨后調(diào)用wakeup_kcompactd函數(shù)嘗試進(jìn)行內(nèi)存規(guī)整。

簡單說,每次kswapd運(yùn)行完成后都會(huì)嘗試喚醒內(nèi)存規(guī)整線程,但是內(nèi)存規(guī)整線程是否真的需要運(yùn)行,其有復(fù)雜的判斷邏輯。

2.2.2 kcompactd運(yùn)行條件

當(dāng)kcompactd線程運(yùn)行時(shí),若當(dāng)前是非預(yù)應(yīng)性規(guī)整(詳見2.3節(jié)),那么將會(huì)調(diào)用

kcompactd_do_work函數(shù)進(jìn)行被動(dòng)內(nèi)存規(guī)整。kcompactd_do_work函數(shù)會(huì)遍歷當(dāng)前node的所有zone進(jìn)行合法性判斷,若符合條件,則調(diào)用compact_zone針對(duì)zone進(jìn)?內(nèi)存規(guī)整,其對(duì)zone的判斷邏輯如下:

59e558cc-8026-11ee-939d-92fbcf53809c.png

根據(jù)上述代碼,可以總結(jié)如下判斷邏輯:

A. zone是否包含有效物理頁,若不包含,不需要規(guī)整;

B. 當(dāng)前規(guī)整目標(biāo)階是否大于等于之前規(guī)整失敗的最小階,若大于不需要規(guī)整;

C. 是否需要延遲規(guī)整,若延遲次數(shù)超過閾值則須規(guī)整,否則不規(guī)整,避免無效規(guī)整;

D. 當(dāng)前內(nèi)存水線是否滿足內(nèi)存申請(qǐng),若滿足則不規(guī)整;

E. 若當(dāng)前order大于3同時(shí)不滿足內(nèi)存分配,則評(píng)估其內(nèi)存碎片程度,若小于閾值,則不規(guī)整;其中,populated_zone函數(shù)僅僅判斷當(dāng)前zone是否存在有效物理頁,其它邏輯在compaction_deferred和compaction_suitable函數(shù)中實(shí)現(xiàn)。

2.2.2.1 延遲規(guī)整

在直接內(nèi)存規(guī)整中已經(jīng)介紹了延遲判斷的邏輯,對(duì)于kcompactd同樣使用,下述代碼可以看到其對(duì)延遲參數(shù)的更新邏輯。

59f52310-8026-11ee-939d-92fbcf53809c.png

如果comapct_zone返回值是由于掃描器相遇導(dǎo)致(一輪掃描結(jié)束),而非規(guī)整目標(biāo)達(dá)成導(dǎo)致,代表規(guī)整并未達(dá)成目標(biāo),同樣按照失敗處理。

2.2.2.2 水線判斷

compaction_suitable函數(shù)一方面通過__compaction_suitable判斷水線是否滿足當(dāng)前order階內(nèi)存分配要求,若滿足則當(dāng)前zone不需要規(guī)整;另一方面,通過fragmentation_index函數(shù)獲取當(dāng)前zone對(duì)于order階內(nèi)存塊碎片程度評(píng)估,如果認(rèn)為碎片程度不高,則不進(jìn)行規(guī)整。先來分析__compaction_suitable函數(shù),此函數(shù)用于評(píng)估當(dāng)前水線是否滿足內(nèi)存分配,若不滿足,則評(píng)估水線是否滿足內(nèi)存規(guī)整過程中內(nèi)存占用需求,關(guān)鍵實(shí)現(xiàn)如下:

59f915b0-8026-11ee-939d-92fbcf53809c.png

此函數(shù)中,調(diào)用兩次__zone_watermark_ok進(jìn)行水線判斷,__zone_watermark_ok函數(shù)功能是判斷當(dāng)前zone在分配order階內(nèi)存塊后水線是否達(dá)標(biāo)。第一次水線檢查成功,代表當(dāng)前zone可以滿足內(nèi)存分配訴求,因此當(dāng)前zone不用規(guī)整。若第一次調(diào)用失敗,則嘗試第二次判斷,第二次水線判斷的目的是確認(rèn)內(nèi)存規(guī)整過程中是否可能存在水線問題,因?yàn)閮?nèi)存規(guī)整過程中需要order階空閑頁用于內(nèi)存遷移。因此第二次水線的判斷,僅對(duì)0階內(nèi)存判斷,因?yàn)檫w移過程中申請(qǐng)空閑頁都是單頁,另一方面watermark增加order階內(nèi)存塊代表遷移內(nèi)存占用,但是需要注意的是理論上遷移只需要order階大小的內(nèi)存,但是實(shí)際watermark增加了2倍order階大小的內(nèi)存(compact_gap函數(shù)),這是由于實(shí)際內(nèi)存規(guī)整過程中由于遷移頁掃描器可能掃描出大于order階待遷移內(nèi)存,因此空閑頁掃描器也會(huì)占用大于order階空閑頁,為了確保評(píng)估的安全性,改為2倍order階內(nèi)存,這部分說明在compact_gap函數(shù)內(nèi)部注釋中有所描述。

2.2.2.3 各階碎片評(píng)分判斷

fragmentation_index函數(shù)用于獲取目標(biāo)階碎片程度評(píng)價(jià),從而評(píng)估當(dāng)前內(nèi)存無法分配的原因是由于低內(nèi)存還是外部碎片導(dǎo)致。

5a0a6ef0-8026-11ee-939d-92fbcf53809c.png

其中fill_contig_page_info會(huì)遍歷zone內(nèi)存,統(tǒng)計(jì)當(dāng)前空閑頁以及對(duì)應(yīng)order階空閑區(qū)域數(shù)量,此函數(shù)下文將會(huì)詳細(xì)介紹,最后通過__fragmentation_index函數(shù)和上述遍歷空閑頁獲得的相關(guān)信息進(jìn)行計(jì)算評(píng)估。代碼中計(jì)算公式如下:

5a11bfb6-8026-11ee-939d-92fbcf53809c.png

先明確info->free_blocks_total代表當(dāng)前zone中各個(gè)order階內(nèi)存區(qū)域數(shù)量,info->free_pages代表當(dāng)前zone中空閑頁的數(shù)量,requested代表order階對(duì)應(yīng)頁數(shù)量。我們命名info->free_pages/requested為target_order_blocks,在當(dāng)前空閑頁狀態(tài)下若不存在內(nèi)存碎片問題這種理想狀態(tài)下?lián)碛卸嗌賯€(gè)order階內(nèi)存塊,現(xiàn)在重新簡化公式,即可獲得如下描述:

5a1b08aa-8026-11ee-939d-92fbcf53809c.png

極限狀態(tài)info->free_blocks_total非常大時(shí),意味著嚴(yán)重的內(nèi)存碎片問題,上述值趨近于1,反之0代表內(nèi)存不足問題。__fragmentation_index函數(shù)引入1000這個(gè)數(shù)值參與運(yùn)算是為了避免小數(shù),導(dǎo)致返回值不易判斷,現(xiàn)在引入此值后,使函數(shù)返回值落入-1000,0~1000范圍中,其中-1000場景較為特殊,其代表當(dāng)前zone滿足內(nèi)存分配需求,但是在此之前卻通過了上文中__compaction_suitable函數(shù)的判斷,當(dāng)前-1000返回值實(shí)際在代碼中似乎并未被使用。重點(diǎn)還是回到0~1000返回值,那么數(shù)值越大代表對(duì)于當(dāng)前order階內(nèi)存塊而言碎片程度越高,難以分配。

再回到compaction_suitable函數(shù),有兩個(gè)關(guān)注點(diǎn),一個(gè)是對(duì)于內(nèi)存分配階大于3的申請(qǐng)才會(huì)利用fragmentation_index進(jìn)行額外判斷(對(duì)于較小內(nèi)存需求評(píng)估其內(nèi)存碎片程度意義降低,例如單頁內(nèi)存分配,伙伴系統(tǒng)基本分配單元,就不涉及內(nèi)存外部碎片問題);另一個(gè)是,fragmentation_index返回值需要和sysctl_extfrag_threshold閾值進(jìn)行比較,如果小于閾值,則不進(jìn)行規(guī)整,此值通過/proc/sys/vm/extfrag_threshold進(jìn)行設(shè)置。

FAQ:基于上述邏輯,內(nèi)核中可以通過/sys/kernel/debug/extfrag/extfrag_index文件節(jié)點(diǎn)查看各個(gè)階外部碎片評(píng)估數(shù)據(jù),其數(shù)值對(duì)1000取余為小數(shù),越趨近于1,代表當(dāng)前order階內(nèi)存碎片程度高,相關(guān)代碼如下:

5a2d0820-8026-11ee-939d-92fbcf53809c.png

2.2.3 kcompactd規(guī)整特點(diǎn)

內(nèi)存規(guī)整的特點(diǎn)還是需要從其配置compact_control來進(jìn)行說明,下述代碼為kcompactd規(guī)整CC設(shè)置。

5a309bde-8026-11ee-939d-92fbcf53809c.png

(1)指定了規(guī)整目標(biāo)階,降低規(guī)整范圍和難度;

(2)遷移頁掃描器和空閑頁掃描器,使用快速掃描能力;

(3)其指定highest_zoneidx和目標(biāo)階,因此存在水線判斷。

(4)規(guī)整模式為MIGRATE_SYNC_LIGHT,輕度同步模式,會(huì)阻塞;

(5)規(guī)整范圍從根據(jù)上次規(guī)整結(jié)果繼續(xù)規(guī)整;

(6)pageblock將會(huì)根據(jù)標(biāo)記選擇跳過,避免頻繁掃描;

上述compact_control結(jié)構(gòu)體參數(shù)含義見3.1節(jié);

2.3 預(yù)應(yīng)性內(nèi)存規(guī)整(proactive compaction)

這屬于新增內(nèi)存規(guī)整特性,其最初目的是降低大頁分配延遲,通過大頁內(nèi)存塊碎片程度決策當(dāng)前是否啟動(dòng)內(nèi)存規(guī)整,提前減少內(nèi)存碎片,提升大頁分配性能。以下鏈接對(duì)此特性做了原理性說明:

https://lwn.net/Articles/817905/

實(shí)際最終代碼與上文的最初設(shè)想已不相同,下文將依據(jù)代碼說明,代碼合?記錄如下:

https://lore.kernel.org/all/20200616204527.19185-1-nigupta@nvidia.com/T/#u

2.3.1 觸發(fā)預(yù)應(yīng)行規(guī)整

預(yù)應(yīng)性規(guī)整并非獨(dú)立存在,其融?kcompactd內(nèi)核線程,但又與kcompactd原有功能互斥。關(guān)鍵代碼如下:

5a402194-8026-11ee-939d-92fbcf53809c.png

上述代碼總結(jié)如下:

(1)若設(shè)置預(yù)應(yīng)性規(guī)整,kcompactd將會(huì)每500ms(HPAGE_FRAG_CHECK_INTERVAL_MSEC宏設(shè)置)喚醒判斷當(dāng)前是否需要進(jìn)行規(guī)整;

(2)若設(shè)置預(yù)應(yīng)性規(guī)整,則會(huì)跳過kcompactd原有邏輯,調(diào)用預(yù)應(yīng)性規(guī)整判斷及規(guī)整邏輯;

(3)預(yù)應(yīng)性規(guī)整觸發(fā)的依據(jù)是根據(jù)整個(gè)node的碎片化程度決策;

(4)預(yù)應(yīng)性規(guī)整會(huì)在規(guī)整前后統(tǒng)計(jì)碎片得分,若得分增加,達(dá)標(biāo)碎片問題未解決,那么會(huì)增加kcompactd喚醒周期,避免頻繁無效的預(yù)應(yīng)性規(guī)整;

(5)每輪循環(huán)后,均會(huì)關(guān)閉預(yù)應(yīng)性規(guī)整,這是考慮到在某些嵌入式場景下并不需要頻繁的喚醒并判斷,將啟動(dòng)預(yù)應(yīng)性規(guī)整策略交給用戶層,如下合入增加了這個(gè)功能:

https://lore.kernel.org/all/1627653207-12317-1-git-send-emailcharante@

codeaurora.org/T/#u

預(yù)應(yīng)性規(guī)整在內(nèi)核中引入vm.compaction_proactiveness文件節(jié)點(diǎn),可以向其寫入0~100數(shù)值,其用于指定預(yù)應(yīng)性規(guī)整的積極性,數(shù)值越大積極性越高(見2.3節(jié)),如果設(shè)置為0相當(dāng)于關(guān)閉,設(shè)置此數(shù)值時(shí),pgdat->proactive_compact_trigger將會(huì)被設(shè)置為true,預(yù)應(yīng)性內(nèi)存規(guī)整功能打開,此節(jié)點(diǎn)實(shí)現(xiàn)不過多描述。

2.3.2 碎片程度評(píng)估

預(yù)應(yīng)性內(nèi)存規(guī)整的碎片化評(píng)價(jià),實(shí)際是對(duì)大頁碎片程度的評(píng)價(jià),其本身也是為了解決大頁分配延遲而產(chǎn)生,與上文各order階內(nèi)存碎片評(píng)估方式不同,目前也并不針對(duì)其它order階內(nèi)存,通過如下代碼進(jìn)?定義:

5a49fbec-8026-11ee-939d-92fbcf53809c.png

即便未開啟大頁功能,COMPACTION_HPAGE_ORDER通常也會(huì)被設(shè)置為9,代表要預(yù)應(yīng)性內(nèi)存規(guī)整主要是針對(duì)2MB內(nèi)存碎片程度進(jìn)行評(píng)估,下面從最頂層代碼進(jìn)行說明。

2.3.2.1 should_proactive_compact_node

should_proactive_compact_node 函數(shù)計(jì)算當(dāng)前node碎片程度,當(dāng)然是針對(duì)

COMPACTION_HPAGE_ORDER階內(nèi)存塊,其將會(huì)對(duì)每個(gè)zone進(jìn)行評(píng)估得分,并將所有zone所得分?jǐn)?shù)累加,獲得最后得分。此得分如果高于預(yù)應(yīng)性規(guī)整水線線,代表碎片化程度較高,需要進(jìn)行預(yù)應(yīng)性規(guī)整。之前通過vm.compaction_proactiveness節(jié)點(diǎn)設(shè)置積極性,將會(huì)影響預(yù)應(yīng)性規(guī)水線,其值越高,預(yù)應(yīng)性水線值越低,將越容易觸發(fā)規(guī)整。預(yù)應(yīng)性水線判斷邏輯如下:

5a53019c-8026-11ee-939d-92fbcf53809c.png

注意,預(yù)應(yīng)性規(guī)整是?種預(yù)操作,應(yīng)盡可能降低對(duì)系統(tǒng)性能影響,因此當(dāng)kswapd運(yùn)行時(shí),預(yù)應(yīng)性規(guī)整不會(huì)啟動(dòng)。

2.3.2.2 fragmentation_score_wmark

fragmentation_score_wmark 函數(shù)將會(huì)獲取當(dāng)前水線值,當(dāng)low入?yún)⒃O(shè)置為true時(shí),獲取的是低水線,否則獲取高水線。

5a6197d4-8026-11ee-939d-92fbcf53809c.png

預(yù)應(yīng)性水線的計(jì)算不復(fù)雜,compaction_proactiveness設(shè)置的越低,低水線值越高,高水線通常比低水線高10,但是高低水線都在100數(shù)值以內(nèi)。

2.3.2.3 fragmentation_score_node

fragmentation_score_node函數(shù)實(shí)現(xiàn)清晰,即針對(duì)每?個(gè)zone計(jì)算一個(gè)得分,并進(jìn)行累加,即為node的最后得分。

5a65726e-8026-11ee-939d-92fbcf53809c.png

由于每個(gè)zone大小不同,因此得分應(yīng)有對(duì)應(yīng)比重,fragmentation_score_zone_weighted函數(shù)完成這個(gè)計(jì)算,實(shí)現(xiàn)方式較為直接。

5a73b734-8026-11ee-939d-92fbcf53809c.png

zone碎片評(píng)分乘以zone的有效內(nèi)存頁數(shù)量,再除以整個(gè)node有效內(nèi)存數(shù)量,即為zone碎片程度得分的比重值。(zone有效頁數(shù)量 / node有效頁數(shù)量) * zone當(dāng)前得分簡單來說就是按照內(nèi)存大小重進(jìn)?計(jì)算。

fragmentation_score_zone函數(shù)用于計(jì)算每個(gè)zone碎片得分,其實(shí)現(xiàn)原理是統(tǒng)計(jì)當(dāng)前zone中?于等于COMPACTION_HPAGE_ORDER階空閑區(qū)域的個(gè)數(shù),并計(jì)算除此之外空閑內(nèi)存內(nèi)存與當(dāng)前zone空閑內(nèi)存百分比,這個(gè)占比可以說明碎片程度,此值越高,說明符合COMPACTION_HPAGE_ORDER階內(nèi)存塊越少。

5a77f0a6-8026-11ee-939d-92fbcf53809c.png

fill_contig_page_info函數(shù)用于獲取當(dāng)前zone空閑頁、多少個(gè)COMPACTION_HPAGE_ORDER階內(nèi)存塊等等,此函數(shù)將會(huì)遍歷當(dāng)前zone上所有order的空閑鏈表進(jìn)行累加計(jì)算,對(duì)于order等于COMPACTION_HPAGE_ORDER的階內(nèi)存塊個(gè)數(shù)累加,對(duì)于order大于COMPACTION_HPAGE_ORDER階內(nèi)存塊會(huì)拆分累加(因?yàn)榘鄠€(gè)COMPACTION_HPAGE_ORDER階空閑內(nèi)存區(qū)),此函數(shù)實(shí)現(xiàn)并不復(fù)雜,不展開描述。

2.3.3 預(yù)應(yīng)性規(guī)整特點(diǎn)

經(jīng)過上述的判斷,終于可以開始預(yù)應(yīng)性內(nèi)存規(guī)整,proactive_compact_node函數(shù)實(shí)現(xiàn)此功能,代碼如下:

5a80c960-8026-11ee-939d-92fbcf53809c.png

從compact_control結(jié)構(gòu)體的設(shè)置可以看出,預(yù)應(yīng)性規(guī)整與下文主動(dòng)規(guī)整類似,其指定參數(shù)有如下特點(diǎn):

(1)不指定目標(biāo)階,規(guī)整持續(xù)進(jìn)行,待掃描結(jié)束;

(2)遷移頁掃描器和空閑頁掃描器,不使用快速掃描能力;

(3)規(guī)整模式為MIGRATE_SYNC_LIGHT,輕度同步模式,會(huì)阻塞;

(4)規(guī)整范圍為完整zone地址空間;

(5)pageblock將會(huì)不會(huì)根據(jù)標(biāo)記選擇跳過;

上述compact_control結(jié)構(gòu)體參數(shù)含義見3.1節(jié);

2.4 主動(dòng)內(nèi)存規(guī)整

主動(dòng)內(nèi)存規(guī)整主要是指用戶通過設(shè)備節(jié)點(diǎn)觸發(fā)完整內(nèi)存規(guī)整或針對(duì)node內(nèi)存規(guī)整。內(nèi)核提供兩個(gè)設(shè)備節(jié)點(diǎn)用于觸發(fā)內(nèi)存規(guī)整。

5a8ca0aa-8026-11ee-939d-92fbcf53809c.png

通過compact_memory節(jié)點(diǎn)可以觸發(fā)當(dāng)前所有node以及下屬所有zone的內(nèi)存規(guī)整,這個(gè)操作的成本非常高。另一個(gè)compact節(jié)點(diǎn)只有在NUMA系統(tǒng)上存在,可以僅觸發(fā)某一個(gè)node進(jìn)行規(guī)整。無論如何主動(dòng)內(nèi)存規(guī)整觸發(fā)邏輯是簡單明了。

2.4.1 主動(dòng)內(nèi)存規(guī)整特點(diǎn)

5aa26840-8026-11ee-939d-92fbcf53809c.png

主動(dòng)內(nèi)存規(guī)整compact_control設(shè)置較為簡單,解釋如下:

(1)不指定目標(biāo)階,規(guī)整持續(xù)進(jìn)行,待掃描結(jié)束;

(2)遷移頁掃描器和空閑頁掃描器,不使用快速掃描能力;

(3)規(guī)整模式為MIGRATE_SYNC,同步模式,會(huì)阻塞;

(4)規(guī)整范圍為完整zone地址空間;

(5)pageblock將會(huì)不會(huì)根據(jù)標(biāo)記選擇跳過;

上述compact_control結(jié)構(gòu)體參數(shù)含義見3.1節(jié);

主動(dòng)內(nèi)存規(guī)整,是內(nèi)存規(guī)整最全面的方法,當(dāng)然其帶來的性能影響也會(huì)最大,默認(rèn)情況下內(nèi)核并不會(huì)觸發(fā)這類內(nèi)存規(guī)整。

3.內(nèi)存規(guī)整

前言中已經(jīng)簡述規(guī)整的大致思路,將一些頁遷移聚攏,騰出更多連續(xù)空間。那么在真正實(shí)現(xiàn)時(shí)實(shí)際需要解決的問題是內(nèi)存規(guī)整范圍是什么,如何找到需要遷移的頁,什么頁適合遷移,規(guī)整何時(shí)結(jié)束等問題。上述的問題最終都在compact_zone函數(shù)中被解決。

compact_zone函數(shù)針對(duì)單個(gè)zone內(nèi)存區(qū)進(jìn)行內(nèi)存規(guī)整,這是內(nèi)存規(guī)整的最小單元。其通過遷移頁掃描器從低地址到高地址尋找遷移頁,通過空閑頁掃描器從高地址到低地址尋找空閑頁,最終將掃描出的遷移頁遷移至掃描出的空閑頁,完成內(nèi)存規(guī)整,如下圖所示:

5aaa9d58-8026-11ee-939d-92fbcf53809c.png

更細(xì)節(jié)一些來說,內(nèi)存規(guī)整開始后,先通過遷移頁掃描器掃描,并且掃描的單位為一個(gè)pageblock,將當(dāng)前pageblock中可遷移的頁隔離后放入到待遷移的鏈表。隨后調(diào)用空閑頁掃描器掃描,空閑頁掃描器依然以pageblock為步長,但不再限制掃描一個(gè)pageblock,其掃描的目標(biāo)是找到大于等于當(dāng)前遷移頁數(shù)量的空閑頁,上述中綠色和黃色箭頭長度不同即想表達(dá)這個(gè)邏輯。上述掃描過程將會(huì)產(chǎn)生遷移頁和空閑頁,用于后續(xù)內(nèi)存遷移,這樣就完成了內(nèi)存規(guī)整的一輪操作,可能與大家理解不同,內(nèi)存規(guī)整并非一次性掃描zone然后再遷移,而是以這種一步一步的方式進(jìn)行遷移,這能平攤內(nèi)存規(guī)整對(duì)性能帶來的風(fēng)險(xiǎn),并且每輪處理后都有機(jī)會(huì)判斷當(dāng)前內(nèi)存規(guī)整是否可以退出。上文描述已經(jīng)非常清晰勾畫了compact_zone函數(shù)的核心邏輯,但是實(shí)際上實(shí)現(xiàn)可能更加復(fù)雜,比如什么條件下規(guī)整可以提前結(jié)束、遷移頁掃描器和空閑頁掃描器是否可以加速等等,這些都屬于更高層面優(yōu)化的話題,下文將會(huì)簡述。

再次回到compact_zone函數(shù),將復(fù)雜的代碼剝離可以很容易得到如下核心代碼邏輯:

5aae5ec0-8026-11ee-939d-92fbcf53809c.png

(1)compact_finished函數(shù)用于判斷當(dāng)前規(guī)整是否結(jié)束

(2)isolate_migratepages是遷移頁掃描器實(shí)現(xiàn),用于查找需要移動(dòng)的頁;

(3)isolate_freepages是空閑頁掃描器實(shí)現(xiàn),用于查找用于頁遷移的空閑頁;

(4)migrate_pages是頁遷移函數(shù),將上述兩個(gè)掃描器掃描結(jié)果進(jìn)行頁遷移處理,完成規(guī)整;

至此,compact_zone大體邏輯已經(jīng)完成說明,下文將會(huì)對(duì)(1)~(3)函數(shù)進(jìn)行細(xì)致描述。

3.1 內(nèi)存規(guī)整參數(shù)說明

compact_control控制了compact_zone的諸多行為,不同場景觸發(fā)內(nèi)存規(guī)整訴求不同,因此參數(shù)也不同,這里初步羅列常用參數(shù)含義,有助于理解不同場景下內(nèi)存規(guī)整的差異。

5abdb5be-8026-11ee-939d-92fbcf53809c.png5ac13f2c-8026-11ee-939d-92fbcf53809c.png

3.2 遷移頁掃描器(migrate scanner)

isolate_migratepages函數(shù)實(shí)際就是遷移頁掃描器的代碼實(shí)現(xiàn),其通常會(huì)從低地址到高地址完整或部分掃描zone區(qū)域,以pageblock為步長選擇一個(gè)合適pageblock調(diào)用isolate_migratepages_block函數(shù)進(jìn)行內(nèi)存隔離,需要注意的是isolate_migratepages函數(shù)在處理完一個(gè)pageblock后就會(huì)退出,換句話說此函數(shù)一次調(diào)用只處理一個(gè)合適的pageblock。核心代碼如下所示:

5ac88ca0-8026-11ee-939d-92fbcf53809c.png

總結(jié)一下,isolate_migratepages函數(shù)主要通過如下三個(gè)函數(shù)調(diào)用完成整個(gè)isolate工作:

5adaa426-8026-11ee-939d-92fbcf53809c.png

A)快速尋找合適pageblock或返回而遷移掃描起始位置(fast_find_migrateblock)

B)判斷pageblock是否合適進(jìn)行隔離(suitable_migration_source)

C)實(shí)際對(duì)一個(gè)pageblock進(jìn)行隔離頁搜索操作(isolate_migratepages_block)

FAQ:注意若未找到合適pageblock,那么會(huì)持續(xù)進(jìn)行線性遍歷查找,直到地址超過cc->free_pfn(最開始時(shí)此值應(yīng)為zone地址區(qū)域結(jié)尾處)。

下面將會(huì)對(duì)上述三個(gè)函數(shù)進(jìn)行解析,完整描述isolate_migratepages函數(shù)功能細(xì)節(jié)。

isolate_migratepages函數(shù)會(huì)返回三個(gè)返回值,內(nèi)容如下:

5ae3f4b8-8026-11ee-939d-92fbcf53809c.png

這些返回值將會(huì)影響compact_zone函數(shù)的返回值。

3.2.1 遷移頁掃描器快速掃描

(fast_find_migrateblock)

fast_find_migrateblock函數(shù)會(huì)嘗試快速尋找一個(gè)pageblock來進(jìn)行規(guī)整,如果無法找到則返回cc->migrate_pfn(注:此值初始應(yīng)為zone的起始地址,后續(xù)應(yīng)記錄上一次遷移掃描器掃描結(jié)束位置避免后續(xù)重復(fù)掃描)作為起始地址開始遍歷,通??紤]不就是從zone內(nèi)存區(qū)域的起始地址尋找一個(gè)么,但這樣可能并不高效,在之前版本中,遷移頁掃描器每次循環(huán)確實(shí)是基于zone的某個(gè)地址開始進(jìn)行線性遍歷,這是一種線性搜索的過程,但是隨后引入了如下補(bǔ)丁,改變了這一現(xiàn)狀:

https://patchwork.kernel.org/project/linux-mm/patch/20181214230531.GC29005@techsingularity.net/

其目的是盡量選擇一個(gè)充斥著可移動(dòng)空閑頁pageblock塊,這樣通過較少頁遷移,就可以滿足高階order內(nèi)存申請(qǐng)需求。通過查找freelist空閑內(nèi)存塊反向查找對(duì)應(yīng)pageblock,這樣效率非常高,另一方面,由于pageblock選擇不是簡單的順序查找,為了避免后續(xù)掃描重復(fù)pageblock還需要將其進(jìn)行單獨(dú)標(biāo)識(shí),通過set_pageblock_skip函數(shù)完成設(shè)置,確保再次進(jìn)行掃描時(shí)會(huì)跳過這個(gè)pageblock內(nèi)存塊。以上就是主要思路,但是具體在實(shí)現(xiàn)上有很多細(xì)節(jié)比如:

(a)如果規(guī)整目標(biāo)order太小,那么完全沒必要去尋找,依然使用cc->migrate_pfn作為起始地址;

(b)由于對(duì)于cc->order有要求,因此僅適用于直接內(nèi)存回收和異步內(nèi)存回收(僅這兩種場景會(huì)指定cc->order,其它場景cc->order為-1);

(c)尋找空閑頁所在pageblock必須是在內(nèi)存搜索范圍的前1/2或1/8,這部分是最有可能被遷移頁掃描到得區(qū)域,避免影響到空閑頁掃描;

(d)空閑頁掃描會(huì)改變free_list布局,盡量保證下次掃描free_list不重復(fù)掃描空閑內(nèi)存塊;

(e)若這只cc->ignore_skip_hint,則遷移頁掃描器不采用遷移頁的fast機(jī)制;

(f)僅搜索可移動(dòng)空閑頁,并且搜索范圍從order - 1階開始;

fast_find_migrateblock函數(shù)若無法找到合適pageblock,那么將會(huì)返回cc->migrate_pages退化為正常線性掃描,請(qǐng)注意的是fast_find_migrateblock函數(shù)每次也只找到一個(gè)pageblock并設(shè)置其為skip,通過上述方式確實(shí)在一定程度上能夠加速針對(duì)目標(biāo)階內(nèi)存規(guī)整,能夠更快整理出目標(biāo)階內(nèi)存需求,但是對(duì)于完整內(nèi)存規(guī)整并無實(shí)質(zhì)效果,因此fast加速查找僅適用于直接內(nèi)存規(guī)整和kcompactd被動(dòng)內(nèi)存規(guī)整,因?yàn)檫@些場景下通常會(huì)指定規(guī)整目標(biāo)階。核心代碼如下:

5ae76f1c-8026-11ee-939d-92fbcf53809c.png

無論如何,通過fast_find_migrateblock函數(shù)我們可以找到一個(gè)待遷移pageblock或者返回一個(gè)遷移頁掃描起始地址(cc->migrate_pfn),用于后續(xù)針對(duì)pageblock內(nèi)存遷移頁掃描使用。

3.2.2 suitable_migration_source函數(shù)解析

suitable_migration_source函數(shù)用于判斷當(dāng)前pageblock是否可以進(jìn)行隔離及遷移,這部分邏輯相對(duì)簡單。

5afb737c-8026-11ee-939d-92fbcf53809c.png

這里需要關(guān)注的是,如果非直接內(nèi)存規(guī)整或非遷移類型非ASYNC模式,則不需要判斷pageblock遷移類型與compact_control遷移類型是否匹配,盡可能進(jìn)行內(nèi)存規(guī)整。

3.2.3 pageblock內(nèi)存隔離(isolate_migratepages_block)

isolate_migratepages_block函數(shù)會(huì)在單個(gè)pageblock內(nèi)進(jìn)行遍歷,嘗試將符合規(guī)整要求的頁放入對(duì)應(yīng)compact_control所指向的migratepages鏈表,進(jìn)行隔離,用于后續(xù)頁遷移操作。函數(shù)整體結(jié)構(gòu)大致如下:

5afecf2c-8026-11ee-939d-92fbcf53809c.png

起始會(huì)通過too_many_isolated函數(shù)檢查當(dāng)前內(nèi)存節(jié)點(diǎn)上是否存在過多isolated頁,如果數(shù)量過大(isolated > (inactive + active) / 2)那么根據(jù)MIGRATE模式選擇處理方法,比如對(duì)于異步模式就是直接退出,對(duì)于同步模式函數(shù)將會(huì)在這里進(jìn)行等待一段時(shí)間,再循環(huán)檢查是否合適繼續(xù)向下執(zhí)行。隨后就是通過for循環(huán)開始遍歷這個(gè)pageblock里面所有頁,并對(duì)每一個(gè)頁進(jìn)行判斷,決定其處理方法,這是一個(gè)復(fù)雜的過程,下文拆分代碼進(jìn)行說明。

3.2.3.1 大頁處理

對(duì)于大頁處理,一般情況下內(nèi)存規(guī)整是會(huì)選擇略過,不進(jìn)行整理。處理代碼如下:

5b0b092c-8026-11ee-939d-92fbcf53809c.png

從代碼看當(dāng)頁為復(fù)合頁并且cc->alloc_contig為false時(shí),此頁將不會(huì)被規(guī)整。無論是hugetlbfs和THP大頁都屬于復(fù)合頁,那么問題的關(guān)鍵來到cc->alloc_contig是什么?

從代碼進(jìn)一步推進(jìn)可以看到,通過alloc_contig_range函數(shù)進(jìn)行內(nèi)存分配時(shí),此函數(shù)會(huì)指定申請(qǐng)內(nèi)存地址范圍并盡力實(shí)現(xiàn),如果指定范圍已經(jīng)被占用,會(huì)嘗試觸發(fā)直接規(guī)整進(jìn)行頁遷移,如下代碼所示:

5b1a743e-8026-11ee-939d-92fbcf53809c.png

可以看到,這種情況下會(huì)將alloc_contig參數(shù)設(shè)置為true,在此邏輯中當(dāng)調(diào)用到isolate_migratepages_block函數(shù)是會(huì)嘗試規(guī)整大頁,其實(shí)際的做法是通過isolate_or_dissolve_huge_page函數(shù)實(shí)現(xiàn)大頁溶解,這部分涉及大頁邏輯不再發(fā)散。

總結(jié),只有當(dāng)頁是hugetlbfs大頁并且通過alloc_contig_range函數(shù)調(diào)用下來觸發(fā)內(nèi)存規(guī)整時(shí)才會(huì)進(jìn)行處理,其它場景下大頁處理策略都是略過。

3.2.3.2 空閑物理頁處理

空閑頁的處理為直接跳過,這無可厚非,唯一需要注意的時(shí),空閑頁跳過時(shí)并非單頁跳過,而是根據(jù)頁的order階進(jìn)行跳過,代碼如下:

5b2530c2-8026-11ee-939d-92fbcf53809c.png

3.2.3.3 non-LRU物理頁

5b28b10c-8026-11ee-939d-92fbcf53809c.png

這個(gè)很有意思,上文談到大部分可移動(dòng)頁應(yīng)該都是用戶態(tài)的匿名頁,這里怎么還會(huì)有不再LRU上的物理頁呢,實(shí)際這涉及到頁遷移特性的一種功能,有興趣的朋友可以閱讀一下"Documentation/vm/page_migration.rst"文章中"Non-LRU page migration"這一小節(jié)。內(nèi)核中申請(qǐng)的內(nèi)存通常都是non-LRU上并且不可移動(dòng),但是內(nèi)核提供了定制能力,開發(fā)者可以在內(nèi)核驅(qū)動(dòng)中將自己申請(qǐng)的內(nèi)存標(biāo)記為可移動(dòng),為此內(nèi)核為page添加了兩個(gè)新的flag即PG_movable和PG_isolated用于標(biāo)識(shí)這種non-LRU并且可遷移的頁。開發(fā)者通常使用__SetPageMovable接口主動(dòng)設(shè)置這些內(nèi)存頁P(yáng)G_movable標(biāo)記,而PG_isolated標(biāo)識(shí)此頁已經(jīng)被隔離,開發(fā)者不需要主動(dòng)設(shè)置此標(biāo)記。

現(xiàn)在我們應(yīng)該可以理解上述代碼中對(duì)于__PageMovable(page)判斷,如果一個(gè)non-LRU頁被設(shè)置了PG_movable并且PG_isolated還未被設(shè)置,那么代表這個(gè)頁也是可以進(jìn)行遷移,隨后將會(huì)調(diào)用isolate_movable_page函數(shù)進(jìn)行隔離操作。

問題還沒有結(jié)束,實(shí)際想要讓這些在內(nèi)核中直接申請(qǐng)的頁變?yōu)榭蛇w移,光設(shè)置標(biāo)記還不行,開發(fā)人員需要自定義這些頁如何隔離以及如何遷移,因此內(nèi)核要求,開發(fā)者需要在address_space_operations結(jié)構(gòu)體里面實(shí)現(xiàn)isolate_page、migratepage及putback_page函數(shù)?,F(xiàn)在回到isolate_movable_page函數(shù),此函數(shù)將會(huì)調(diào)用開發(fā)人員注冊(cè)的isolate_page函數(shù)完成這些頁隔離操作,代碼如下:

5b3224f8-8026-11ee-939d-92fbcf53809c.png

3.2.3.4 pinned匿名頁

如果匿名頁已經(jīng)被mlock等接口pin住,那么將會(huì)略過。

5b35f984-8026-11ee-939d-92fbcf53809c.png

一方面,通過page_mapping判斷當(dāng)前頁是否為文件頁;另一方面,通過page_count(page) > page_mapcount(page)判斷是否被pin住,匿名頁被pin住時(shí)會(huì)增加_refcount數(shù)值。

3.2.3.5 GFP_NOFS配置下僅處理匿名頁

5b40c12a-8026-11ee-939d-92fbcf53809c.png

__GFP_FS表示內(nèi)存分配過程中可以觸發(fā)文件操作,如果compact_control中g(shù)fp_mask不帶__GFP_FS則結(jié)果依賴page_mapping返回值,對(duì)于匿名頁而言page_mapping返回NULL,因此上述代碼判斷實(shí)際的含義是當(dāng)分配上下文為無__GFP_FS并且是文件頁時(shí)將會(huì)略過,另一方面也可以解釋為GFP_NOFS時(shí)僅處理匿名頁。

那么什么時(shí)候compact_control中g(shù)fp_mask不帶__GFP_FS,前文說明了觸發(fā)內(nèi)存回收的場景,在內(nèi)存分配失敗時(shí)有可能導(dǎo)致直接觸發(fā)內(nèi)存規(guī)整,此時(shí)內(nèi)存分配GFP標(biāo)記將會(huì)被賦值到compact_control中用于配置內(nèi)存規(guī)整行為。

5b5749fe-8026-11ee-939d-92fbcf53809c.png

可以想象這么做的原因,實(shí)際在內(nèi)存分配的過程中直接觸發(fā)內(nèi)存規(guī)整其系統(tǒng)并不希望耗費(fèi)過多時(shí)間,做有限度規(guī)整更為合適。

其它觸發(fā)內(nèi)存規(guī)整的場景,通常compact_control中g(shù)fp_mask為GFP_KERNEL,這是包含__GFP_FS,因此規(guī)整涉及的內(nèi)存范圍通常更廣。

3.2.3.6 不同isolate模式會(huì)影響頁的處理策略

5b608532-8026-11ee-939d-92fbcf53809c.png

__isolate_lru_page_prepare完成此任務(wù),關(guān)鍵代碼如下:

5b644064-8026-11ee-939d-92fbcf53809c.png

此處邏輯是根據(jù)隔離類型篩選可遷移的物理頁,隔離類型來源于cc-mode也就是遷移類型,代碼如下:

5b706a6a-8026-11ee-939d-92fbcf53809c.png

通常,僅當(dāng)MIGRATE_ASYNC和MIGRATE_SYNC_LIGHT模式時(shí),其隔離模式為ISOLATE_ASYNC_MIGRATE異步模式,在這種模式下其會(huì)盡可能避免隔離可能會(huì)阻塞頁,比如代碼中正在回寫的頁或者是臟頁,這里注意如果頁為臟頁,但是其并非文件頁(swap匿名頁)或擁有自己migratepage函數(shù)那么頁被認(rèn)為遷移過程不會(huì)被阻塞,否則都無法隔離。如果isolate_mode為ISOLATE_UNEVICTABLE,代表本次隔離可以處理不可回收頁,這個(gè)主要是針對(duì)那些被lock住的unevictable頁,這些頁不能夠被回收但是支持遷移。

3.2.3.7 修改LRU即真正意義isolate(隔離)

5b73e67c-8026-11ee-939d-92fbcf53809c.png

在完成(1)~(6)的判斷后,剩下頁將能夠被隔離,隔離的含義就是將其從LRU鏈表去除(這個(gè)LRU有可能是來自于pglist_data也可能來自于頁對(duì)應(yīng)memcg),隨后將這些頁添加至cc->migratepages,用于后續(xù)頁遷移。

3.2.3.8 總結(jié)

isolate_migratepages_block函數(shù)是內(nèi)存規(guī)整過程中頁隔離的重要函數(shù),其確定哪些頁應(yīng)該被隔離,哪些頁應(yīng)該被略過,其基本策略如下:

(1)大頁不應(yīng)被隔離,但是alloc_contig_range場景下有可能觸發(fā)hugetlbfs大頁溶解,但這已不屬于內(nèi)存規(guī)整場景;

(2)空閑物理頁,不會(huì)被隔離;

(3)non-LRU物理頁,作為在內(nèi)核分配內(nèi)存,如果開發(fā)者為其實(shí)現(xiàn)isolate_page、migratepage及putback_page函數(shù),則可以被隔離或遷移;

(4)被Pin住的匿名頁,不會(huì)被隔離;

(5)GFP_NOFS分配上下文僅隔離匿名頁;

(6)不同isolate模式會(huì)影響頁的處理策略,比如ISOLATE_ASYNC_MIGRATE不會(huì)隔離正在回寫的頁或臟頁;

3.3 空閑頁掃描器(free scanner)

isolate_freepages函數(shù)是空閑頁掃描器的核心邏輯,但是compact_zone中對(duì)于空閑頁掃描器調(diào)用并不直接,而是通過migrate_pages間接調(diào)用。

5b7914ee-8026-11ee-939d-92fbcf53809c.png

migrate_pages是內(nèi)存遷移的基礎(chǔ)接口,其核心功能是將from鏈表中的頁遷移至空閑頁,空閑頁如何獲取則在get_new_page中實(shí)現(xiàn),這是一個(gè)函數(shù)指針,在compact_zone函數(shù)中,此接口的實(shí)際調(diào)用形態(tài)如下:

5b925fee-8026-11ee-939d-92fbcf53809c.png

cc->migratepages就是之前通過isolate_migratepages隔離出來頁,compaction_alloc則實(shí)現(xiàn)如何獲取空閑頁,可以想象isolate_freepages函數(shù)會(huì)在此調(diào)用,也是本節(jié)分析的重點(diǎn)。compaction_alloc函數(shù)并不復(fù)雜,其用于為內(nèi)存規(guī)整頁遷移時(shí)申請(qǐng)遷移的目的內(nèi)存,代碼如下:

5b9b9c6c-8026-11ee-939d-92fbcf53809c.png

cc->migratepages保存了需要進(jìn)行規(guī)整遷移的頁,也就是遷移掃描器掃描的結(jié)果。

cc->freepages保存了頁遷移的目的空閑頁,也就是空閑頁掃描器掃描的結(jié)果。

當(dāng)cc->freepages為空時(shí),嘗試調(diào)用空閑頁掃描器isolate_freepages函數(shù)嘗試掃描隔離更多空閑頁用于頁遷移,為什么要隔離呢?隔離的本質(zhì)是將其從伙伴分配系統(tǒng)中取出不再參與系統(tǒng)內(nèi)存分配,僅用于內(nèi)存規(guī)整遷移使用。

isolate_freepages與上文isolate_migratepages函數(shù)相對(duì)應(yīng),用于隔離空閑頁,用于頁遷移。此函數(shù)也是free scanner(空閑頁掃描器的核心邏輯)。其實(shí)現(xiàn)邏輯也與isolate_migratepages函數(shù)相似,核心邏輯大致如下:

5b9f6ed2-8026-11ee-939d-92fbcf53809c.png

isolate_freepages函數(shù),會(huì)根據(jù)cc->free_pfn開始反向以pageblock為單位進(jìn)行遍歷,如果遇到合適pageblock,則會(huì)進(jìn)一步對(duì)pageblock中的頁進(jìn)行遍歷,將其中合適的空閑頁進(jìn)行隔離,放入cc->freepages鏈表中用于后續(xù)頁遷移使用,當(dāng)收集的空閑頁足夠遷移時(shí)將會(huì)退出。上圖僅描述核心邏輯與實(shí)際實(shí)現(xiàn)有一些出入,比如fast_isolate_freepages機(jī)制引入,就會(huì)導(dǎo)致上述反向線性遍歷的過程改變,但是上圖已經(jīng)比較簡要說明了空閑頁掃描器的工作原理

5bb0b20a-8026-11ee-939d-92fbcf53809c.png

上文從函數(shù)調(diào)用角度描述isolate_freepages函數(shù)邏輯,下文對(duì)部分重要函數(shù)調(diào)用做詳細(xì)分析。

3.3.1 空閑頁掃描器快速掃描(fast_isolate_freepages)

常規(guī)情況下,系統(tǒng)通過從cc->free_pfn開始反向遍歷尋找一個(gè)合適pageblock,隨后針對(duì)這個(gè)pageblock隔離其空閑頁。但是這有可能低效,比如第一個(gè)pageblock里面并沒有多少空閑頁,那么針對(duì)這個(gè)pageblock進(jìn)行大部分操作都是無效,fast_isolate_freepages就是為改善這個(gè)問題,其并不從cc->free_pfn開始進(jìn)行線性查找,而是借助伙伴系統(tǒng)中free_list快速找到一塊合適空閑區(qū)域進(jìn)行隔離,從某種角度看這已經(jīng)不是基于pageblock的處理了。這與fast_find_migrateblock函數(shù)目標(biāo)類似均為提升掃描器效率。詳細(xì)代碼功能描述如下:

(1)首先選取合適order即cc->search_order,通常開始此值為cc->order - 1;

FAQ:此功能也是應(yīng)用在直接內(nèi)存回收和kcompactd場景下,因?yàn)槠淇焖偎阉鞯那疤崾莄c->order和cc->search_order。其它場景下觸發(fā)的內(nèi)存規(guī)整,cc->order為-1,其直接返回cc->free_pfn,也就蛻變?yōu)榫€性搜索的模式;

(2)在order的free_list中進(jìn)行空閑頁的遍歷查找;

5bb76dde-8026-11ee-939d-92fbcf53809c.png

(3)查找到合適空閑頁后,如果空閑頁落入圖中下圖中綠色區(qū)域,那么此空閑區(qū)域就會(huì)被優(yōu)先隔離,這里注意并非以pageblock為單位進(jìn)行了(min_pfn為1/2處,low_pfn為3/4處);

5bc32b24-8026-11ee-939d-92fbcf53809c.png

這個(gè)邏輯并不復(fù)雜,內(nèi)存規(guī)整期望內(nèi)存向后方遷移,如果空閑頁太靠前,極端點(diǎn),如果空閑頁落入紅色區(qū)域,內(nèi)存規(guī)整掃描器容易快速相遇導(dǎo)致無法解決內(nèi)存碎片的問題;

那么,如果空閑頁落入min_pfn和low_pfn之間,那么系統(tǒng)會(huì)降低在當(dāng)前階freelist遍歷機(jī)會(huì),傾向于降階在新search_order階的freelist中尋找綠色區(qū)域的空閑頁;除此以外這種場景下,系統(tǒng)還會(huì)記錄當(dāng)前search_order對(duì)應(yīng)freelist搜索記錄(這會(huì)改變freelist內(nèi)存塊順序),后續(xù)可避免額外搜索,代碼如下:

5bd7d22c-8026-11ee-939d-92fbcf53809c.png

(4)對(duì)于(3)已經(jīng)找到search_order次的空閑區(qū)域,將直接調(diào)用__isolate_free_page(接口分析詳見3.3.2.1節(jié))函數(shù)完成隔離,隨后將這些頁放入cc->freepages鏈表,完成整個(gè)操作;

(5)到這里,已經(jīng)成功隔離了search_order階空閑頁,這并不針對(duì)pageblock,并且對(duì)于是否已經(jīng)滿足遷移需求數(shù)量也并沒有約束,所以在函數(shù)末尾調(diào)用了fast_isolate_around函數(shù),此函數(shù)本質(zhì)是根據(jù)當(dāng)前需求,確認(rèn)是否需要針對(duì)當(dāng)前空閑區(qū)域所在pageblock再額外進(jìn)行隔離,代碼如下:

5beabbda-8026-11ee-939d-92fbcf53809c.png

簡單總結(jié),代碼邏輯如下:

5bee5ede-8026-11ee-939d-92fbcf53809c.png

如果當(dāng)前已隔離的綠色區(qū)域已經(jīng)滿足訴求,那么此函數(shù)將會(huì)直接退出,如果不滿足,將會(huì)嘗試將這個(gè)空閑區(qū)域?qū)?yīng)pageblock左側(cè)和右側(cè)區(qū)域通過isolate_freepages_block函數(shù)進(jìn)行空閑頁隔離。

無論如何,fast_isolate_freepages接口都嘗試以更快速的方式獲取空閑頁,有可能這個(gè)空閑頁并不是緊貼著cc->free_pfn,但是它一定在后1/4范圍內(nèi),并且它會(huì)改變freelist的結(jié)構(gòu)避免重復(fù)判斷相同空閑頁,這是一個(gè)優(yōu)化功能。對(duì)于內(nèi)存規(guī)整若想簡單理解,可以忽略此處細(xì)節(jié)直接理解為從zone末尾開始線性查找。

3.3.2 空閑頁隔離(isolate_freepages_block)

isolate_freepages_block函數(shù),即在指定內(nèi)存范圍內(nèi)正向遍歷,將合適的空閑頁進(jìn)行隔離加入到cc->freepages鏈表,用于后續(xù)頁遷移,這是isolate_freepages函數(shù)得核心函數(shù)調(diào)用,通常isolate_freepages每次調(diào)用會(huì)傳遞一個(gè)pageblock范圍進(jìn)行空閑頁隔離。關(guān)鍵代碼如下:

5bf7b7b8-8026-11ee-939d-92fbcf53809c.png

函數(shù)關(guān)鍵邏輯說明:

(1)函數(shù)在pageblock內(nèi)遍歷并不一定按照頁為單位,參數(shù)stride作為步長存在;

(2)復(fù)合頁將會(huì)略過;

(3)非空閑頁將會(huì)略過;

(4)符合上述要求空閑頁通過__isolate_free_page進(jìn)行隔離操作,隨后將這些加入到cc->freepages鏈表;

(5)如果當(dāng)前收集空閑頁已經(jīng)大于當(dāng)前已經(jīng)收集遷移頁則退出循環(huán);

上述即isolate_freepages_block函數(shù)的邏輯,這里面需要關(guān)注一個(gè)strict參數(shù),如果該參數(shù)為true,那么isolate_freepages_block函數(shù)將會(huì)以頁為遍歷單位進(jìn)行遍歷及隔離,并且不會(huì)再根據(jù)上述(5)條件提前退出,而是完整隔離整個(gè)pageblock中合適的空閑頁。

3.3.2.1 伙伴系統(tǒng)處理(__isolate_free_page)

空閑頁隔離與遷移頁的隔離不太相同,由于空閑頁還屬于伙伴系統(tǒng)管轄范圍內(nèi),伙伴體統(tǒng)提供專用隔離接口,即__isolate_free_page函數(shù)。

5bfb5af8-8026-11ee-939d-92fbcf53809c.png

此結(jié)構(gòu)邏輯清晰,不過多贅述。

3.4 內(nèi)存規(guī)整退出判斷(compact_finished)

compact_finished用于判斷當(dāng)前規(guī)整是否結(jié)束,有多種不同條件導(dǎo)致規(guī)整結(jié)束,并且返回值不同,由于compact_finished函數(shù)較長并且對(duì)于理解內(nèi)存規(guī)整較為重要,因此代碼拆分說明。

3.4.1 掃描器相遇

上文說明migrate scanner從正向掃描,free scanner反向掃描,當(dāng)兩者相遇,代表掃描和遷移操作結(jié)束,因此規(guī)整結(jié)束,這是最為正常的一種退出方式,代碼如下:

5c02fae2-8026-11ee-939d-92fbcf53809c.png

掃描器相遇場景退出,上述代碼注釋完整標(biāo)記其邏輯,對(duì)于如何判斷掃描器相遇,實(shí)際根據(jù)cc->free_pfn和cc->migrate_pfn的大小容易判斷,不進(jìn)行函數(shù)代碼說明。

3.4.2 預(yù)應(yīng)性規(guī)整退出條件

這里預(yù)應(yīng)性規(guī)整除了掃描器相遇退出條件外,擁有額外退出條件。

5c1230e8-8026-11ee-939d-92fbcf53809c.png

fragmentation_score_zone和fragmentation_score_wmark均為預(yù)應(yīng)性規(guī)整碎片評(píng)估函數(shù),簡單說當(dāng)前如果對(duì)于大頁階碎片評(píng)估分?jǐn)?shù)低于預(yù)應(yīng)性碎片水線時(shí),則停止規(guī)整,返回成功。關(guān)于預(yù)應(yīng)性規(guī)整碎片評(píng)估邏輯詳見2.3.2節(jié)。

3.4.3 direct規(guī)整模式額外退出條件

直接內(nèi)存規(guī)整在識(shí)別是否成功時(shí),如果判斷當(dāng)前申請(qǐng)需求已滿足,并且分配遷移類型也滿足一定要求即可退出直接規(guī)整邏輯。為何在申請(qǐng)可以滿足的情況下還要滿足一定要求才能退出呢,主要考慮是即便滿足分配,但也不能引入潛在擴(kuò)大內(nèi)存碎片化的情況,否則將會(huì)頻繁進(jìn)入直接內(nèi)存規(guī)整。

5c204ade-8026-11ee-939d-92fbcf53809c.png

3.4.4 返回值總結(jié)

1.COMPACT_CONTINUE:代表內(nèi)存規(guī)整未結(jié)束,繼續(xù)規(guī)整;

2.COMPACT_COMPLETE:在cc->whole_zone為true場景下,完成全區(qū)域掃描和規(guī)整,將返回此值;

3.COMPACT_PARTIAL_SKIPPED:多種場景下均會(huì)返回此值,例如:

(1)cc->whole_zone為false場景下,掃描和規(guī)整完成,將會(huì)范圍此值;

(2)在proactive_compaction模式下,如果此時(shí)kswapd運(yùn)行,規(guī)整也將會(huì)停止,返回此值;

4.COMPACT_SUCCESS:代表規(guī)整成功,此值也是多種場景下均會(huì)返回:

(1)proactive規(guī)整模式下,碎片化得分達(dá)標(biāo),主動(dòng)退出規(guī)整,即返回成功;

(2)direct規(guī)整模式下,需求order階及遷移類型鏈表上,已有足量內(nèi)存,即返回成功;

(3)direct規(guī)整模式下,需求order階上如果有足量CMA內(nèi)存,前提是本身需求也是可遷移頁(否則CMA內(nèi)存申請(qǐng)時(shí)無法遷移),即返回成功;

(4)direct規(guī)整模式下,可以從其它遷移類型偷到內(nèi)存,在滿足一定條件下也會(huì)范圍成功;

5.COMPACT_CONTENDED:若當(dāng)前進(jìn)程被強(qiáng)制退出或依然持有zone lock,則規(guī)整邏輯返回此值,屬于一種異常退出狀態(tài);

4.內(nèi)存規(guī)整總結(jié)

代碼分析基本完成,再對(duì)內(nèi)存規(guī)整統(tǒng)計(jì)信息及可調(diào)文件節(jié)點(diǎn)進(jìn)行簡要說明。

4.1 內(nèi)存規(guī)整統(tǒng)計(jì)信息

在上文的代碼描述中忽略內(nèi)存規(guī)整相關(guān)信息統(tǒng)計(jì)邏輯,統(tǒng)計(jì)信息可以通過/proc/vmstat文件節(jié)點(diǎn)進(jìn)查詢,相關(guān)信息含義說明如下:

5c23d0dc-8026-11ee-939d-92fbcf53809c.png

4.2 內(nèi)存規(guī)整文件節(jié)點(diǎn)

5c2f2112-8026-11ee-939d-92fbcf53809c.png

4.3 總結(jié)

內(nèi)存規(guī)整是一個(gè)較重內(nèi)存碎片優(yōu)化措施,在使用時(shí)內(nèi)核較為謹(jǐn)慎,當(dāng)前有直接內(nèi)存規(guī)整、kcompactd內(nèi)存規(guī)整、預(yù)應(yīng)性內(nèi)存規(guī)整及主動(dòng)內(nèi)存規(guī)整四種場景,這些場景涵蓋在內(nèi)存分配、內(nèi)存回收等上下文,由于規(guī)整的訴求和緊迫程度不同,其通過compact_control結(jié)構(gòu)體參數(shù)控制compact_zone內(nèi)存規(guī)整行為包括但不限于內(nèi)存掃描范圍、頁遷移的能力、遷移頁是否適合規(guī)整及是否可以阻塞等等。

另一方面,內(nèi)存規(guī)整的核心邏輯在于遷移頁掃描器(migrate scanner)和空閑頁掃描器(free scanner)運(yùn)作原理,包括哪些頁可以作為遷移頁或空閑頁,何時(shí)內(nèi)存規(guī)整結(jié)束等等這些直接影響對(duì)內(nèi)存規(guī)整理解。

審核編輯:彭菁

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    9005

    瀏覽量

    153769
  • 數(shù)據(jù)
    +關(guān)注

    關(guān)注

    8

    文章

    7256

    瀏覽量

    91892
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11511

    瀏覽量

    213838
  • 內(nèi)存
    +關(guān)注

    關(guān)注

    8

    文章

    3125

    瀏覽量

    75274
  • 分配器
    +關(guān)注

    關(guān)注

    0

    文章

    204

    瀏覽量

    26269

原文標(biāo)題:超詳細(xì)!Linux內(nèi)核內(nèi)存規(guī)整詳解

文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    Linux內(nèi)存管理是什么,Linux內(nèi)存管理詳解

    Linux內(nèi)存管理 Linux內(nèi)存管理是一個(gè)非常復(fù)雜的過程,主要分成兩個(gè)大的部分:內(nèi)核內(nèi)存
    的頭像 發(fā)表于 05-11 17:54 ?6536次閱讀
    <b class='flag-5'>Linux</b>的<b class='flag-5'>內(nèi)存</b>管理是什么,<b class='flag-5'>Linux</b>的<b class='flag-5'>內(nèi)存</b>管理詳解

    走進(jìn)Linux內(nèi)存系統(tǒng)探尋內(nèi)存管理的機(jī)制和奧秘

    Linux 內(nèi)存是后臺(tái)開發(fā)人員,需要深入了解的計(jì)算機(jī)資源。合理的使用內(nèi)存,有助于提升機(jī)器的性能和穩(wěn)定性。本文主要介紹Linux 內(nèi)存組織結(jié)構(gòu)
    的頭像 發(fā)表于 01-05 09:47 ?1960次閱讀

    Linux內(nèi)核內(nèi)存泄漏怎么辦

    Linux內(nèi)核開發(fā)中,Kmemleak是一種用于檢測(cè)內(nèi)核內(nèi)存泄漏的工具。
    發(fā)表于 07-04 11:04 ?1004次閱讀

    Linux內(nèi)核的作用

    Linux操作系統(tǒng)是當(dāng)今世界上最為廣泛使用的開源操作系統(tǒng)之一,內(nèi)核則是一個(gè)操作系統(tǒng)的核心和靈魂所在。對(duì)于一名Linux驅(qū)動(dòng)開發(fā)者來說,了解Linux
    發(fā)表于 07-06 11:46 ?2038次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>的作用

    Linux內(nèi)核內(nèi)存管理詳解

    內(nèi)存管理的主要工作就是對(duì)物理內(nèi)存進(jìn)行組織,然后對(duì)物理內(nèi)存的分配和回收。但是Linux引入了虛擬地址的概念。
    發(fā)表于 08-31 14:46 ?1029次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>的<b class='flag-5'>內(nèi)存</b>管理詳解

    Linux內(nèi)核內(nèi)存管理架構(gòu)解析

    內(nèi)存管理子系統(tǒng)可能是linux內(nèi)核中最為復(fù)雜的一個(gè)子系統(tǒng),其支持的功能需求眾多,如頁面映射、頁面分配、頁面回收、頁面交換、冷熱頁面、緊急頁面、頁面碎片管理、頁面緩存、頁面統(tǒng)計(jì)等,而且對(duì)性能也有很高
    的頭像 發(fā)表于 01-04 09:24 ?1031次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>內(nèi)存</b>管理架構(gòu)解析

    Linux內(nèi)核地址映射模型與Linux內(nèi)核高端內(nèi)存詳解

    Linux 操作系統(tǒng)和驅(qū)動(dòng)程序運(yùn)行在內(nèi)核空間,應(yīng)用程序運(yùn)行在用戶空間,兩者不能簡單地使用指針傳遞數(shù)據(jù),因?yàn)?b class='flag-5'>Linux使用的虛擬內(nèi)存機(jī)制,用戶空間的數(shù)據(jù)可能被換出,當(dāng)
    發(fā)表于 05-08 10:33 ?3599次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>地址映射模型與<b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>高端<b class='flag-5'>內(nèi)存</b>詳解

    學(xué)習(xí)Linux內(nèi)核過程中的心得總結(jié)

    在上了Linux內(nèi)核這門課后,我對(duì)Linux內(nèi)核開始有了初步的了解,關(guān)于Linux內(nèi)核,我覺得最
    發(fā)表于 07-12 07:27

    linux內(nèi)存管理機(jī)制淺析

    本內(nèi)容介紹了arm linux內(nèi)存管理機(jī)制,詳細(xì)說明了linux內(nèi)核內(nèi)存管理,linux虛擬
    發(fā)表于 12-19 14:09 ?73次下載
    <b class='flag-5'>linux</b><b class='flag-5'>內(nèi)存</b>管理機(jī)制淺析

    高端內(nèi)存的詳解:linux用戶空間與內(nèi)核空間

    Linux 操作系統(tǒng)和驅(qū)動(dòng)程序運(yùn)行在內(nèi)核空間,應(yīng)用程序運(yùn)行在用戶空間,兩者不能簡單地使用指針傳遞數(shù)據(jù),因?yàn)?b class='flag-5'>Linux使用的虛擬內(nèi)存機(jī)制,用戶空間的數(shù)據(jù)可能被換出,當(dāng)
    發(fā)表于 04-28 17:33 ?1141次閱讀
    高端<b class='flag-5'>內(nèi)存</b>的詳解:<b class='flag-5'>linux</b>用戶空間與<b class='flag-5'>內(nèi)核</b>空間

    知識(shí)總結(jié):一篇就讓你入Linux內(nèi)核的大門

    ,為什么還要看你這一篇,這正是我寫此文的原因,網(wǎng)上碎片化的相關(guān)知識(shí)點(diǎn)大都是東拼西湊,先不說正確性與否,就連基本的邏輯都沒有搞清楚,我可以負(fù)責(zé)任的說Linux內(nèi)存管理只需要看此文一篇就可以讓你入Linux
    的頭像 發(fā)表于 05-08 10:18 ?2379次閱讀
    知識(shí)<b class='flag-5'>總結(jié)</b>:一篇就讓你入<b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>的大門

    Linux內(nèi)核引導(dǎo)內(nèi)存分配器的原理

    Linux內(nèi)核引導(dǎo)內(nèi)存分配器使用的是伙伴系統(tǒng)算法。這種算法是一種用于動(dòng)態(tài)內(nèi)存分配的高效算法,它將內(nèi)存空間劃分為大小相等的塊,然后將這些塊組合
    發(fā)表于 04-03 14:52 ?570次閱讀

    Linux內(nèi)核實(shí)現(xiàn)內(nèi)存管理的基本概念

    本文概述Linux內(nèi)核實(shí)現(xiàn)內(nèi)存管理的基本概念,在了解基本概念后,逐步展開介紹實(shí)現(xiàn)內(nèi)存管理的相關(guān)技術(shù),后面會(huì)分多篇進(jìn)行介紹。
    發(fā)表于 06-23 11:56 ?1118次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>實(shí)現(xiàn)<b class='flag-5'>內(nèi)存</b>管理的基本概念

    內(nèi)存內(nèi)核中發(fā)生頁面遷移的典型場景

    頁面遷移(page migrate)最早是為 NUMA 系統(tǒng)提供一種將進(jìn)程頁面遷移到指定內(nèi)存節(jié)點(diǎn)的能力用來提升訪問性能。后來在內(nèi)核中廣泛被使用,如內(nèi)存規(guī)整、CMA、
    的頭像 發(fā)表于 11-08 12:28 ?916次閱讀
    <b class='flag-5'>內(nèi)存</b><b class='flag-5'>內(nèi)核</b>中發(fā)生頁面遷移的典型場景

    Linux 內(nèi)存管理總結(jié)

    一、Linux內(nèi)存管理概述 Linux內(nèi)存管理是指對(duì)系統(tǒng)內(nèi)存的分配、釋放、映射、管理、交換、壓縮等一系列操作的管理。在
    的頭像 發(fā)表于 11-10 14:58 ?830次閱讀
    <b class='flag-5'>Linux</b> <b class='flag-5'>內(nèi)存</b>管理<b class='flag-5'>總結(jié)</b>