偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Glibc堆內存管理:原理、機制與實戰(zhàn)

系統(tǒng) Linux
GlibC Malloc 作為 Linux 環(huán)境下被廣泛使用的內存分配器,其運作機制極其復雜且微妙。GlibC Malloc for Exploiters 項目就像一把精準的手術刀,深入到 GlibC 的內部,清晰地揭示了 malloc 與 free 函數(shù)是如何管理堆內存的,尤其是在那些易受攻擊的場景下,它們的行為模式更是被剖析得淋漓盡致。

在內存管理領域,glibc(GNU C Library)通過 brk 和 mmap 兩大系統(tǒng)調用,構建了一套高效的堆內存管理機制。這種設計大幅減少了系統(tǒng)調用的頻次,顯著提升內存利用率。在 glibc 的管理架構中,堆內存以層級化的方式組織,包含分配區(qū)(Arena)、堆(Heap)和內存塊(Chunk)。其中,主 Arena 依賴 brk 系統(tǒng)調用實現(xiàn)內存分配,而子 Arena 則通過 mmap 完成內存獲取。在多線程程序運行時,每個線程通常會擁有專屬的 Arena,主線程與子線程的堆空間各自獨立管理,互不干擾。

內存塊在運行過程中存在空閑與已使用兩種狀態(tài),glibc 通過 fast bins、small bins、large bins 和 unsorted bin 四類數(shù)據(jù)結構,對內存塊進行有序組織,從而加速內存的分配與釋放操作。在具體實現(xiàn) malloc 和 free 函數(shù)時,glibc 遵循 “先小后大,最佳適配” 原則,同時還會根據(jù)實際需求動態(tài)擴展堆空間,并采用合理的內存釋放策略,確保內存資源的高效利用。

一、Glibc 堆內存管理:為何重要?

在編程的世界里,內存就像是程序運行的舞臺,每一個變量、每一段數(shù)據(jù)都在這個舞臺上登場、表演和落幕。而 Glibc 堆內存管理,無疑是這場演出中至關重要的幕后導演,它默默掌控著內存的分配與釋放,確保程序能夠順利運行。

在實際開發(fā)中,內存相關的問題屢見不鮮,它們就像隱藏在暗處的 “定時炸彈”,隨時可能給程序帶來嚴重的影響。程序崩潰便是其中一個常見的問題。想象一下,你精心編寫的程序在運行一段時間后突然崩潰,所有的努力瞬間化為泡影,這是多么令人沮喪的事情。而內存泄漏往往是導致程序崩潰的罪魁禍首之一。當程序中分配的內存沒有被正確釋放時,隨著時間的推移,可用內存會越來越少,最終導致系統(tǒng)內存耗盡,程序不得不終止運行。這種情況在長時間運行的服務器程序中尤為常見,一個小小的內存泄漏可能會在不知不覺中引發(fā)服務器的崩潰,給用戶帶來極大的不便。

除了程序崩潰,性能下降也是內存問題的一個重要表現(xiàn)。當內存管理不善時,程序可能會頻繁地進行內存分配和釋放操作,這會導致內存碎片化。內存碎片化就像是一個雜亂無章的倉庫,雖然倉庫的總容量足夠,但由于物品擺放混亂,需要使用某個物品時卻很難快速找到,從而降低了程序的運行效率。在一些對性能要求極高的應用場景中,如游戲開發(fā)、大數(shù)據(jù)處理等,內存碎片化可能會導致游戲卡頓、數(shù)據(jù)處理速度變慢,嚴重影響用戶體驗。

Glibc 堆內存管理在編程中占據(jù)著關鍵地位,它直接關系到程序的穩(wěn)定性和性能。如果把程序比作一輛汽車,那么內存就是汽車的燃油,而 Glibc 堆內存管理則是汽車的燃油噴射系統(tǒng),它能夠精準地控制燃油的供應,確保汽車能夠高效、穩(wěn)定地行駛。只有深入理解并合理運用 Glibc 堆內存管理,才能編寫出高質量、高性能的程序,避免內存問題帶來的種種困擾。

二、Glibc 堆內存管理基礎

2.1進程內存布局

在計算機系統(tǒng)中,進程的內存布局就像是一個精心規(guī)劃的城市,不同的區(qū)域承擔著不同的功能。對于 32 位系統(tǒng)而言,其進程內存布局有著獨特的結構。整個內存空間就像一座擁有不同功能分區(qū)的大廈,棧區(qū)位于大廈的較高樓層,它由編譯器自動分配釋放,主要存放函數(shù)的參數(shù)值、局部變量的值等。棧區(qū)就像是一個臨時的物資存放點,隨著函數(shù)的調用和結束,物資(數(shù)據(jù))不斷地進出。它從高地址向低地址生長,就像樓層從高往低依次被占用。

圖片圖片

堆區(qū)則位于大廈的較低樓層,它是動態(tài)內存分配區(qū)域,通過malloc、new、free和delete等函數(shù)來管理,一般由程序員分配釋放,若程序員不釋放,程序結束時可能由系統(tǒng)回收 。堆區(qū)就像是一個可以自由擴建的倉庫,其大小受限于計算機系統(tǒng)中有效的虛擬內存,向高地址擴展,由于系統(tǒng)是用鏈表來存儲空閑內存地址的,所以它是不連續(xù)的內存區(qū)域。

數(shù)據(jù)區(qū)存放著在源代碼中有預定義值的全局變量和靜態(tài)變量,它就像是大廈中存放重要物資儲備的區(qū)域,這些物資(數(shù)據(jù))在程序運行過程中有著重要的作用。

未初始化變量區(qū)(BSS)存儲未被初始化的全局變量和靜態(tài)變量,它就像是一個等待填充物資的倉庫,在程序運行前,這些變量雖然已經(jīng)分配了空間,但還沒有具體的值。

代碼區(qū)則存儲只讀的程序執(zhí)行代碼,即機器指令,它就像是大廈的控制中心,指揮著整個程序的運行。

而在 64 位系統(tǒng)中,雖然內存布局的基本概念與 32 位系統(tǒng)相似,但由于其擁有更大的虛擬地址空間,就像是一座更加龐大的大廈,在內存布局上也有一些不同之處。64 位系統(tǒng)采用與 32 位經(jīng)典內存布局相似的方式,棧區(qū)和堆區(qū)的生長方向、作用等與 32 位系統(tǒng)類似,但在地址范圍和內存管理的一些細節(jié)上有所差異。例如,64 位系統(tǒng)可以支持更大的堆空間和??臻g,這使得程序在處理大規(guī)模數(shù)據(jù)和復雜函數(shù)調用時更加從容。

2.2關鍵系統(tǒng)調用:brk 與 mmap

在 Glibc 堆內存管理的底層,brk和mmap這兩個系統(tǒng)調用扮演著舉足輕重的角色,它們就像是內存管理這座大廈的基石,為整個內存管理機制提供了底層支持。

brk系統(tǒng)調用主要用于改變進程的數(shù)據(jù)段大小,它就像是一個可以調整倉庫大小的工具。數(shù)據(jù)段是進程地址空間中存儲動態(tài)分配數(shù)據(jù)的區(qū)域,如全局變量、靜態(tài)變量、堆等。當進程需要分配更多內存時,brk系統(tǒng)調用能夠擴展進程的堆,通過將當前堆的末尾地址移動到所需內存塊的末尾地址,從而為進程提供新的內存空間。相反,當進程需要釋放已經(jīng)分配的內存時,可以通過調用brk系統(tǒng)調用,將堆的末尾地址移動回去,釋放不再需要的內存。

brk分配的內存是連續(xù)的,適合小塊內存的頻繁分配和釋放,就像一個小倉庫,對于一些小型物資(小塊內存)的存放和取出非常方便。例如,當一個程序需要頻繁地分配和釋放一些小型的數(shù)據(jù)結構時,brk系統(tǒng)調用可以高效地完成這些操作。

lang=c
#include <unistd.h>
int brk(void *addr);
void *sbrk(intptr_t increment);

mmap系統(tǒng)調用則像是一個功能強大的大型倉庫構建器,它用于在進程的虛擬地址空間中創(chuàng)建一個內存映射。它可以將文件或者設備映射到進程的地址空間,使得進程可以像訪問內存一樣訪問文件或設備。mmap的功能非常豐富,它可以創(chuàng)建匿名映射,即不與任何文件關聯(lián)的內存映射,用于在進程間共享內存,或者作為大塊內存的分配器。

它還可以用于文件映射,將一個文件的全部或部分內容映射到進程的虛擬內存中,進程可以像訪問內存一樣讀寫文件的內容,而不需要顯式地進行文件 I/O 操作,對內存的修改會自動同步到文件中,減少了數(shù)據(jù)拷貝和系統(tǒng)調用的次數(shù)。在處理大型文件時,如數(shù)據(jù)庫文件、日志文件等,mmap可以大大提高文件的讀寫效率。此外,mmap還常用于實現(xiàn)進程間通信的機制,如共享內存、消息隊列等,多個進程可以映射同一個文件或匿名映射到它們的地址空間,實現(xiàn)共享內存,從而高效地進行數(shù)據(jù)交換和同步。

lang=c
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);

在 Glibc 堆內存管理中,malloc函數(shù)在分配內存時,會根據(jù)請求的內存大小來選擇使用brk還是mmap。當請求的內存大小小于一定閾值(在大多數(shù)系統(tǒng)中,這個閾值通常為 128KB )時,malloc函數(shù)會優(yōu)先使用brk系統(tǒng)調用來分配內存,因為brk的系統(tǒng)調用開銷較小,適合頻繁的小塊內存分配;當請求的內存大小大于這個閾值時,則會使用mmap系統(tǒng)調用,因為mmap可以分配大塊連續(xù)的虛擬內存,且可以獨立管理每個內存塊,能夠滿足大塊內存的分配需求。

三、Glibc 堆內存管理核心機制

圖片圖片

3.1分配區(qū)(Arena)探秘

在 Glibc 堆內存管理的復雜體系中,分配區(qū)(Arena)就像是一個大型的內存資源調配中心,它在內存管理中扮演著至關重要的角色,是理解整個內存管理機制的關鍵所在。

Arena 本質上是一個內存區(qū)域,它通過sbrk或mmap系統(tǒng)調用為線程分配堆區(qū)。在一個進程中,Arena 分為主分配區(qū)(main arena)和非主分配區(qū)(sub - arena) 。主線程擁有自己獨立的主分配區(qū),即main_arena,它就像是內存調配中心的總部,在程序啟動時就被創(chuàng)建,并且通過sbrk系統(tǒng)調用從操作系統(tǒng)獲取內存,這些內存主要來自于進程的堆區(qū)。main_arena在整個內存管理中具有特殊地位,它管理著所有線程共享的堆內存,就像一個大型倉庫,存放著各種內存資源,等待著被分配給各個線程使用。

而對于多線程環(huán)境下的子線程,它們所對應的非主分配區(qū)則是通過mmap系統(tǒng)調用創(chuàng)建的。當線程數(shù)量較多時,并非每個線程都能擁有自己獨立的 Arena,因為 Arena 的數(shù)量是有限的,這與 CPU 核數(shù)相關。在 32 位系統(tǒng)中,Arena 數(shù)量上限 = 2核數(shù);在 64 位系統(tǒng)中,Arena 數(shù)量上限 = 8核數(shù)。當線程數(shù)量超過這個上限時,就會出現(xiàn)線程之間共享 Arena 的情況。這就好比多個工人在有限的倉庫中領取物資,當倉庫數(shù)量不足時,就需要多個工人共享一個倉庫。

當線程調用malloc申請內存時,線程會先查看線程私有變量中是否已經(jīng)存在一個分配區(qū)。如果存在,則對該分配區(qū)加鎖,加鎖成功的話就用該分配區(qū)進行內存分配;失敗的話則搜索環(huán)形鏈表找一個未加鎖的分配區(qū)。如果所有分配區(qū)都已經(jīng)加鎖,那么malloc會開辟一個新的分配區(qū)加入環(huán)形鏈表并加鎖,用它來分配內存。這種機制就像是工人在領取物資時,會先查看自己專屬的倉庫是否可用,如果不可用,就會去尋找其他空閑的倉庫,若所有倉庫都被占用,就會新建一個倉庫來存放物資。

Arena的數(shù)據(jù)結構:

struct malloc_state
{
  /* Serialize access.  */
  mutex_t mutex;//互斥量,用于多線程共享一個Arena

  /* Flags (formerly in max_fast).  */
  int flags;

#if THREAD_STATS
  /* Statistics for locking.  Only used if THREAD_STATS is defined.  */
  long stat_lock_direct, stat_lock_loop, stat_lock_wait;
#endif


/* 回收箱:fastbins,bins */
  /* Fastbins */
  mfastbinptr fastbinsY[NFASTBINS];

  /* Base of the topmost chunk -- not otherwise kept in a bin */
  mchunkptr top;//指向當前top chunk

  /* The remainder from the most recent split of a small request */
  mchunkptr last_remainder;

  /* Normal bins packed as described above */
  mchunkptr bins[NBINS * 2 - 2];

  /* Bitmap of bins */
  unsigned int binmap[BINMAPSIZE];//位圖,標記bins中是否存在內存塊

/* Arena被連成鏈表 */

  /* Linked list */
  struct malloc_state *next;

  /* Linked list for free arenas.  */
  struct malloc_state *next_free;

  /* Memory allocated from the system in this arena.  */
  INTERNAL_SIZE_T system_mem;
  INTERNAL_SIZE_T max_system_mem;
};

