精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Linux內核中的頁面分配機制

嵌入式與Linux那些事 ? 來源:嵌入式與Linux那些事 ? 2024-08-07 15:51 ? 次閱讀

來源:嵌入式Linux那些事公眾號

前言

Linux內核中是如何分配出頁面的,如果我們站在CPU的角度去看這個問題,CPU能分配出來的頁面是以物理頁面為單位的。也就是我們計算機中常講的分頁機制。本文就看下Linux內核是如何管理,釋放和分配這些物理頁面的。

伙伴算法

伙伴系統的定義

大家都知道,Linux內核的頁面分配器的基本算法是基于伙伴系統的,伙伴系統通俗的講就是以2^order 分配內存的。這些內存塊我們就稱為伙伴。

何為伙伴

兩個塊大小相同

兩個塊地址連續

兩個塊必須是同一個大塊分離出來的

下面我們舉個例子理解伙伴分配算法。假設我們要管理一大塊連續的內存,有64個頁面,假設現在來了一個請求,要分配8個頁面。總不能把64個頁面全部給他使用吧。

7214e058-47f3-11ef-b8af-92fbcf53809c.png截圖_20240623133911

首先把64個頁面一切為二,每部分32個頁面。

72261f1c-47f3-11ef-b8af-92fbcf53809c.png截圖_20240623134103

把32個頁面給請求者還是很大,這個時候會繼續拆分為16個。

7245169c-47f3-11ef-b8af-92fbcf53809c.png截圖_20240623134157

最后會將16個頁面繼續拆分為8個,將其返回給請求者,這就完成了第一個請求。

725d22e6-47f3-11ef-b8af-92fbcf53809c.png截圖_20240623134617

這個時候,第二個請求者也來了,同樣的請求8個頁面,這個時候系統就會把另外8個頁面返回給請求者。

72741b54-47f3-11ef-b8af-92fbcf53809c.png截圖_20240623134828

假設現在有第三個請求者過來了,它請求4個頁面。這個時候之前的8個頁面都被分配走了,這個時候就要從16個頁面的內存塊切割了,切割后變為每份8個頁面。最后將8個頁面的內存塊一分為二后返回給調用者。

7285b3c8-47f3-11ef-b8af-92fbcf53809c.png截圖_20240623134934 729f5d5a-47f3-11ef-b8af-92fbcf53809c.png截圖_20240623135122

假設前面分配的8個頁面都已經用完了,這個時候可以把兩個8個頁面合并為16個頁面。

72b56956-47f3-11ef-b8af-92fbcf53809c.png截圖_20240623135232

以上例子就是伙伴系統的簡單的例子,大家可以通過這個例子通俗易懂的理解伙伴系統。

另外一個例子將要去說明三個條件中的第三個條件:兩個塊必須要是從同一個大塊中分離出來的,這兩個塊才能稱之為伙伴,才能去合并為一個大塊。

72ceb050-47f3-11ef-b8af-92fbcf53809c.png

我們以8個頁面的一個大塊為例子來說明,如圖A0所示。將A0一分為二分,分別為 B0,B1。

B0:4頁

B1:4頁

再將B0,B1繼續切分:

C0:2頁

C1:2頁

C2:2頁

C3:2頁

最后可以將C0,C1,C2,C3切分為1個頁面大小的內存塊。

我們從C列來看,C0,C1稱之為伙伴關系,C2,C3為伙伴關系。

同理,page0 和 page1也為伙伴關系,因為他們都是從C0分割出來的。

72e3378c-47f3-11ef-b8af-92fbcf53809c.png截圖_20240623140813

假設,page0正在使用,page1 和 page2都是空閑的。那page1 和 page 2 可以合并成一個大的內存塊嗎?

我們從上下級的關系來看,page 1,page 2 并不屬于一個大內存塊切割而來的,不屬于伙伴關系。

如果我們把page 1 page 2,page4 page 5 合并了,看下結果會是什么樣子。

7301aeb0-47f3-11ef-b8af-92fbcf53809c.png截圖_20240623141028

page0和page3 就會變成大內存塊中孤零零的空洞了。page 0 和 page3 就無法再和其他塊合并了。這樣就形成了外碎片化。因此,內核的伙伴系統是極力避免這種清空發生的。

伙伴系統在內核中的實現

下面我們看下內核中是怎么實現伙伴系統的。

731c4522-47f3-11ef-b8af-92fbcf53809c.png截圖_20240623143810

