Glibc堆管理機制基礎


最近正在學習linux下堆的管理機制,收集了書籍和網絡上的資料,以自己的理解做了整理,做個記錄。如果有什么不對的地方歡迎指出!

Memory Allocator

常見的內存管理機制

  • dlmalloc:通用分配器
  • ptmalloc2:glibc分配器,繼承自dlmalloc,並提供了多線程支持,主要研究對象。
  • jemalloc:Firefox
  • tcmalloc:Chrome
  • 其他:編程語言內存分配及回收,比如python
  • ......

malloc工作機制

第一次調用malloc

內存分配機制

頭文件:#include<unistd.h>

  • brk()
  1. 函數原型:int brk(void* end_data_segment)
  2. 功能和作用:用於設置program_break指向的位置。
  • sbrk()
  1. 函數原型:void* sbrk(intptr_t increment)
  2. 功能和作用:同brk(),參數可以是負數。執行成功返回上一次program_break的值,可以設置參數為0返回當前的program_break.
  • mmap()
  1. 功能和作用:當用戶申請空間大於等於128kb,也就是0x20000字節時,不再使用brk()進行分配,改為使用mmap()。
  • unmmap()
  1. 功能和作用:堆mmap()申請的空間進行回收。
    內存分配圖

  • 主線程的arena就是main_arena,包含start_brk和brk中間的連續內存,當main_arena不夠分配時,會使用brk()進行擴展。
  • 子線程arena可以有多片連續內存,但是大小是固定的,不可以擴展,如果不夠用的話需要再次調用mmap()來分配。

第二次調用malloc

  • 只要分配的空間不超過128kb,則不會再次向system申請空間,超過時才會調用brk()進行擴展。
  • 即使將main_arena全部free,也不會立即把內存還給操作系統,此時內存由glib進行管理。

chunk

chunk是glibc管理內存的基本單元。主要分為以下幾類:

  • alloced chunk:已分配正在使用中的chunk。
  • free chunk:已經free的chunk。
  • top chunk:可以理解為地址的最高處,還沒有分配的chunk。
  • last remainder chunk:是為了提高內存分配的局部性。

chunk = chunk header + user data,malloc返回給用戶的其實是user data指針,具體如下圖:

alloced chunk結構

  • size:本chunk的大小,包括prev,大小為8的整數倍。32位以8字節對齊,最小為0x10。64位以16字節對齊,最小為0x20。其中低三位有特殊含義,分別為N、M、P
  • N位:是否屬於主進程。
  • M位:是否由mmap()分配。
  • P位:前一堆塊占用標志,1為占用,0為空閑。
  • 當P位為0時,表示前一堆塊釋放,prev表示前一堆塊的大小。當P位為1,表示前一堆塊使用,prev表示前一堆塊的數據。
  • userdata為輸入的數據。
  • 將下一堆塊的P位設置為1。

free chunk

  • 其中fd、bk屬於鏈表指針,有特殊用途,后面會講到。
  • prev_size為當前釋放塊的大小(包含chunk header)
  • 下一堆塊P位通常被設置為0(fastbin除外)。

top chcunk

  • 該堆塊位於前兩種堆塊之后,頭部結構與alloced相似
  • size:top chunk還有多少空間可以分配。
  • 重要的是P位:0表示上一堆塊處於空閑,1表示上一堆塊處於使用狀態。主要用於判斷free時是否能與上一堆塊進行合並(fastbin除外)。

last remainder chunk

  • 在malloc時,如果有比較大的chunk可以分配,會把這個chunk分成兩部分,一部分返回給用戶,另一部分稱為remainder,加入到 unsorted bin,last remainder會記錄最近拆分的remainder。這個remainder大小至少要為MINSIZE,否則不能拆分。
  • 當下次malloc時,如果last remainder chunk夠大,則重復上一過程。
  • 拆分的情況:fast bin 和 small bin 都沒有合適的chunk,同時unsorted bin有且只有一個可拆分的chunk,並且這個chunk 是last remainder。

堆空閑塊管理結構bin

當alloced chunk被釋放后,會根據大小放入bin或者合並到top chunk 中去。bin的主要作用時加快分配速度,通過鏈表方式(chunk中的fd和bk指針)進行管理。主要有以下幾種,顧名思義:

  • fast bin
  • unsorted bin
  • small bin
  • large bin

fastbinsY:這是一個bin數組,里面有NFASTBINS個fast bin

bins:也是一個bin數組,一共有126個bin,按順序分別是:

  • bin 1 為unsorted bin
  • bin 2 到 bin 63 為small bin
  • bin 64 到 bin 126 為 large bin

fast bin

  • 這類bin通常申請和釋放的堆塊都比較小,所以使用單鏈表結構,LIFO(后進先出)分配策略。
  • 為了速度,fast bin不會進行合並,下一個chunk始終處於使用狀態。
  • 在fastbinsY數組里按照從小到大的順序排列。
  • 以64位為例,fast bin結構如下(大小區間0x200x80,32位為0x100x40):

unsorted bin

  • 一定大小堆塊被釋放時,在加入small bin 和large bin 之前,會首先加入此bin,可以加快分配速度。使用雙鏈表結構,FIFO(先進先出)分配策略。
  • unsorted bin大小可能是不相同的。
  • 由於使用雙鏈表,一個bin會占用bins的兩個元素。fd指向上一個chunk,bk指向下一個。
  • 以64位為例,unsorted bin結構如下(非連續內存,大小無限制):

small bin

  • 同一個small bin里的chunk大小相同,使用雙鏈表結構,FIFO(先進先出)分配策略。
  • 由於fast bin和small bin 有重合部分,在某些情況下會加入到small bin
  • 根據大小分成62個不同的bin,0x20,0x30,0x40...0x80,0x90...1008
  • 以64位為例,small bin結構如下(大小區間:size<0x400byte):

large bin

  • 使用雙鏈表結構,FIFO(先進先出)分配策略。
  • free時bk后面多兩個此參數:fd_nextsize、bk_nextsize。分別指向前一個和后一個large chunk。
  • 根據大小分成63個不同的bin,大小不再固定。前32個bin為 0x400+64i,32-48 bin為 0x1380+512j,依此類推。並且會將大的chunk放在前面,小的放在后面,以加快速度。
  • 以64位為例,large bin大小區間:size>=1024byte。32位為:size>=512byte。
  • fd_nextsize和bk_nextsize指針用於指向第一個與自己大小不同的chunk,所以也只有在加入了大小不同的chunk時,這兩個指針才會被修改。

隨后附上glibc內存管理流程圖

看不清楚可以保存下來放大。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM