伙伴系統的概述

来源:https://www.cnblogs.com/linhaostudy/archive/2020/06/24/13187148.html
-Advertisement-
Play Games

Linux內核記憶體管理的一項重要工作就是如何在頻繁申請釋放記憶體的情況下,避免碎片的產生。Linux採用伙伴系統解決外部碎片的問題,採用slab解決內部碎片的問題,在這裡我們先討論外部碎片問題。避免外部碎片的方法有兩種:一種是之前介紹過的利用非連續記憶體的分配;另外一種則是用一種有效的方法來監視記憶體,保 ...


Linux內核記憶體管理的一項重要工作就是如何在頻繁申請釋放記憶體的情況下,避免碎片的產生。Linux採用伙伴系統解決外部碎片的問題,採用slab解決內部碎片的問題,在這裡我們先討論外部碎片問題。避免外部碎片的方法有兩種:一種是之前介紹過的利用非連續記憶體的分配;另外一種則是用一種有效的方法來監視記憶體,保證在內核只要申請一小塊記憶體的情況下,不會從大塊的連續空閑記憶體中截取一段過來,從而保證了大塊記憶體的連續性和完整性。顯然,前者不能成為解決問題的普遍方法,一來用來映射非連續記憶體線性地址空間有限,二來每次映射都要改寫內核的頁表,進而就要刷新TLB,這使得分配的速度大打折扣,這對於要頻繁申請記憶體的內核顯然是無法忍受的。因此Linux採用後者來解決外部碎片的問題,也就是著名的伙伴系統。

什麼是伙伴系統?

伙伴系統的宗旨就是用最小的記憶體塊來滿足內核的對於記憶體的請求。在最初,只有一個塊,也就是整個記憶體,假如為1M大小,而允許的最小塊為64K,那麼當我們申請一塊200K大小的記憶體時,就要先將1M的塊分裂成兩等分,各為512K,這兩分之間的關係就稱為伙伴,然後再將第一個512K的記憶體塊分裂成兩等分,各位256K,將第一個256K的記憶體塊分配給記憶體,這樣就是一個分配的過程。下麵我們結合示意圖來瞭解伙伴系統分配和回收記憶體塊的過程。

1 初始化時,系統擁有1M的連續記憶體,允許的最小的記憶體塊為64K,圖中白色的部分為空閑的記憶體塊,著色的代表分配出去了得記憶體塊。

2 程式A申請一塊大小為34K的記憶體,對應的order為0,即2^0=1個最小記憶體塊

2.1 系統中不存在order 0(64K)的記憶體塊,因此order 4(1M)的記憶體塊分裂成兩個order 3的記憶體塊(512K)

2.2 仍然沒有order 0的記憶體塊,因此order 3的記憶體塊分裂成兩個order 2的記憶體塊(256K)

2.3 仍然沒有order 0的記憶體塊,因此order 2的記憶體塊分裂成兩個order 1的記憶體塊(128K)

2.4 仍然沒有order 0的記憶體塊,因此order 1的記憶體塊分裂成兩個order 0的記憶體塊(64K)

2.5 找到了order 0的記憶體塊,將其中的一個分配給程式A,現在伙伴系統的記憶體為一個order 0的記憶體塊,一個order
1的記憶體塊,一個order 2的記憶體塊以及一個order 3的記憶體塊

3 程式B申請一塊大小為66K的記憶體,對應的order為1,即2^1=2個最小記憶體塊,由於系統中正好存在一個order 1的記憶體塊,所以直接用來分配

4 程式C申請一塊大小為35K的記憶體,對應的order為0,同樣由於系統中正好存在一個order 0的記憶體塊,直接用來分配

5 程式D申請一塊大小為67K的記憶體,對應的order為1

5.1 系統中不存在order 1的記憶體塊,於是將order 2的記憶體塊分裂成兩塊order 1的記憶體塊

5.2 找到order 1的記憶體塊,進行分配

6 程式B釋放了它申請的記憶體,即一個order 1的記憶體塊

7 程式D釋放了它申請的記憶體

7.1 一個order 1的記憶體塊回收到記憶體當中