上面這張圖是內核中早期伙伴系統的實現

內核中把內存以2^order 為單位分為多個鏈表。order范圍為[0,MAX_ORDER-1],MAX_ORDER一般為11。因此,Linux內核中可以分配的最大的內存塊為2^10= 4M,術語叫做page block。

內核中有一個叫free_area的數據結構,這個數據結構為鏈表的數組。數組的大小為MAX_ORDER。數組的每個成員為一個鏈表。分別表示對應order的空閑鏈表。以上就是早期的伙伴系統的頁面分配器的實現。

現在的伙伴系統中的頁面分配器的實現,為了解決內存碎片化的問題,在Linux內核2.6.4中引入了遷移類型的 算法緩解內存碎片化的問題。

733862b6-47f3-11ef-b8af-92fbcf53809c.png

我們看這張圖,現在的頁面分配器中,每個free_area數組成員中都增加了一個遷移類型。也就是說在每個order鏈表中多增加了一個鏈表。例如,order = 0 的鏈表中,新增了MOVABLE 鏈表,UNMOVABLE 鏈表,RECLAIMABLE鏈表。隨著內核的發展,遷移類型越來越多,但常用的就那三個。

遷移類型

在Linux內核2.6.4內核中引入了反碎片化的概念,反碎片化就是根據遷移類型來實現的。我們知道遷移類型 是根據page block來劃分的。我們看下常用的遷移類型。

MIGRATE_UNMOVABLE:在內存中有固定位置,不能隨意移動,比如內核分配的內存。那為什么內核分配的不能遷移呢?因此要遷移頁面,首先要把物理頁面的映射關系斷開,在新的地方分配物理頁面,重新建立映射關系。在斷開映射關系的途中,如果內核繼續訪問這個頁面,會導致oop錯誤或者系統crash。因為內核是敏感區,內核必須保證它使用的內存是安全的。這一點和用戶進程不一樣。如果是用戶進程使用的內存,我們將其斷開后,用戶進程再去訪問,就會產生缺頁中斷,重新去尋找可用物理內存然后建立映射關系。

MIGRATE_MOVABLE:可以隨意移動,用戶態app分配的內存,mlock,mmap分配的 匿名頁面。

MIGRATE_RECLAIMABLE:不能移動可以刪除回收,比如文件映射。

內存碎片化的產生

伙伴系統的遷移算法可以解決一些碎片化的問題,但在內存管理的方面,長期存在一個問題。從系統啟動,長期運行之后,經過大量的分配-釋放過程,還是會產生很多碎片,下面我們看下,這些碎片是怎么產生的。

我們以8個page的內存塊為例,假設page3是被內核使用的,比如alloc_page(GFP_KERNRL),所以它屬于不可移動的頁面,它就像一個樁一樣,插入在一大塊內存的中間。

盡管其他的頁面都是空閑頁面,導致page0 ~ page 7 不能合并為一個大塊的內存。

73597af0-47f3-11ef-b8af-92fbcf53809c.png

下面我們看下,遷移類型是怎么解決這類問題的。我們知道,遷移算法是以page block為單位工作的,一個page block大小就是頁面分配器能分配的最大內存塊。也就是說,一個page block 中的頁面都是屬于一個遷移類型的。所以,就不會存在上面說的多個page中夾著一個不可遷移的類型的情況。

頁面分配和釋放常用的函數

頁面分配函數

alloc_pages是內核中常用的分配物理內存頁面的函數, 用于分配2^order個連續的物理頁。

staticinlinestructpage*alloc_pages(gfp_tgfp_mask,unsignedintorder)

gfp_mask:gfp的全稱是get free page, 因此gfp_mask表示頁面分配的方法。gfp_mask的具體分類后面我們會詳細介紹。

order:頁面分配器使用伙伴系統按照順序請求頁面分配。所以只能以2的冪內存分配。例如,請求order=3的頁面分配,最終會分配2 ^ 3 = 8頁。arm64當前默認MAX_ORDER為11, 即最多一次性分配2 ^(MAX_ORDER-1)個頁。

返回值:返回指向第一個page的struct page指針

__get_free_page() 是頁面分配器提供給調用者的最底層的內存分配函數。它分配連續的物理內存。__get_free_page() 函數本身是基于 buddy 實現的。在使用 buddy 實現的物理內存管理中最小分配粒度是以頁為單位的。

