1. 程式人生 > >Linux中動態記憶體的分配與回收(heap, buddy system, stab)

Linux中動態記憶體的分配與回收(heap, buddy system, stab)

夥伴系統演算法

  在實際應用中,經常需要分配一組連續的頁框,而頻繁地申請和釋放不同大小的連續頁框,必然導致在已分配頁框的記憶體塊中分散了許多小塊的 空閒頁框常見的Linux核心中記憶體分配 - leon - 我的奮鬥這樣,即使這些頁框是空閒的,其他需要分配連續頁框的應用也很難得到滿足常見的Linux核心中記憶體分配 - leon - 我的奮鬥

  為了避免出現這種情況,Linux核心中引入了夥伴系統演算法(buddy system)常見的Linux核心中記憶體分配 - leon - 我的奮鬥把所有的空閒頁框分組為11個 塊連結串列,每個塊連結串列分別包含大小為1,2,4,8,16,32,64,128,256,512和1024個連續頁框的頁框塊常見的Linux核心中記憶體分配 - leon - 我的奮鬥最大可以申請1024個連 續頁框,對應4MB大小的連續記憶體常見的Linux核心中記憶體分配 - leon - 我的奮鬥每個頁框塊的第一個頁框的實體地址是該塊大小的整數倍常見的Linux核心中記憶體分配 - leon - 我的奮鬥

  假設要申請一個256個頁框的塊,先從256個頁框的連結串列中查詢空閒塊,如果沒有,就去512個 頁框的連結串列中找,找到了則將頁框塊分為2個256個 頁框的塊,一個分配給應用,另外一個移到256個頁框的連結串列中常見的Linux核心中記憶體分配 - leon - 我的奮鬥

如果512個頁框的連結串列中仍沒有空閒塊,繼續向1024個頁 框的連結串列查詢,如果仍然沒有,則返回錯誤常見的Linux核心中記憶體分配 - leon - 我的奮鬥

  頁框塊在釋放時,會主動將兩個連續的頁框塊合併為一個較大的頁框塊常見的Linux核心中記憶體分配 - leon - 我的奮鬥

  1.2.slab分 配器

  slab分配器源於 Solaris 2.4 的 分配演算法,工作於物理記憶體頁框分配器之上,管理特定大小物件的快取,進行快速而高效的記憶體分配常見的Linux核心中記憶體分配 - leon - 我的奮鬥

  slab分配器為每種使用的核心物件建立單獨的緩衝區常見的Linux核心中記憶體分配 - leon - 我的奮鬥Linux 核心已經採用了夥伴系統管理實體記憶體頁框,因此 slab分配器直接工作於夥伴系 統之上常見的Linux核心中記憶體分配 - leon - 我的奮鬥每種緩衝區由多個 slab 組成,每個 slab就是一組連續的實體記憶體頁框,被劃分成了固定數目的物件常見的Linux核心中記憶體分配 - leon - 我的奮鬥

根據物件大小的不同,預設情況下一個 slab 最多可以由 1024個頁框構成常見的Linux核心中記憶體分配 - leon - 我的奮鬥出於對齊 等其它方面的要求,slab 中分配給物件的記憶體可能大於使用者要求的物件實際大小,這會造成一定的 記憶體浪費常見的Linux核心中記憶體分配 - leon - 我的奮鬥

  2.常用記憶體分配函式

  2.1.__get_free_pages

  unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)

  __get_free_pages函式是最原始的記憶體分配方式,直接從夥伴系統中獲取原始頁框,返回值為第一個頁框的起始地址常見的Linux核心中記憶體分配 - leon - 我的奮鬥__get_free_pages在實現上只是封裝了alloc_pages函 數,從程式碼分析,alloc_pages函式會分配長度為1<<order的 連續頁框塊常見的Linux核心中記憶體分配 - leon - 我的奮鬥

order引數的最大值由include/Linux/Mmzone.h文 件中的MAX_ORDER巨集決定,在預設的2.6.18內 核版本中,該巨集定義為10常見的Linux核心中記憶體分配 - leon - 我的奮鬥也就是說在理論上__get_free_pages函 數一次最多能申請1<<10 * 4KB也就是4MB的 連續實體記憶體常見的Linux核心中記憶體分配 - leon - 我的奮鬥但是在實際應用中,很可能因為不存在這麼大量的連續空閒頁框而導致分配失敗常見的Linux核心中記憶體分配 - leon - 我的奮鬥測試中,order為10時分配成功,order為11則返回錯誤常見的Linux核心中記憶體分配 - leon - 我的奮鬥

  2.2.kmem_cache_alloc

  struct kmem_cache *kmem_cache_create(const char *name, size_t size,

  size_t align, unsigned long flags,

  void (*ctor)(void*, struct kmem_cache *, unsigned long),

  void (*dtor)(void*, struct kmem_cache *, unsigned long))

  void *kmem_cache_alloc(struct kmem_cache *c, gfp_t flags)

  kmem_cache_create/ kmem_cache_alloc是基於slab分配器的一種記憶體分配方式,適用於反覆分配釋放同一大小記憶體塊的場合常見的Linux核心中記憶體分配 - leon - 我的奮鬥首先用kmem_cache_create建立一個快取記憶體區域,然後用kmem_cache_alloc從 該快取記憶體區域中獲取新的記憶體塊常見的Linux核心中記憶體分配 - leon - 我的奮鬥 kmem_cache_alloc一次能分配的最大記憶體由mm/slab.c檔案中的MAX_OBJ_ORDER巨集 定義,在預設的2.6.18核心版本中,該巨集定義為5, 於是一次最多能申請1<<5 * 4KB也就是128KB的 連續實體記憶體常見的Linux核心中記憶體分配 - leon - 我的奮鬥分析核心原始碼發現,kmem_cache_create函式的size引數大於128KB時會呼叫BUG()常見的Linux核心中記憶體分配 - leon - 我的奮鬥測試結果驗證了分析結果,用kmem_cache_create分 配超過128KB的記憶體時使核心崩潰常見的Linux核心中記憶體分配 - leon - 我的奮鬥

  2.3.kmalloc

  void *kmalloc(size_t size, gfp_t flags)

  kmalloc是核心中最常用的一種記憶體分配方式,它通過呼叫kmem_cache_alloc函 數來實現常見的Linux核心中記憶體分配 - leon - 我的奮鬥kmalloc一次最多能申請的記憶體大小由include/Linux/Kmalloc_size.h的 內容來決定,在預設的2.6.18核心版本中,kmalloc一 次最多能申請大小為131702B也就是128KB字 節的連續實體記憶體常見的Linux核心中記憶體分配 - leon - 我的奮鬥測試結果表明,如果試圖用kmalloc函式分配大於128KB的記憶體,編譯不能通過常見的Linux核心中記憶體分配 - leon - 我的奮鬥

  2.4.vmalloc

  void *vmalloc(unsigned long size)

  前面幾種記憶體分配方式都是物理連續的,能保證較低的平均訪問時間常見的Linux核心中記憶體分配 - leon - 我的奮鬥但是在某些場合中,對記憶體區的請求不是很頻繁,較高的記憶體訪問時間也 可以接受,這是就可以分配一段線性連續,物理不連續的地址,帶來的好處是一次可以分配較大塊的記憶體常見的Linux核心中記憶體分配 - leon - 我的奮鬥圖3-1表 示的是vmalloc分配的記憶體使用的地址範圍常見的Linux核心中記憶體分配 - leon - 我的奮鬥vmalloc對 一次能分配的記憶體大小沒有明確限制常見的Linux核心中記憶體分配 - leon - 我的奮鬥出於效能考慮,應謹慎使用vmalloc函式常見的Linux核心中記憶體分配 - leon - 我的奮鬥在測試過程中, 最大能一次分配1GB的空間常見的Linux核心中記憶體分配 - leon - 我的奮鬥

  Linux核心部分記憶體分佈

  2.5.dma_alloc_coherent

  void *dma_alloc_coherent(struct device *dev, size_t size,

  ma_addr_t *dma_handle, gfp_t gfp)

  DMA是一種硬體機制,允許外圍裝置和主存之間直接傳輸IO資料,而不需要CPU的參與,使用DMA機制能大幅提高與裝置通訊的 吞吐量常見的Linux核心中記憶體分配 - leon - 我的奮鬥DMA操作中,涉及到CPU高速緩 存和對應的記憶體資料一致性的問題,必須保證兩者的資料一致,在x86_64體系結構中,硬體已經很 好的解決了這個問題, dma_alloc_coherent和__get_free_pages函式實現差別不大,前者實際是呼叫__alloc_pages函 數來分配記憶體,因此一次分配記憶體的大小限制和後者一樣常見的Linux核心中記憶體分配 - leon - 我的奮鬥__get_free_pages分配的內 存同樣可以用於DMA操作常見的Linux核心中記憶體分配 - leon - 我的奮鬥測試結果證明,dma_alloc_coherent函 數一次能分配的最大記憶體也為4M常見的Linux核心中記憶體分配 - leon - 我的奮鬥

  2.6.ioremap

  void * ioremap (unsigned long offset, unsigned long size)

  ioremap是一種更直接的記憶體“分配”方式,使用時直接指定物理起始地址和需要分配記憶體的大小,然後將該段 實體地址對映到核心地址空間常見的Linux核心中記憶體分配 - leon - 我的奮鬥ioremap用到的實體地址空間都是事先確定的,和上面的幾種記憶體 分配方式並不太一樣,並不是分配一段新的實體記憶體常見的Linux核心中記憶體分配 - leon - 我的奮鬥ioremap多用於裝置驅動,可以讓CPU直接訪問外部裝置的IO空間常見的Linux核心中記憶體分配 - leon - 我的奮鬥ioremap能對映的記憶體由原有的實體記憶體空間決定,所以沒有進行測試常見的Linux核心中記憶體分配 - leon - 我的奮鬥

  2.7.Boot Memory

  如果要分配大量的連續實體記憶體,上述的分配函式都不能滿足,就只能用比較特殊的方式,在Linux內 核引導階段來預留部分記憶體常見的Linux核心中記憶體分配 - leon - 我的奮鬥

  2.7.1.在核心引導時分配記憶體

  void* alloc_bootmem(unsigned long size)

  可以在Linux核心引導過程中繞過夥伴系統來分配大塊記憶體常見的Linux核心中記憶體分配 - leon - 我的奮鬥使用方法是在Linux核心引導時,呼叫mem_init函式之前 用alloc_bootmem函式申請指定大小的記憶體常見的Linux核心中記憶體分配 - leon - 我的奮鬥如果需要在其他地方呼叫這塊記憶體,可以將alloc_bootmem返回的記憶體首地址通過EXPORT_SYMBOL導 出,然後就可以使用這塊記憶體了常見的Linux核心中記憶體分配 - leon - 我的奮鬥這種記憶體分配方式的缺點是,申請記憶體的程式碼必須在連結到核心中的程式碼裡才能使用,因此必須重新編譯核心,而且記憶體管理系統 看不到這部分記憶體,需要使用者自行管理常見的Linux核心中記憶體分配 - leon - 我的奮鬥測試結果表明,重新編譯核心後重啟,能夠訪問引導時分配的記憶體塊常見的Linux核心中記憶體分配 - leon - 我的奮鬥

  2.7.2.通過核心引導引數預留頂部記憶體

  在Linux核心引導時,傳入引數“mem=size”保留頂部的記憶體區間常見的Linux核心中記憶體分配 - leon - 我的奮鬥比如系統有256MB內 存,引數“mem=248M”會預留頂部的8MB記憶體,進入系統後可以呼叫ioremap(0xF800000,0x800000)來申請這段記憶體常見的Linux核心中記憶體分配 - leon - 我的奮鬥

  3.幾種分配函式的比較