3.2堆(Heap)的分類與管理

堆,作為內存管理中的重要組成部分,就像是一個巨大的物資儲備庫,它為程序提供了動態(tài)分配內存的區(qū)域。在 Glibc 堆內存管理中,堆主要分為兩類,即主 Arena 的堆和子 Arena 的堆,它們各自有著獨特的特點和管理方式。

lang=c
/* 該數(shù)據(jù)結構只在子Arena中使用,用于記錄當前堆信息。 */
typedef struct _heap_info
{
  mstate ar_ptr; /* Arena for this heap. */ // 指向該堆所在的Arena
  struct _heap_info *prev; /* Previous heap. */ //由于一個子Arena管理多個堆,因此
  size_t size;   /* Current size in bytes. */ //當前堆分配給用戶使用的大小,剩余部分為預留區(qū)域
  size_t mprotect_size; /* Size in bytes that has been mprotected
                           PROT_READ|PROT_WRITE.  */ //從代碼來看,和size并無區(qū)別(本人意見)
  /* Make sure the following data is properly aligned, particularly
     that sizeof (heap_info) + 2 * SIZE_SZ is a multiple of
     MALLOC_ALIGNMENT. */
  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; //用于對齊
} heap_info;

主 Arena 的堆是通過brk系統(tǒng)調用從操作系統(tǒng)獲取內存,它只有一個,就像一個大型的中央倉庫,位于進程地址空間的特定區(qū)域,從低地址向高地址增長。主 Arena 的堆在初始化時,其大小通常是一個較小的值,但隨著程序運行過程中對內存的不斷需求,它可以通過brk系統(tǒng)調用動態(tài)擴展。例如,當一個程序需要分配更多內存時,brk系統(tǒng)調用會將堆的末尾地址移動到所需內存塊的末尾地址,從而為程序提供新的內存空間。這就好比中央倉庫在物資不足時,可以通過擴建來增加存儲容量。主 Arena 的堆在內存管理中承擔著重要的角色,它是許多小型內存分配的主要來源,由于其內存分配和釋放的操作相對頻繁,因此需要高效的管理機制來確保內存的合理使用。

子 Arena 的堆則是通過mmap系統(tǒng)調用創(chuàng)建的,與主 Arena 的堆不同,子 Arena 的堆可以有多個,并且這些堆之間通過鏈表進行連接。這就像是多個分散的小型倉庫,每個倉庫都有自己獨立的管理方式。子 Arena 的堆通常用于滿足一些特殊的內存分配需求,或者在多線程環(huán)境下,為不同的線程提供獨立的內存分配空間,以減少線程之間的內存競爭。當一個子 Arena 的堆空間用盡時,會申請新的堆,并將其加入到鏈表中,就像小型倉庫物資不足時,會新建倉庫并與原有倉庫連接起來。

①堆的申請

第一類的堆無需申請,只是調用brk進行堆邊界的拓展即可。這里主要對第二類堆的申請進行說明。

  1. 堆的大小和對齊:第二類堆在申請時,總是mmap大小為HEAP_MAX_SIZE的內存,多出來的部分將作為預留空間,防止頻繁申請。并且使其首地址對齊于HEAP_MAX_SIZE,這可以方便找到堆的起始地址。
  2. 什么時候申請堆:在兩種情況會進行第二類堆的申請,第一種情況是在創(chuàng)建子Arena時,會相應地進行堆的申請作為該Arena的第一個堆;第二種情況是在原來申請的堆已經(jīng)分配完畢時,會重新進行堆的申請,并將該堆和原來的堆通過鏈表連接起來。
  3. 堆的可用部分:只將用戶所需要的部分分配出去,并使用size記錄,剩下的部分作為預留。

②堆的釋放

這里堆的釋放是指glibc將申請的堆內存歸還給內核。

對于第一類堆,可以認為只有堆大小的縮減,當堆的頂部空閑的內存滿足一定條件時,可以通過brk將堆的邊界下移,top chunk指向地址不變,但大小變小了。

對于第二類堆,當一個堆中的內存已經(jīng)完全被釋放時,就會將該該堆通過munmap歸還給內核,同時將top chunk重新指向上一個堆內的可用內存地址。

可以這么理解,堆由兩部分組成,一部分是已經(jīng)分配出去的內存,另一部分是預留的內存(top,因為它總是存在于地址最高部分),而已經(jīng)分配出去的內存一部分由free釋放,成為了空閑內存(內存碎片),由此除預留部分部分之外,分為兩種內存,空閑內存和已使用內存。

無論是主 Arena 的堆還是子 Arena 的堆,在內存的申請、釋放與管理過程中,都遵循著一定的機制。當程序通過malloc函數(shù)申請內存時,堆管理器會首先在堆中查找合適的空閑內存塊。如果找到大小合適的空閑內存塊,就會將其分配給程序使用,并將該內存塊標記為已分配狀態(tài);如果沒有找到合適的空閑內存塊,堆管理器會根據(jù)情況從操作系統(tǒng)申請更多的內存,或者對已有的內存塊進行合并和整理,以滿足程序的內存需求。

而當程序通過free函數(shù)釋放內存時,堆管理器會將釋放的內存塊標記為空閑狀態(tài),并嘗試將其與相鄰的空閑內存塊進行合并,以減少內存碎片化,提高內存利用率。這就像是在倉庫中,當需要領取物資時,會先查找倉庫中是否有合適的物資,若沒有則會申請新的物資;當歸還物資時,會將物資放回倉庫,并整理倉庫,使物資擺放更加整齊。

3.3內存塊(Chunk)的組織與操作

內存塊(Chunk)是 Glibc 堆內存管理中的基本單元,它就像是構成內存大廈的一塊塊基石,程序所使用的內存都是以 Chunk 為單位進行分配和管理的。了解 Chunk 的組織方式以及malloc和free操作內存塊的具體過程和原理,對于深入理解 Glibc 堆內存管理機制至關重要。

圖片

在堆中,Chunk 按照一定的規(guī)則進行組織。每個 Chunk 都包含了一些元數(shù)據(jù),用于描述該 Chunk 的狀態(tài)和屬性。在 64 位系統(tǒng)中,一個典型的 Chunk 結構如下:

struct malloc_chunk {
    INTERNAL_SIZE_T prev_size; // 前一個Chunk的大?。ㄈ绻耙粋€Chunk是空閑的)
    INTERNAL_SIZE_T size;      // 當前Chunk的大小,包括頭部和數(shù)據(jù)部分,并且包含一些標志位
    struct malloc_chunk* fd;   // 雙向鏈表指針,指向下一個空閑Chunk(僅當Chunk空閑時有效)
    struct malloc_chunk* bk;   // 雙向鏈表指針,指向前一個空閑Chunk(僅當Chunk空閑時有效)
    // 對于大內存塊,還會有以下兩個指針,用于快速查找不同大小的Chunk
    struct malloc_chunk* fd_nextsize; 
    struct malloc_chunk* bk_nextsize; 
};

prev_size字段用于記錄前一個 Chunk 的大小,當且僅當前一個 Chunk 是空閑狀態(tài)時,這個字段才是有效的,它為內存合并提供了重要的信息。size字段則記錄了當前 Chunk 的大小,這個大小包括了 Chunk 頭部的大小以及用戶數(shù)據(jù)部分的大小,并且在size字段的低 3 位中,還包含了一些標志位,用于表示 Chunk 的狀態(tài),如是否是從mmap映射區(qū)分配的(M標志位)、前一個 Chunk 是否被使用(P標志位)以及是否屬于非主分配區(qū)(N標志位)。

fd和bk指針則是用于將空閑的 Chunk 組織成雙向鏈表,當一個 Chunk 被釋放時,它會被插入到相應的空閑鏈表中,以便后續(xù)的內存分配操作能夠快速找到合適的空閑 Chunk。而fd_nextsize和bk_nextsize指針則主要用于大內存塊的管理,它們可以幫助快速定位到不同大小的空閑 Chunk,提高大內存塊分配和釋放的效率。

當程序調用malloc函數(shù)申請內存時,malloc會按照一定的策略在堆中查找合適的 Chunk。首先,它會檢查請求的內存大小,如果請求的內存大小小于一個閾值(通常稱為max_fast),malloc會優(yōu)先在fast bins中查找合適的 Chunk。fast bins是一種特殊的空閑鏈表,用于管理較小的內存塊,它采用單向鏈表結構,并且后進先出(FILO)的原則,這樣可以快速地分配和釋放小內存塊,提高內存分配的效率。如果在fast bins中沒有找到合適的 Chunk,malloc會繼續(xù)在small bins和large bins中查找。

small bins用于管理中等大小的內存塊,其中相同大小的 Chunk 被組織在同一個雙向循環(huán)鏈表中;large bins則用于管理較大的內存塊,每個large bins鏈表中保存的是一組大小范圍相近的 Chunk,并且這些 Chunk 按照大小從大到小排序。在查找過程中,如果找到大小合適的 Chunk,malloc會將其從空閑鏈表中移除,并根據(jù)需要對 Chunk 進行分割,將剩余的部分重新插入到合適的空閑鏈表中。