unsignedlong__get_free_pages(gfp_tgfp_mask,unsignedintorder)

返回值:返回第一個page映射后的虛擬地址。

#definealloc_page(gfp_mask)alloc_pages(gfp_mask,0)

alloc_page 是宏定義,邏輯是調用 alloc_pages,傳遞給 order 參數的值為 0,表示需要分配的物理頁個數為 2 的 0 次方,即 1 個物理頁,需要用戶傳遞參數 GFP flags。

釋放函數

voidfree_pages(unsignedlongaddr,unsignedintorder)

釋放2^order大小的頁塊,傳入參數是頁框首地址的虛擬地址

#define__free_page(page)__free_pages((page),0)

釋放一個頁,傳入參數是指向該頁對應的虛擬地址

#definefree_page(addr)free_pages((addr),0)

釋放一個頁,傳入參數是頁框首地址的虛擬地址

gfp_mask標志位

行為修飾符

標志 描述
GFP_WAIT 分配器可以睡眠
GFP_HIGH 分配器可以訪問緊急的內存池
GFP_IO 不能直接移動,但可以刪除
GFP_FS 分配器可以啟動文件系統IO
GFP_REPEAT 在分配失敗的時候重復嘗試
GFP_NOFAIL 分配失敗的時候重復進行分配,直到分配成功位置
GFP_NORETRY 分配失敗時不允許再嘗試

zone 修飾符

標志 描述
GFP_DMA 從ZONE_DMA中分配內存(只存在與X86)
GFP_HIGHMEM 可以從ZONE_HIGHMEM或者ZONE_NOMAL中分配

水位修飾符

標志 描述
GFP_ATOMIC 分配過程中不允許睡眠,通常用作中斷處理程序、下半部、持有自旋鎖等不能睡眠的地方
GFP_KERNEL 常規的內存分配方式,可以睡眠
GFP_USER 常用于用戶進程分配內存
GFP_HIGHUSER 需要從ZONE_HIGHMEM開始進行分配,也是常用于用戶進程分配內存
GFP_NOIO 分配可以阻塞,但不會啟動磁盤IO
GFP_NOFS 可以阻塞,可以啟動磁盤,但不會啟動文件系統操作

GFP_MASK和zone 以及遷移類型的關系

GFP_MASK除了表示分配行為之外,還可以表示從那些ZONE來分配內存。還可以確定從那些遷移類型的page block 分配內存。

我們以ARM為例,由于ARM架構沒有ZONE_DMA的內存,因此只能從ZONE_HIGHMEM或者ZONE_NOMAL中分配.

在內核中有兩個數據結構來表示從那些地方開始分配內存。

structzonelist{
structzoneref_zonerefs[MAX_ZONES_PER_ZONELIST+1];
};structzonelist

zonelist是一個zone的鏈表。一次分配的請求是在zonelist上執行的。開始在鏈表的第一個zone上分配,如果失敗,則根據優先級降序訪問其他zone。zlcache_ptr 指向zonelist的緩存。為了加速對zonelist的讀取操作 ,用_zonerefs 保存zonelist中每個zone的index。

structzoneref{
structzone*zone;/*Pointertoactualzone*/
intzone_idx;/*zone_idx(zoneref->zone)*/
};

頁面分配器是基于ZONE來設計的,因此頁面的分配有必要確定那些zone可以用于本次頁面分配。系統會優先使用ZONE_HIGHMEM,然后才是ZONE_NORMAL 。

基于zone 的設計思想,在分配物理頁面的時候理應以zone_hignmem優先,因為hign_mem 在zone_ref中排在zone_normal的前面。而且,ZONE_NORMAL是線性映射的,線性映射的內存會優先給內核態使用。

頁面分配的時候從那個遷移類型中分配出內存呢?

函數static inline int gfp_migratetype(const gfp_t gfp_flags)可以根據掩碼類型轉換出遷移類型,從那個遷移類型分配頁面。比如GFP_KERNEL是從UNMOVABLE類型分配頁面的。

ZONE水位

頁面分配器是基于ZONE的機制來實現的,怎么去管理這些空閑頁面呢?Linux內核中定義了三個警戒線,WATERMARK_MIN,WATERMARK_LOW,WATERMARK_HIGH。大家可以看下面這張圖,就是分配水位和警戒線的關系。

7378b7c6-47f3-11ef-b8af-92fbcf53809c.jpg