7.2由於該記憶體塊的伙伴也是空閑的,因此兩個order 1的記憶體塊合併成一個order 2的記憶體塊

8 程式A釋放了它申請的記憶體,即一個order 0的記憶體塊

9 程式C釋放了它申請的記憶體

9.1 一個order 0的記憶體塊被釋放

9.2 兩個order 0伙伴塊都是空閑的,進行合併,生成一個order 1的記憶體塊m

9.3 兩個order 1伙伴塊都是空閑的,進行合併,生成一個order 2的記憶體塊

9.4 兩個order 2伙伴塊都是空閑的,進行合併,生成一個order 3的記憶體塊

9.5 兩個order 3伙伴塊都是空閑的,進行合併,生成一個order 4的記憶體塊

相關的數據結構

在前面的文章中已經簡單的介紹過struct zone這個結構,對於每個管理區都有自己的struct zone,而struct zone中的struct free_area則是用來描述該管理區伙伴系統的空閑記憶體塊的

<mmzone.h>

struct zone {
	...
         ...	
	struct free_area	free_area[MAX_ORDER];
	...
	...
}

<mmzone.h>

struct free_area {
	struct list_head	free_list[MIGRATE_TYPES];
	unsigned long		nr_free;
};

free_area共有MAX_ORDER個元素,其中第order個元素記錄了2order的空閑塊,這些空閑塊在free_list中以雙向鏈表的形式組織起來,對於同等大小的空閑塊,其類型不同,將組織在不同的free_list中,nr_free記錄了該free_area中總共的空閑記憶體塊的數量。MAX_ORDER的預設值為11,這意味著最大記憶體塊的大小為210=1024個頁框。對於同等大小的記憶體塊,每個記憶體塊的起始頁框用於鏈表的節點進行相連,這些節點對應的著struct page中的lru域

struct page {
	
	...
	...
	struct list_head lru;		/* Pageout list, eg. active_list
					 * protected by zone->lru_lock !
					 */
	...
}

連接示意圖如下:

在2.6.24之前的內核版本中,free_area結構中只有一個free_list數組,而從2.6.24開始,free_area結構中存有MIGRATE_TYPES個free_list,這些數組是根據頁框的移動性來劃分的,為什麼要進行這樣的劃分呢?實際上也是為了減少碎片而提出的,我們考慮下麵的情況:

圖中一共有32個頁,只分配出了4個頁框,但是能夠分配的最大連續記憶體也只有8個頁框(因為伙伴系統分配出去的記憶體必須是2的整數次冪個頁框),內核解決這種問題的辦法就是將不同類型的頁進行分組。分配出去的頁面可分為三種類型:

  • 不可移動頁(Non-movable pages):這類頁在記憶體當中有固定的位置,不能移動。內核的核心分配的記憶體大多屬於這種類型
  • 可回收頁(Reclaimable pages):這類頁不能直接移動,但可以刪除,其內容頁可以從其他地方重新生成,例如,映射自文件的數據屬於這種類型,針對這種頁,內核有專門的頁面回收處理
  • 可移動頁:這類頁可以隨意移動,用戶空間應用程式所用到的頁屬於該類別。它們通過頁表來映射,如果他們複製到新的位置,頁表項也會相應的更新,應用程式不會註意到任何改變。

假如上圖中大部分頁都是可移動頁,而分配出去的四個頁都是不可移動頁,由於不可移動頁插在了其他類型頁的中間,就導致了無法從原本空閑的連續記憶體區中分配較大的記憶體塊。考慮下圖的情況:

將可回收頁和不可移動頁分開,這樣雖然在不可移動頁的區域當中無法分配大塊的連續記憶體,但是可回收頁的區域卻沒有受其影響,可以分配大塊的連續記憶體。

內核對於遷移類型的定義如下:

<mmzone.h>

#define MIGRATE_UNMOVABLE     0
#define MIGRATE_RECLAIMABLE   1
#define MIGRATE_MOVABLE       2
#define MIGRATE_PCPTYPES      3 /* the number of types on the pcp lists */
#define MIGRATE_RESERVE       3
#define MIGRATE_ISOLATE       4 /* can't allocate from here */
#define MIGRATE_TYPES         5

