最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Linux內(nèi)核機制總結(jié)內(nèi)存管理之反碎片技術(shù)

2022-09-17 20:18 作者:補給站Linux內(nèi)核  | 我要投稿

重要:本系列文章內(nèi)容摘自<Linux內(nèi)核深度解析>基于ARM64架構(gòu)的Linux4.x內(nèi)核一書,作者余華兵。系列文章主要用于記錄Linux內(nèi)核的大部分機制及參數(shù)的總結(jié)說明

1 反碎片技術(shù)

內(nèi)存碎片分為內(nèi)部碎片和外部碎片,內(nèi)部碎片指內(nèi)存頁里面的碎片,外部碎片指空閑的內(nèi)存頁分散,很難找到一組物理地址連續(xù)的空閑內(nèi)存頁,無法滿足超過一頁的內(nèi)存分配請求。

對于內(nèi)核來說,外部碎片是一個問題,內(nèi)核有時候需要分配超過一頁的物理內(nèi)存,因為內(nèi)核使用線性映射區(qū)域的虛擬地址,所以必須分配連續(xù)的物理頁。

如果進程使用巨型頁,外部碎片是一個問題,因為巨型頁需要連續(xù)的物理頁。

為了解決外部碎片問題,內(nèi)核引入了以下反碎片技術(shù): (1)2.6.23版本引入了虛擬可移動區(qū)域。

(2)2.6.23版本引入了成塊回收(lumpy reclaim,有的書中翻譯為集中回收),3.5版本廢除,被內(nèi)存碎片整理技術(shù)取代。

成塊回收不是一個完整的解決方案,它只是緩解了碎片問題。成塊回收,就是嘗試成塊回收目標頁相鄰的頁面,以形成一塊滿足需求的高階連續(xù)頁塊。這種方法有其局限性,就是成塊回收時沒有考慮被連帶回收的頁面可能是“熱頁”,即被高強度使用的頁,這對系統(tǒng)性能是損傷。

(3)2.6.24版本引入了根據(jù)可移動性分組的技術(shù),把物理頁分為不可移動頁、可移動頁和可回收頁3種類型,在之前文章已經(jīng)介紹了這種反碎片技術(shù)。

(4)2.6.35版本引入了內(nèi)存碎片整理技術(shù)。

虛擬可移動區(qū)域和根據(jù)可移動性分組是預防外部碎片的技術(shù),成塊回收和內(nèi)存碎片整理是在出現(xiàn)外部碎片以后消除外部碎片的技術(shù)。


【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個人覺得比較好的學習書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦!?。。ê曨l教程、電子書、實戰(zhàn)項目及代碼)? ? ?

1.1 虛擬可移動區(qū)域

可移動區(qū)域(ZONE_MOVABLE)是一個偽內(nèi)存區(qū)域,基本思想很簡單:把物理內(nèi)存分為兩個區(qū)域,一個區(qū)域用于分配不可移動的頁,另一個區(qū)域用于分配可移動的頁,防止不可移動頁向可移動區(qū)域引入碎片。

1.使用方法 可移動區(qū)域必須由管理員配置,配置方法如下: (1)使用內(nèi)核引導參數(shù)“kernelcore=nn[KMGTPE]”(K表示單位是KB,M表示單位是MB)指定不可移動區(qū)域的大?。灰部梢允褂谩発ernelcore=mirror”指定使用鏡像的內(nèi)存作為不可移動區(qū)域,使用其他內(nèi)存作為可移動區(qū)域。

內(nèi)存鏡像是內(nèi)存冗余技術(shù)的一種,是為了提高服務器的可靠性,防止內(nèi)存故障導致服務器的數(shù)據(jù)永久丟失或者系統(tǒng)宕機。內(nèi)存鏡像的工作原理與硬盤的熱備份類似,內(nèi)存鏡像是將內(nèi)存數(shù)據(jù)做兩個拷貝,分別放在主內(nèi)存和鏡像內(nèi)存中。系統(tǒng)工作時會向兩個內(nèi)存中同時寫入數(shù)據(jù),因此使得內(nèi)存數(shù)據(jù)有兩套完整的備份。

(2)使用內(nèi)核引導參數(shù)“movablecore=nn[KMG]”指定可移動區(qū)域的大小。