最低水線(WMARK_MIN):當剩余內存在min以下時,則系統內存壓力非常大。一般情況下min以下的內存是不會被分配的,min以下的內存默認是保留給特殊用途使用,屬于保留的頁框,用于原子的內存請求操作。比如:當我們在中斷上下文申請或者在不允許睡眠的地方申請內存時,可以采用標志GFP_ATOMIC來分配內存,此時才會允許我們使用保留在min水位以下的內存。

低水線(WMARK_LOW):空閑頁數小數低水線,說明該內存區域的內存輕微不足。默認情況下,該值為WMARK_MIN的125%

高水線(WMARK_HIGH):如果內存區域的空閑頁數大于高水線,說明該內存區域水線充足。默認情況下,該值為WMARK_MAX的150%

在進行內存分配的時候,如果分配器(比如buddy allocator)發現當前空余內存的值低于”low”但高于”min”,說明現在內存面臨一定的壓力,那么在此次內存分配完成后,kswapd將被喚醒,以執行內存回收操作。在這種情況下,內存分配雖然會觸發內存回收,但不存在被內存回收所阻塞的問題,兩者的執行關系是異步的

對于kswapd來說,要回收多少內存才算完成任務呢?只要把空余內存的大小恢復到”high”對應的watermark值就可以了,當然,這取決于當前空余內存和”high”值之間的差距,差距越大,需要回收的內存也就越多。”low”可以被認為是一個警戒水位線,而”high”則是一個安全的水位線。

如果內存分配器發現空余內存的值低于了”min”,說明現在內存嚴重不足。這里要分兩種情況來討論,一種是默認的操作,此時分配器將同步等待內存回收完成,再進行內存分配,也就是direct reclaim。還有一種特殊情況,如果內存分配的請求是帶了PF_MEMALLOC標志位的,并且現在空余內存的大小可以滿足本次內存分配的需求,那么也將是先分配,再回收。

per-cpu頁面分配

內核會經常請求和釋放單個頁框,比如網卡驅動。

頁面分配器分配和釋放頁面的時候需要申請一把鎖:zone->lock

為了提高單個頁框的申請和釋放效率,內核建立了per-cpu頁面告訴緩存池

其中存放了若干預先分配好的頁框

當請求單個頁框時,直接從本地cpu的頁框告訴緩存池中獲取頁框

體現了預先建立緩存池的優勢,而且是每個CPU有一個獨立的緩存池

不必申請鎖

不必進行復雜的頁框分配操作

per-cpu數據結構

由于頁框頻繁的分配和釋放,內核在每個zone中放置了一些事先保留的頁框。這些頁框只能由來自本地CPU的請求使用。zone中有一個成員pageset字段指向per-cpu的高速緩存,高速緩存由struct per_cpu_pages數據結構來描述。

structper_cpu_pages{
intcount;/*numberofpagesinthelist*/
inthigh;/*highwatermark,emptyingneeded*/
intbatch;/*chunksizeforbuddyadd/remove*/

/*Listsofpages,onepermigratetypestoredonthepcp-lists*/
structlist_headlists[MIGRATE_PCPTYPES];
};

count:表示高速緩存中的頁框數量。

high :緩存中頁框數量的最大值

batch :buddy allocator增加或刪除的頁框數

lists:頁框鏈表。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 內核
    +關注

    關注

    3

    文章

    1362

    瀏覽量

    40226
  • cpu
    cpu
    +關注

    關注

    68

    文章

    10824

    瀏覽量

    211131
  • Linux
    +關注

    關注

    87

    文章

    11225

    瀏覽量

    208910

原文標題:【內存管理】頁面分配機制