前三種類型已經介紹過

MIGRATE_PCPTYPES是per_cpu_pageset,即用來表示每CPU頁框高速緩存的數據結構中的鏈表的遷移類型數目

MIGRATE_RESERVE是在前三種的列表中都沒用可滿足分配的記憶體塊時,就可以從MIGRATE_RESERVE分配

MIGRATE_ISOLATE用於跨越NUMA節點移動物理記憶體頁,在大型系統上,它有益於將物理記憶體頁移動到接近於是用該頁最頻繁地CPU

MIGRATE_TYPES表示遷移類型的數目

當一個指定的遷移類型所對應的鏈表中沒有空閑塊時,將會按以下定義的順序到其他遷移類型的鏈表中尋找

static int fallbacks[MIGRATE_TYPES][MIGRATE_TYPES-1] = {
	[MIGRATE_UNMOVABLE]   = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE,   MIGRATE_RESERVE },
	[MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE,   MIGRATE_MOVABLE,   MIGRATE_RESERVE },
	[MIGRATE_MOVABLE]     = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },
	[MIGRATE_RESERVE]     = { MIGRATE_RESERVE,     MIGRATE_RESERVE,   MIGRATE_RESERVE }, /* Never used */
};

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 我們利用IIS建立網站的時候,一般都是設定好網站名稱和物理地址,直接下一步建立完成了。正常訪問都沒問題,但如果我們這時候想要更改訪問的IP或者埠號,打開了很多設置項就是沒找到設置的地方。原來它一直在右邊的那個“連接”或者叫“綁定”那裡。 ...
  • 前言 RSA加密演算法是一種非對稱加密演算法,簡單來說,就是加密時使用一個鑰匙,解密時使用另一個鑰匙。 因為加密的鑰匙是公開的,所又稱公鑰,解密的鑰匙是不公開的,所以稱為私鑰。 密鑰 關於RSA加密有很多文章,但幾乎都只介紹了RSACryptoServiceProvider類的使用方法,如果只是走走看看 ...
  • 前言 上一篇【.Net Core微服務入門全紀錄(五)——Ocelot-API網關(下)】中已經完成了Ocelot + Consul的搭建,這一篇簡單說一下EventBus。 EventBus-事件匯流排 首先,什麼是事件匯流排呢? 貼一段引用: 事件匯流排是對觀察者(發佈-訂閱)模式的一種實現。它是一種 ...
  • 如果要支持Blazor WebAssembly的本地化,應該如何實現呢?下麵,我們就按照本地化問題操作中所涉及的所有主要問題以提問的方式進行說明。 1.本地化的核心原理是什麼? 答:就是顯式地在Program.Main方法中設置 CultureInfo.DefaultThreadCurrentCul ...
  • 大家好,我是良許。 在我們編寫代碼的時候,我們經常需要知道兩個文件之間,或者同一個文件不同版本之間有什麼差異性。在 Windows 下有個很強大的工具叫作 BeyondCompare ,那在 Linux 下需要用到什麼工具呢? 本文介紹 9 種 Linux 下常用的 9 種代碼比對工具,不僅有命令行 ...
  • 利用數組實現 1 #include<stdio.h> 2 #include<string.h> 3 4 void copy_string(char str1[],char str2[]) 5 { 6 int i = 0; 7 while(str2[i] != '\0') 8 { 9 str1[i] ...
  • 1. 概念 原子操作是指不被打斷的操作,即它是最小的執行單位。最簡單的原子操作就是一條條的彙編指令(不包括一些偽指令,偽指令會被彙編器解釋成多條彙編指令)。在 linux 中原子操作對應的數據結構為 atomic_t,定義如下: typedef struct { int counter; } ato ...
  • 隨著智能化互聯時代的來臨,家中的智能設備越來越多:電視機、平板、游戲主機、電腦、手機等遍及家中各個角落,同時設備之間共用數據的需求變的越來越強烈。比如同步、備份手機上的照片和視頻,在電視機上觀看電腦中下載的影片、手機拍攝的視頻,存儲高清電影、音樂、VLOG 素材等。這時候在家中搭建一臺 NAS(Ne ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...