如果在所有的空閑鏈表中都沒有找到合適的 Chunk,malloc會嘗試使用top chunk。top chunk是位于堆頂部的一個空閑 Chunk,當其他空閑鏈表中沒有合適的內存塊時,malloc會從top chunk中分割出一部分來滿足內存請求,如果top chunk的大小小于請求的內存大小,malloc會通過系統(tǒng)調用(brk或mmap)向操作系統(tǒng)申請更多的內存。

當程序調用free函數(shù)釋放內存時,free會將釋放的 Chunk 標記為空閑狀態(tài),并嘗試將其與相鄰的空閑 Chunk 進行合并,以減少內存碎片化。如果釋放的 Chunk 大小小于max_fast,它會被直接插入到fast bins中;如果大于max_fast,則會被插入到unsorted bin中。unsorted bin是一個臨時存放未整理 Chunk 的鏈表,后續(xù)malloc在查找內存塊時,會對unsorted bin中的 Chunk 進行整理,將它們移動到合適的small bins或large bins中。在合并 Chunk 時,free會根據(jù)prev_size和size字段中的標志位,判斷相鄰的 Chunk 是否空閑,如果相鄰的 Chunk 也是空閑的,則會將它們合并成一個更大的空閑 Chunk,然后再將其插入到相應的空閑鏈表中。

四、堆內存管理的分配

glib中堆內存分配的基本思路就是,首先找到本線程的Arena,然后優(yōu)先在Arena對應的回收箱中尋找合適大小的內存,在內存箱中所有內存塊均小于所需求的大小,那么就會去top chunk分割,但是如果top chunk的大小也不足夠,此時不一定要拓展top,檢查所需的內存是否大于128k,若大于,則直接使用系統(tǒng)調用mmap分配內存,如果小于,就進行top chunk的拓展,即堆的拓展,拓展完成后,從top chunk中分配內存,剩余部分成為新的top chunk。

4.1 malloc函數(shù)

malloc 函數(shù)是 C 語言標準庫中用于動態(tài)內存分配的核心函數(shù),其函數(shù)原型為:void* malloc(size_t size);。在這個原型中,size參數(shù)表示需要分配的內存塊的字節(jié)數(shù),它是一個無符號整數(shù)類型(size_t),這意味著我們可以根據(jù)實際需求,精確地指定所需內存的大小。

malloc 函數(shù)的主要功能就是從堆內存中分配一塊指定大小的連續(xù)內存空間,并返回一個指向該內存塊起始地址的指針。這個返回的指針類型是void*,也就是無類型指針。這是因為 malloc 函數(shù)在分配內存時,并不知道這塊內存將來會被用于存儲什么類型的數(shù)據(jù),所以它返回一個通用的無類型指針,需要我們在使用時將其強制轉換為實際所需的數(shù)據(jù)類型指針。例如,如果我們需要分配一塊內存來存儲整數(shù),就需要將 malloc 返回的指針轉換為int*類型;如果要存儲字符,就轉換為char*類型。

(1)分配機制

當程序調用 malloc 函數(shù)請求分配內存時,其背后的分配機制涉及到操作系統(tǒng)與程序之間的協(xié)同工作。操作系統(tǒng)為了有效地管理堆內存,通常會維護一個空閑內存鏈表,這個鏈表就像是一個記錄著所有空閑 “房間”(內存塊)的清單。鏈表中的每個節(jié)點都代表著一塊空閑的內存區(qū)域,節(jié)點中包含了該內存塊的大小、前后指針等信息,以便操作系統(tǒng)能夠快速地查找和管理這些空閑內存。

當 malloc 函數(shù)被調用時,操作系統(tǒng)會按照一定的算法,通常是首次適應算法、最佳適應算法或最差適應算法等,開始遍歷這個空閑內存鏈表。以首次適應算法為例,操作系統(tǒng)會從鏈表的頭部開始,依次檢查每個空閑內存塊,尋找第一個大小大于或等于所需分配大小size的內存塊。一旦找到這樣的內存塊,操作系統(tǒng)就會將其從空閑鏈表中移除,并根據(jù)需要對該內存塊進行分割。如果找到的空閑內存塊比請求的size大,那么操作系統(tǒng)會將多余的部分重新插入到空閑鏈表中,以便后續(xù)的內存分配請求使用。而分割出來的正好滿足size大小的內存塊,就會被標記為已分配,并返回其起始地址給程序,這個地址就是 malloc 函數(shù)的返回值。通過這樣的方式,malloc 函數(shù)能夠在堆內存中靈活地為程序分配所需的內存空間,以滿足各種動態(tài)內存需求。

(2)示例代碼

下面通過一段簡單的 C 語言代碼示例,來展示 malloc 函數(shù)的具體用法。假設我們要動態(tài)分配一個包含 10 個整數(shù)的數(shù)組,并對其進行初始化和輸出:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *arr;
    int n = 10;

    // 使用malloc分配內存,為n個整數(shù)分配空間
    arr = (int *)malloc(n * sizeof(int));

    // 檢查內存分配是否成功
    if (arr == NULL) {
        printf("內存分配失敗\n");
        return 1;
    }

    // 初始化數(shù)組
    for (int i = 0; i < n; i++) {
        arr[i] = i + 1;
    }

    // 輸出數(shù)組內容
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    // 釋放內存,避免內存泄漏
    free(arr);

    return 0;
}

4.2 free函數(shù)

free 函數(shù)與 malloc 函數(shù)緊密配合,是 C 語言中用于釋放動態(tài)分配內存的關鍵函數(shù)。其函數(shù)原型為:void free(void *ptr);,這里的ptr參數(shù)是一個指向先前通過 malloc、calloc 或 realloc 等函數(shù)動態(tài)分配的內存塊的指針。free 函數(shù)的主要功能就是將ptr所指向的內存塊歸還給系統(tǒng),使其重新成為可供分配的空閑內存,以便后續(xù)其他內存分配請求使用。

(1)釋放機制

當程序調用 free 函數(shù)釋放內存時,其內部的釋放機制如下:free 函數(shù)首先會根據(jù)傳入的指針ptr,找到對應的內存塊。在 malloc 分配內存時,除了分配用戶請求大小的內存空間外,還會在該內存塊的頭部或其他特定位置,記錄一些額外的管理信息,如內存塊的大小等。free 函數(shù)通過這些管理信息,能夠準確地確定要釋放的內存塊的邊界和大小。然后,free 函數(shù)會將該內存塊標記為空閑狀態(tài),并將其重新插入到操作系統(tǒng)維護的空閑內存鏈表中。

如果相鄰的內存塊也是空閑狀態(tài),free 函數(shù)通常會將它們合并成一個更大的空閑內存塊,這一過程被稱為內存合并。內存合并可以有效地減少內存碎片的產生,提高內存的利用率。例如,在一個頻繁進行內存分配和釋放的程序中,如果不進行內存合并,隨著時間的推移,內存中可能會出現(xiàn)大量零散的小空閑內存塊,這些小內存塊由于無法滿足較大的內存分配請求,而導致內存資源的浪費。通過內存合并,這些相鄰的小空閑內存塊可以合并成一個較大的空閑內存塊,從而提高內存的使用效率。

(2)示例代碼

接著上面 malloc 函數(shù)的示例代碼,我們來看一下 free 函數(shù)的使用:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *arr;
    int n = 10;

    // 使用malloc分配內存,為n個整數(shù)分配空間
    arr = (int *)malloc(n * sizeof(int));

    // 檢查內存分配是否成功
    if (arr == NULL) {
        printf("內存分配失敗\n");
        return 1;
    }

    // 初始化數(shù)組
    for (int i = 0; i < n; i++) {
        arr[i] = i + 1;
    }

    // 輸出數(shù)組內容
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    // 釋放內存,避免內存泄漏
    free(arr);
    // 將指針置空,避免懸空指針
    arr = NULL;

    return 0;
}

在這段代碼中,當我們使用 malloc 函數(shù)分配內存并完成對數(shù)組的操作后,調用 free (arr) 來釋放之前分配的內存。需要特別注意的是,在調用 free 函數(shù)之后,我們將指針arr賦值為NULL 。這是一個非常重要的操作,因為如果不將指針置空,arr就會成為一個懸空指針(Dangling Pointer)。懸空指針指向的是一塊已經(jīng)被釋放的內存,繼續(xù)使用懸空指針進行內存訪問,會導致未定義行為,可能引發(fā)程序崩潰、數(shù)據(jù)損壞等嚴重問題。將指針置空后,就可以避免不小心對已釋放內存的訪問,提高程序的穩(wěn)定性和安全性。

五、案例分析:GlibC Malloc for Exploiters 項目

5.1項目介紹

在安全研究的廣闊領域中,GlibC Malloc for Exploiters 項目宛如一顆璀璨的明星,為我們深入理解 glibc 堆內存管理機制以及開展安全研究提供了強大的助力。該項目可以說是對 GlibC 分配器進行了一次極為深入且全面的剖析,堪稱安全研究人員探索內存管理漏洞的有力工具。