文章出處:【微信號:嵌入式與Linux那些事,微信公眾號:嵌入式與Linux那些事】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Linux內存管理之頁面回收

    請求調頁機制,只要用戶態進程繼續執行,他們就能獲得頁框,然而,請求調頁沒有辦法強制進程釋放不再使用的頁框。因此,遲早所有空閑內存將被分配給進程和高速緩存,Linux內核
    發表于 05-19 14:09 ?1063次閱讀
    <b class='flag-5'>Linux</b>內存管理之<b class='flag-5'>頁面</b>回收

    cc2530網絡地址分配機制是什么樣的?

    是2.5.1a,不知道這個版本所采用的網絡地址分配機制是什么樣的?這個版本還閱讀協議棧解壓出來的document有沒有用,會不會已經過時了,那些document基本上都是zstack2007的謝謝大家了。
    發表于 04-01 10:19

    Linux內存管理的Slab分配機制

    早期Linux 的內存分配機制采用伙伴算法, 當請求分配的內存大小為幾十個字節或幾百個字節時會產生內存碎片, 嚴重消耗系統資源。現今采用Slab 機制可以緩存物理空間的申請和回
    發表于 04-24 10:49 ?11次下載

    WCDMA的鑒權和密鑰分配機制

    為了滿足第三代移動通信安全體系的要求, 詳細討論了WCDMA 系統的鑒權和密鑰分配機制,從應用的角度分析了具體的算法和參數, 如從HEöA uC 發送鑒權消息到VLRöSGSN 的過程,VLR&o
    發表于 06-02 16:29 ?35次下載

    linux內核rcu機制詳解

    Linux內核源碼當中,關于RCU的文檔比較齊全,你可以在 /Documentation/RCU/ 目錄下找到這些文件。Paul E. McKenney 是內核RCU源碼的主要實現者
    發表于 11-13 16:47 ?8751次閱讀
    <b class='flag-5'>linux</b><b class='flag-5'>內核</b>rcu<b class='flag-5'>機制</b>詳解

    linux內核機制有哪些

     在操作系統引入了進程概念,進程成為調度實體后,系統就具備了并發執行多個進程的能力,但也導致了系統各個進程之間的資源競爭和共享。另外,由于中斷、異常機制的引入,以及內核態搶占都導致了這些內核
    發表于 11-14 15:25 ?5539次閱讀
    <b class='flag-5'>linux</b><b class='flag-5'>內核</b><b class='flag-5'>機制</b>有哪些

    基于IPv6的DiffServ流標簽分配機制

    本文在DiffServ模型的基礎上,改變了路由器的功能,使用IPv6流標簽字段和源地址作為關鍵字,在路由器建立轉發表,根據關鍵字轉發IPv6分組,提高轉發速度;同時,在DS區域使用一種流標簽分配機制,避免了流標簽的重復和混亂。
    發表于 12-12 19:05 ?0次下載
    基于IPv6的DiffServ流標簽<b class='flag-5'>分配機制</b>

    基于分簇的資源分配機制

    針對Macro-Femto網絡Femtocell與Macrocell之間的干擾問題,提出了一種基于分簇的資源分配機制。該機制結合圖論及凸優化理論對毫微微節點分簇;隨后采用基于速率公平的子信道
    發表于 01-26 17:58 ?0次下載
    基于分簇的資源<b class='flag-5'>分配機制</b>

    你了解過Linux內核的Device Mapper 機制

    Device mapper 是 Linux 2.6 內核中提供的一種從邏輯設備到物理設備的映射框架機制,在該機制下,用戶可以很方便的根據自己的需要制定實現存儲資源的管理策略,當前比較流
    發表于 04-29 15:25 ?738次閱讀

    比特幣分配機制最公平的原因是什么

    比特幣協議中最早設計的分配機制至今仍然是最公平、也是最可靠的。
    發表于 07-19 14:59 ?2126次閱讀

    基于拓撲結構與分配機制的PoW共識機制

    對經典的PoW共識機制進行改進,改變了礦工所挖出區塊接入主鏈的條件和收益分配策略,從而提出了一種改進共識機制。與PoW不同,在該改進共識機制
    發表于 05-31 15:48 ?3次下載

    基于權值和基于夏普利值的圖像酬勞分配機制

    基于權值和基于夏普利值的圖像酬勞分配機制
    發表于 06-24 15:35 ?43次下載

    Linux內核文件Cache機制

    Linux內核文件Cache機制(開關電源技術與設計 第二版)-Linux內核文件Cache機制
    發表于 08-31 16:34 ?4次下載
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>文件Cache<b class='flag-5'>機制</b>

    Linux內核之塊分配

    為了解決小塊內存的分配問題,Linux 內核提供了塊分配器,最早實現的塊分配器是SLAB 分配
    的頭像 發表于 07-27 09:35 ?1598次閱讀

    jemalloc分配機制的介紹及其優化實踐

    C/C++通過libc做內存分配。glibc默認的分配機制是ptmalloc。除此之外,還有眾多的不同側重的優化,例如tcmalloc,jemalloc。
    的頭像 發表于 05-30 09:12 ?1123次閱讀
    jemalloc<b class='flag-5'>分配機制</b>的介紹及其優化實踐