分配原理

最大記憶體

其他

__get_free_pages

直接對頁框進行操作

4MB

適用於分配較大量的連續實體記憶體

kmem_cache_alloc

基於slab機制實現

128KB

適合需要頻繁申請釋放相同大小記憶體塊時使用

kmalloc

基於kmem_cache_alloc實現

128KB

最常見的分配方式,需要小於頁框大小的記憶體時可以使用

vmalloc

建立非連續實體記憶體到虛擬地址的對映

物理不連續,適合需要大記憶體,但是對地址連續性沒有要求的場合

dma_alloc_coherent

基於__alloc_pages實現

4MB

適用於DMA操 作

ioremap

實現已知實體地址到虛擬地址的對映

適用於實體地址已知的場合,如裝置驅動

alloc_bootmem

在啟動kernel時,預留一段記憶體,核心看不見

小於實體記憶體大小,記憶體管理要求較高


相關推薦

Linux動態記憶體分配回收(heap, buddy system, stab)

夥伴系統演算法   在實際應用中,經常需要分配一組連續的頁框,而頻繁地申請和釋放不同大小的連續頁框,必然導致在已分配頁框的記憶體塊中分散了許多小塊的 空閒頁框這樣,即使這些頁框是空閒的,其他需要分配連續頁框的應用也很難得到滿足   為了避免出現這種情況,Linux核心中引入了夥伴系統演算法(buddy

動態開點線段樹(多棵線段樹)的記憶體分配回收

前言 線段樹,是一個很好用的能支援O(logn)區間操作的資料結構,隨著做一些稍微煩一點的題,有時候會發現有些情況要開一個數組的線段樹,更有甚者要樹套樹,而在很多情況下線段樹就不能把所有點都開滿了(否則會MLE記憶體超限),於是就出現了線段樹的動態開點寫法 基本思想 與普通的

我理解的堆疊(stack)、動態記憶體分配堆(heap

                   看到第4章,首次接觸到堆(heap)這個概念,不好理解,所以用vs2010反彙編跟蹤下程式: // use_new.cpp -- using the new operator #include <iostream> int

JVM記憶體分配回收學習(2)

1、垃圾收集器什麼時候開始回收? (1)新生代有一個Eden區和兩個survivor區(From survivor 和To Survivor),每次使用Eden和其中一個Survivor(From Survivor),建立物件時,首先會將物件放入Eden區,如果放不下就會引發一次發生在新生代

動態記憶體分配釋放(陳銳、葛麗萍 編著《跟我學資料結構》整理)

1.malloc函式 作用:在記憶體中分配一個長度為size 的連續儲存空間,返回一個指向分配空間的起始地址的指標。          如果分配失敗,則返回NULL。 原型: void *malloc(unsigned int size)

垃圾收集器記憶體分配策略(六)——記憶體分配回收策略

物件的記憶體分配,往大方向上講,就是在堆上分配(但也可能經過JIT編譯後被拆散為標量型別並間接地棧上分配),物件主要分配在新生代的Eden區上,如果啟動了本地執行緒分配緩衝,將按執行緒優先在TLAB上分配。少數情況下也可能會直接分配在老年代中,分配的規則並不是百分之百固定的,

JVM:GC-記憶體分配回收策略

物件優先在Eden區分配 物件優先在eden區分配,當eden區沒有足夠空間分配記憶體時,就會發現minor gc. 程式碼例項: public class Main { static int _1M = 1024*1024; //vm 引數 // -ver

簡單的動態記憶體分配釋放

掌握動態記憶體分配方法。 程式設計輸入學生人數n及某門課成績,計算並輸出其平均分的整數部分(請用malloc和free進行動態記憶體分配)。 input: 5 90 80 70 60 50 output: 70 #include <iostream> #include <mal

JVM六:記憶體分配回收策略

對於物件的回收,前面以及講過具體的回收機制,下面我們來看看物件的分配策略! ①物件優先在Eden區域分配 大多數情況下,物件在新生代Eden區分配。當Eden區沒有足夠空間進行分配時,虛擬機器將發起一次Minor GC。 虛擬機器提供了-XX:PrintGCDetails這個收集日誌引數

JVM之記憶體分配回收策略

JVM之記憶體分配與回收策略 來源 https://www.cnblogs.com/xiaoxi/p/6557473.html   JVM分代垃圾回收策略的基礎概念 來源 https://www.cnblogs.com/xiaoxi/p/6602166.html 一、為

Linux記憶體分配和釋放之slab分配器分析(完)

        我們在上篇文章分析cache_grow()函式的時候涉及兩個函式,我們沒有細說。一個就是kmem_getpages()和kmem_freepages()函式,這兩個函式有3個引數。kmem_cahce_t:主要是把申請到的物件加到這個快取記憶體內   flag

java虛擬機器-記憶體分配回收策略

1.物件優先在Eden分配 大多數情況下,物件在新生代Eden區分配。當Eden區沒有足夠空間進行分配時,虛擬機器將發起一次Minor GC。 虛擬機器提供了-XX:+PrintGCDetails這個收集器日誌引數,告訴虛擬機器在傳送垃圾收集行為時列印記憶體回收日誌,並且在

Java虛擬機器(三):記憶體分配回收策略

[GC[DefNew Desired survivor size 524288 bytes, new threshold 1 (max 1) - age 1: 748104 bytes, 748104 total : 5024K->730K(9216K), 0.0038710 sec

C語言之動態記憶體分配釋放

一,堆記憶體 1,堆記憶體特點 堆記憶體可以存放任意型別的資料,但需要自己申請與釋放。 2,堆大小 堆大小,想像中的無窮大,但實際使用中,受限於實際記憶體的大小和記憶體是否有連續性。 二,堆記憶體的申請與釋放 1,malloc函式

記憶體分配回收策略(五)

回收策略: Minor GC Major GC/Full GC Minor GC:發生在新生代的GC,發生非常頻繁,消耗時間短。 Major GC:發生在老年代GC,消耗時間一般為新生代GC的10倍,甚至更多(1000倍)。 Full GC:新生代+老年代 GC。

Linux C/C++ 記憶體分配釋放 [摘抄整理]

 no malloc no free no new no delete 寫了一個簡單類,執行的時候報了個錯 ,下決心好好看下記憶體相關知識 class ConstChar{ public: ConstChar(const char *data, int size)

記憶體分配回收策略

物件的記憶體分配,往大方向上講,就是在堆上分配(但也可能經過JIT編譯後被拆散為標量型別並間接的在棧上分配),物件主要分配在新生代的Eden區上,如果啟動了本地執行緒分配緩衝,將按執行緒優先在TLAB上分配。少數情況下也可能會直接分配在老年代中,分配的規則

JVM 記憶體分配回收策略

Java 中,物件的記憶體分配,大的方向講,就是在堆上分配。物件主要分配在新生代的eden區上。 記憶體分配規則: 大多數情況,記憶體在新生代eden區中分配,當eden區沒有足夠空間進行分配的時候,虛擬機器將發起一次minor GC 大物件直接

實體記憶體分配回收(2)

1.物理頁面的分配 函式__get_free_pages用於物理頁塊的分配,其定義如下: unsigned long __get_free_pages(int gfp_mask, unsigned long order); 其中g

JVM_8_記憶體分配回收策略

記憶體分配與回收策略 參考資料: 《Java虛擬機器垃圾回收(四) 總結:記憶體分配與回收策略 方法區垃圾回收 以及 JVM垃圾回收的調優方法》 在之前看"分代收集演算法"的時候,我們知道目前幾乎所有商業虛擬機器的垃圾收集器都採用分代收集演算法,對於Hotspot虛擬機