(3)如果同時指定參數(shù)kernelcore和movablecore,那么不可移動區(qū)域的大小取參數(shù)kernelcore和(物理內(nèi)存容量 ? 參數(shù)movablecore)的最大值。

默認認為巨型頁是不可移動的,不會從可移動區(qū)域分配巨型頁,可以通過文件“/proc/ sys/vm/hugepages_treat_as_movable”配置允許從可移動區(qū)域分配巨型頁。

在NUMA系統(tǒng)上,如果打開配置宏CONFIG_MOVABLE_NODE(允許一個內(nèi)存節(jié)點只有可移動的內(nèi)存),并且指定內(nèi)核引導參數(shù)“movable_node”,那么忽略內(nèi)核引導參數(shù)“kernelcore”和“movablecore”,所有可以熱插拔的物理內(nèi)存都作為可移動區(qū)域。

2.技術(shù)原理 可移動區(qū)域(ZONE_MOVABLE)沒有包含任何物理內(nèi)存,所以我們說它是偽內(nèi)存區(qū)域,或者說是虛擬的內(nèi)存區(qū)域??梢苿訁^(qū)域借用最高內(nèi)存區(qū)域的內(nèi)存,在32位系統(tǒng)上最高的內(nèi)存區(qū)域通常是高端內(nèi)存區(qū)域(ZONE_HIGHMEM),在64位系統(tǒng)上最高的內(nèi)存區(qū)域通常是普通區(qū)域(ZONE_NORMAL)。

(1)解析內(nèi)核引導參數(shù)。 (2)確定可移動區(qū)域的范圍。 函數(shù)find_zone_movable_pfns_for_nodes確定可移動區(qū)域的范圍。

1)確定可移動區(qū)域從哪個內(nèi)存區(qū)域借用物理頁:調(diào)用函數(shù)find_usable_zone_for_movable以查找包含物理頁的最高內(nèi)存區(qū)域,全局變量movable_zone保存借用區(qū)域的索引。

2)確定每個內(nèi)存節(jié)點中可移動區(qū)域的起始物理頁號,使用全局數(shù)組zone_movable_pfn[MAX_NUMNODES]保存,分3種情況:

使用可以熱插拔的物理內(nèi)存作為可移動區(qū)域。

使用鏡像內(nèi)存作為不可移動區(qū)域,使用其他內(nèi)存作為可移動區(qū)域。

如果管理員配置了不可移動區(qū)域或可移動區(qū)域的大小,處理如下:如果同時指定參數(shù)kernelcore和movablecore,那么不可移動區(qū)域的大小取參數(shù)kernelcore和(物理內(nèi)存容量 ? 參數(shù)movablecore)的最大值;把不可移動區(qū)域的內(nèi)存按比例分布到所有內(nèi)存節(jié)點上。

函數(shù)calculate_node_totalpages負責計算一個內(nèi)存節(jié)點中所有內(nèi)存區(qū)域的起始物理頁號和物理頁總數(shù),針對每個內(nèi)存區(qū)域,調(diào)用函數(shù)zone_spanned_pages_in_node來計算內(nèi)存區(qū)域的起始物理頁號和結(jié)束物理頁號。

1)從全局數(shù)組arch_zone_lowest_possible_pfn和arch_zone_highest_possible_pfn中分別得到內(nèi)存區(qū)域的起始物理頁號和結(jié)束物理頁號。

2)調(diào)用函數(shù)adjust_zone_range_for_zone_movable,根據(jù)數(shù)組zone_movable_pfn修正借用區(qū)域的結(jié)束物理頁號,以及得到可移動區(qū)域的起始物理頁號。

(3)從可移動區(qū)域分配物理頁。

申請物理頁的時候,如果同時指定了分配標志__GFP_HIGHMEM和__GFP_MOVABLE,頁分配器的核心函數(shù)__alloc_pages_nodemask(-> prepare_alloc_pages -> gfp_zone)計算出首選的內(nèi)存區(qū)域是可移動區(qū)域,首先嘗試從可移動區(qū)域分配物理頁。如果可移動區(qū)域分配失敗,從備用的內(nèi)存區(qū)域借用物理頁。

分配標志__GFP_MOVABLE有兩個用處。 1)和__GFP_HIGHMEM組合表示從可移動區(qū)域分配物理頁。 2)在根據(jù)可移動性分組技術(shù)中表示申請遷移類型是可移動類型的物理頁。