當我們訪問其 GitHub Pages 站點,或者直接閱讀 Markdown 格式的原始內容時,就如同踏入了一座知識的殿堂。這里面詳細講解了 glibc 堆內存管理的諸多細節(jié),還通過豐富的實戰(zhàn)演示,將抽象的概念具象化,讓我們能夠更加直觀地理解相關知識。不僅如此,項目還貼心地分享了作者在 Insomni'hack 會議上的演講視頻,為我們的學習之路提供了更為生動直觀的方式,使我們能夠從多個角度深入了解項目的核心內容。

5.2技術分析與應用

GlibC Malloc 作為 Linux 環(huán)境下被廣泛使用的內存分配器,其運作機制極其復雜且微妙。GlibC Malloc for Exploiters 項目就像一把精準的手術刀,深入到 GlibC 的內部,清晰地揭示了 malloc 與 free 函數(shù)是如何管理堆內存的,尤其是在那些易受攻擊的場景下,它們的行為模式更是被剖析得淋漓盡致。

通過逆向工程與實證分析的手段,該項目為我們提供了一系列深刻的洞見。這些洞見能夠幫助開發(fā)者和安全研究員更好地理解那些可能導致安全漏洞的關鍵環(huán)節(jié)。比如,在內存碎片化管理方面,項目詳細闡述了隨著程序不斷地進行內存分配和釋放操作,內存是如何逐漸碎片化的,以及這種碎片化對程序性能和安全性的影響。對于 bins 結構,它深入分析了不同類型的 bins(如 fast bins、small bins、large bins 等)是如何組織和管理內存塊的,以及在內存分配和釋放過程中,bins 結構是如何發(fā)揮作用的。還有雙鏈表的脆弱之處,項目也進行了詳細的探討,指出了雙鏈表在某些情況下可能出現(xiàn)的問題,如指針錯誤、鏈表遍歷異常等,這些問題都有可能被攻擊者利用,從而導致安全漏洞。

在實際應用場景中,GlibC Malloc for Exploiters 項目展現(xiàn)出了極高的價值。對于安全研究者而言,掌握 GlibC Malloc 的工作原理是構建防御機制與實施精準攻擊的基石。在進行安全審計時,研究人員可以借助該項目提供的知識和工具,對程序的內存分配和釋放操作進行細致的檢查,從而發(fā)現(xiàn)潛在的安全隱患。在漏洞挖掘方面,通過深入理解 glibc 堆內存管理機制,研究人員能夠更敏銳地捕捉到可能存在的漏洞,如堆溢出、釋放后重用等。在開發(fā)防御工具時,該項目的研究成果也能為工具的設計提供重要的參考,幫助開發(fā)出更有效的防御工具,提高系統(tǒng)的安全性。

在逆向工程和滲透測試中,這個項目同樣發(fā)揮著重要作用。它能夠幫助研究人員找到內存操作中的薄弱點,從而進行有效的 exploit 開發(fā)。在 CTF 比賽中,很多題目都涉及到堆溢出等漏洞的利用,了解 GlibC Malloc for Exploiters 項目所揭示的細節(jié),能夠極大地提升選手對這些漏洞的利用效率,幫助選手在比賽中取得更好的成績。

責任編輯:武曉燕 來源: 深度Linux
相關推薦

2024-11-07 09:37:46

2009-10-22 17:39:34

CLR內存管理

2025-06-03 04:10:00

2009-09-02 09:23:26

.NET內存管理機制

2013-07-23 06:47:55

Android內存機制Android堆和棧Android開發(fā)學習

2022-06-01 16:01:58

MySQL內存管理系統(tǒng)

2010-09-26 13:23:13

JVM內存管理機制

2025-04-09 05:22:00

2013-09-29 15:11:46

Linux運維內存管理

2010-07-23 09:34:48

Python

2009-06-03 15:52:34

堆內存棧內存Java內存分配

2021-03-08 09:00:00

Java編程內存

2025-01-14 10:09:43

硬中斷Linux系統(tǒng)

2020-11-08 14:32:01

JavaScript變量內存管理

2022-02-28 10:25:17

Python參數(shù)傳遞拷貝

2016-10-09 14:41:40

Swift開發(fā)ARC

2010-12-10 15:40:58

JVM內存管理

2011-06-29 17:20:20

Qt 內存 QOBJECT

2025-04-15 06:00:00

2022-10-08 10:10:58

內存技術安全
點贊
收藏

51CTO技術棧公眾號