為用戶空間分配物理頁時,通常使用分配標志組合GFP_HIGHUSER_MOVABLE,這個組合包含標志__GFP_HIGHMEM和__GFP_MOVABLE。

例如,進程訪問匿名頁的時候,如果沒有映射到物理頁,生成頁錯誤異常。頁錯誤異常處理程序在函數(shù)do_anonymous_page中調(diào)用函數(shù)alloc_zeroed_user_highpage_movable以分配物理頁,函數(shù)alloc_zeroed_user_highpage_movable使用分配標志組合GFP_HIGHUSER_MOVABLE分配物理頁。

1.2 內(nèi)存碎片整理

內(nèi)存碎片整理(memory compaction,直譯為“內(nèi)存緊縮”,意譯為“內(nèi)存碎片整理”)的基本思想是:從內(nèi)存區(qū)域的底部掃描已分配的可移動頁,從內(nèi)存區(qū)域的頂部掃描空閑頁,把底部的可移動頁移到頂部的空閑頁,在底部形成連續(xù)的空閑頁。

1.使用方法 編譯內(nèi)核時,如果需要內(nèi)存碎片整理功能,必須開啟配置文件“mm/Kconfig”定義的配置宏CONFIG_COMPACTION,默認開啟。

內(nèi)存碎片整理技術(shù)提供了以下配置文件: (1)文件“/proc/sys/vm/compact_memory”:向這個文件寫入任何整數(shù)值(數(shù)值沒有意義),觸發(fā)內(nèi)存碎片整理。 (2)文件“/proc/sys/vm/compact_unevictable_allowed”:用來設(shè)置是否允許內(nèi)存碎片整理移動不可回收的頁(進程使用系統(tǒng)調(diào)用mlock把頁鎖定在內(nèi)存中),如果設(shè)置為1,表示允許,默認值是1。 (3)文件“/proc/sys/vm/extfrag_threshold”:用來設(shè)置外部碎片的閾值,取值范圍是 0~1000,默認值是500。這個參數(shù)影響內(nèi)核在申請連續(xù)頁失敗的時候選擇直接回收頁還是選擇內(nèi)存碎片整理。內(nèi)核計算出內(nèi)存區(qū)域的碎片指數(shù),碎片指數(shù)趨向0表示分配失敗是因為內(nèi)存不足,碎片指數(shù)趨向1000表示分配失敗是因為內(nèi)存碎片。如果碎片指數(shù)小于或等于外部碎片的閾值,選擇直接回收頁;如果碎片指數(shù)大于閾值,那么選擇內(nèi)存碎片整理。

2.技術(shù)原理 我們假設(shè)有一個很小的內(nèi)存區(qū)域,包含16個頁,如下所示:





白色表示頁是空閑的,這個內(nèi)存區(qū)域已經(jīng)碎片化。最大的連續(xù)空閑頁是兩頁,從這個區(qū)域分配四頁將會失敗,甚至分配兩頁也會失敗,因為連續(xù)的兩個空閑頁的起始地址沒有對齊到兩頁的整數(shù)倍。

首先,內(nèi)存碎片整理算法從內(nèi)存區(qū)域的底部向頂部掃描,把可以移動的已分配頁組成一條鏈表,我們把這個掃描稱為遷移掃描器,如下所示:




然后,內(nèi)存碎片整理算法從內(nèi)存區(qū)域的頂部向底部掃描,把空閑的頁組成一條鏈表,我們把這個掃描稱為空閑掃描器,如下所示:




最后,遷移掃描器和空閑掃描器在內(nèi)存區(qū)域的中間相遇,把可以移動的已分配頁移到頂部的空閑頁,形成連續(xù)的8個空閑頁,可以滿足申請連續(xù)8頁的需求,如下所示:




在真實的系統(tǒng)中,內(nèi)存區(qū)域大得多,內(nèi)存碎片整理以內(nèi)存區(qū)域為單位執(zhí)行,在內(nèi)存區(qū)域內(nèi)部以分組頁塊為單位執(zhí)行。 內(nèi)存碎片整理的算法如下: (1)首先從內(nèi)存區(qū)域的底部向頂部以頁塊為單位掃描,在頁塊內(nèi)部從起始頁向結(jié)束頁掃描,把這個頁塊里面的可移動頁組成一條鏈表。 (2)然后從內(nèi)存區(qū)域的頂部向底部以頁塊為單位掃描,在頁塊內(nèi)部也是從起始頁向結(jié)束頁掃描,把空閑頁組成一條鏈表。 (3)最后把底部的可移動頁的數(shù)據(jù)復制到頂部的空閑頁,修改進程的頁表,把虛擬頁映射到新的物理頁。

內(nèi)存碎片整理有3種優(yōu)先級,從高到低依次如下所示: (1)COMPACT_PRIO_SYNC_FULL:完全同步模式,允許阻塞,允許把臟的文件頁回寫到存儲設(shè)備上,并且等待回寫完成。 (2)COMPACT_PRIO_SYNC_LIGHT:輕量級同步模式,允許大多數(shù)操作阻塞,但是不允許把臟的文件頁回寫到存儲設(shè)備上(因為可能需要等待很長的時間)。 (3)COMPACT_PRIO_ASYNC:異步模式,不允許阻塞。

完全同步模式的成本最高,輕量級同步模式的成本其次,異步模式的成本最低。

執(zhí)行內(nèi)存碎片整理的時機如下: (1)頁分配器使用最低水線分配頁失敗以后,如果調(diào)用者允許直接回收頁(即設(shè)置了分配標志__GFP_DIRECT_RECLAIM)和寫存儲設(shè)備(即設(shè)置了分配標志__GFP_IO),并且是昂貴的分配(申請的階數(shù)大于 3)或者申請不可移動類型的連續(xù)頁,那么在嘗試直接回收頁之前,先嘗試執(zhí)行異步模式的內(nèi)存碎片整理。 (2)頁分配器直接回收頁以后分配連續(xù)頁仍然失敗,如果調(diào)用者允許寫存儲設(shè)備,嘗試執(zhí)行輕量級同步模式的內(nèi)存碎片整理。 (3)每個內(nèi)存節(jié)點有一個頁回收線程和一個內(nèi)存碎片整理線程,當頁回收線程準備睡眠一小段時間的時候,喚醒內(nèi)存碎片整理線程,內(nèi)存碎片整理線程執(zhí)行輕量級同步模式的內(nèi)存碎片整理。 內(nèi)存碎片整理線程的名稱是“kcompactd<node_id>”,內(nèi)存節(jié)點的pglist_data 實例的成員“kcompactd”指向內(nèi)存碎片整理線程的進程描述符。 (4)當管理員向文件“/proc/sys/vm/compact_memory”寫入任何整數(shù)值的時候,在所有內(nèi)存節(jié)點的所有內(nèi)存區(qū)域上執(zhí)行完全同步的內(nèi)存碎片整理。

判斷一個內(nèi)存區(qū)域是否適合執(zhí)行內(nèi)存碎片整理的標準如下: (1)如果管理員通過寫文件“/proc/sys/vm/compact_memory”觸發(fā)內(nèi)存碎片整理,那么這個內(nèi)存區(qū)域強制執(zhí)行內(nèi)存碎片整理。 (2)如果內(nèi)存區(qū)域同時滿足以下3個條件,適合執(zhí)行內(nèi)存碎片整理。 1)如果(空閑頁數(shù) ? 申請頁數(shù))低于水線,或者雖然大于或等于水線但是沒有一個足夠大的空閑頁塊,那么這個內(nèi)存區(qū)域適合執(zhí)行內(nèi)存碎片整理。 2)如果(空閑頁數(shù) ? 兩倍的申請頁數(shù))大于或等于水線,說明有足夠多的空閑頁作為遷移的目的地,那么這個內(nèi)存區(qū)域適合執(zhí)行內(nèi)存碎片整理。 3)對于昂貴的分配(階數(shù)大于3),計算碎片指數(shù)(fragmentation index)。如果碎片指數(shù)在范圍[0,外部碎片的閾值]以內(nèi),說明分配失敗是內(nèi)存不足導致的,不是外部碎片導致的,那么這個內(nèi)存區(qū)域不適合執(zhí)行內(nèi)存碎片整理。

如果不存在空閑頁塊,那么碎片指數(shù) = 0。 如果至少存在一個足夠大的空閑頁塊,那么碎片指數(shù) = ?1000。 其他情況,碎片指數(shù) = 1000 ?(1000 + 1000 * 空閑頁數(shù) / 申請頁數(shù))/ 空閑頁塊的總數(shù) 碎片指數(shù)趨向0表示分配失敗是因為內(nèi)存不足,趨向1000表示分配失敗是因為外部碎片。外部碎片的閾值是內(nèi)存不足和外部碎片的分界線:如果碎片指數(shù)小于或等于閾值,分配失敗是因為內(nèi)存不足,應該直接回收頁;如果碎片指數(shù)大于閾值,分配失敗是因為外部碎片,應該執(zhí)行內(nèi)存碎片整理。

內(nèi)存碎片整理的結(jié)束條件如下: (1)如果遷移掃描器和空閑掃描器相遇,那么內(nèi)存碎片整理結(jié)束。 (2)如果遷移掃描器和空閑掃描器沒有相遇,但是申請或備用的遷移類型至少有一個足夠大的空閑頁塊,那么內(nèi)存碎片整理結(jié)束。

如果管理員通過寫文件“/proc/sys/vm/compact_memory”觸發(fā)內(nèi)存碎片整理,結(jié)束的唯一條件是遷移掃描器和空閑掃描器相遇。

內(nèi)存碎片整理推遲

內(nèi)存碎片整理成功的標準是:(空閑頁數(shù) ? 申請頁數(shù))大于或等于水線,并且申請或備用的遷移類型至少有一個足夠大的空閑頁塊。

執(zhí)行完全同步模式或輕量級同步模式的內(nèi)存碎片整理,當遷移掃描器和空閑掃描器相遇的時候,沒有達到成功標準,以后試圖執(zhí)行輕量級同步或異步模式的內(nèi)存碎片整理,如果申請階數(shù)大于或等于內(nèi)存碎片整理失敗時的申請階數(shù),需要推遲若干次。

內(nèi)核在內(nèi)存區(qū)域中增加了3個成員用來記錄內(nèi)存碎片整理推遲的信息:

(1)成員compact_considered記錄推遲的次數(shù)。 (2)成員compact_defer_shift是推遲的最大次數(shù)以2為底的對數(shù),當推遲的次數(shù)達到(1 << compact_defer_shift)時,不能推遲。

每次內(nèi)存碎片整理執(zhí)行失敗,把成員compact_defer_shift加1,不允許超過COMPACT_MAX_DEFER_SHIFT(值為6),即把推遲的最大次數(shù)翻倍,但是不能超過64。

頁分配器在執(zhí)行內(nèi)存碎片整理以后,如果分配頁成功,那么把成員compact_defer_shift設(shè)置為0。

(3)成員compact_order_failed記錄內(nèi)存碎片整理失敗時的申請階數(shù)。 內(nèi)存碎片整理執(zhí)行成功的時候,如果申請階數(shù)order大于或等于成員compact_order_failed,那么把成員compact_order_failed設(shè)置為(order+1)。

內(nèi)存碎片整理執(zhí)行失敗的時候,如果申請階數(shù)order小于成員compact_order_failed,那么把成員compact_order_failed設(shè)置為order。

3.代碼分析 如下所示,函數(shù)__alloc_pages_nodemask是頁分配器的核心函數(shù),函數(shù)__alloc_pages_slowpath是頁分配器的慢速路徑,執(zhí)行內(nèi)存碎片整理的代碼如下:





Linux內(nèi)核機制總結(jié)內(nèi)存管理之反碎片技術(shù)的評論 (共 條)

分享到微博請遵守國家法律
紫金县| 株洲市| 龙岩市| 临沂市| 荆门市| 屏南县| 饶河县| 丰宁| 金沙县| 合川市| 泗洪县| 唐山市| 石泉县| 班玛县| 平乐县| 团风县| 新泰市| 库车县| 东乌珠穆沁旗| 岳阳县| 宜兴市| 繁昌县| 永善县| 旬邑县| 广南县| 松原市| 布尔津县| 临西县| 龙陵县| 林甸县| 东山县| 沈阳市| 东乌| 普陀区| 陈巴尔虎旗| 开鲁县| 桐乡市| 鹿邑县| 太仆寺旗| 莲花县| 黄大仙区|