成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Glibc堆內(nèi)存管理:原理、機(jī)制與實(shí)戰(zhàn)

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

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

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

一、Glibc 堆內(nèi)存管理:為何重要?

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

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

除了程序崩潰,性能下降也是內(nèi)存問(wèn)題的一個(gè)重要表現(xiàn)。當(dāng)內(nèi)存管理不善時(shí),程序可能會(huì)頻繁地進(jìn)行內(nèi)存分配和釋放操作,這會(huì)導(dǎo)致內(nèi)存碎片化。內(nèi)存碎片化就像是一個(gè)雜亂無(wú)章的倉(cāng)庫(kù),雖然倉(cāng)庫(kù)的總?cè)萘孔銐颍捎谖锲窋[放混亂,需要使用某個(gè)物品時(shí)卻很難快速找到,從而降低了程序的運(yùn)行效率。在一些對(duì)性能要求極高的應(yīng)用場(chǎng)景中,如游戲開發(fā)、大數(shù)據(jù)處理等,內(nèi)存碎片化可能會(huì)導(dǎo)致游戲卡頓、數(shù)據(jù)處理速度變慢,嚴(yán)重影響用戶體驗(yàn)。

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

二、Glibc 堆內(nèi)存管理基礎(chǔ)

2.1進(jìn)程內(nèi)存布局

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

圖片圖片

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

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

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

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

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

2.2關(guān)鍵系統(tǒng)調(diào)用:brk 與 mmap

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

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

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

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

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

它還可以用于文件映射,將一個(gè)文件的全部或部分內(nèi)容映射到進(jìn)程的虛擬內(nèi)存中,進(jìn)程可以像訪問(wèn)內(nèi)存一樣讀寫文件的內(nèi)容,而不需要顯式地進(jìn)行文件 I/O 操作,對(duì)內(nèi)存的修改會(huì)自動(dòng)同步到文件中,減少了數(shù)據(jù)拷貝和系統(tǒng)調(diào)用的次數(shù)。在處理大型文件時(shí),如數(shù)據(jù)庫(kù)文件、日志文件等,mmap可以大大提高文件的讀寫效率。此外,mmap還常用于實(shí)現(xiàn)進(jìn)程間通信的機(jī)制,如共享內(nèi)存、消息隊(duì)列等,多個(gè)進(jìn)程可以映射同一個(gè)文件或匿名映射到它們的地址空間,實(shí)現(xiàn)共享內(nèi)存,從而高效地進(jì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 堆內(nèi)存管理中,malloc函數(shù)在分配內(nèi)存時(shí),會(huì)根據(jù)請(qǐng)求的內(nèi)存大小來(lái)選擇使用brk還是mmap。當(dāng)請(qǐng)求的內(nèi)存大小小于一定閾值(在大多數(shù)系統(tǒng)中,這個(gè)閾值通常為 128KB )時(shí),malloc函數(shù)會(huì)優(yōu)先使用brk系統(tǒng)調(diào)用來(lái)分配內(nèi)存,因?yàn)閎rk的系統(tǒng)調(diào)用開銷較小,適合頻繁的小塊內(nèi)存分配;當(dāng)請(qǐng)求的內(nèi)存大小大于這個(gè)閾值時(shí),則會(huì)使用mmap系統(tǒng)調(diào)用,因?yàn)閙map可以分配大塊連續(xù)的虛擬內(nèi)存,且可以獨(dú)立管理每個(gè)內(nèi)存塊,能夠滿足大塊內(nèi)存的分配需求。

三、Glibc 堆內(nèi)存管理核心機(jī)制

圖片圖片

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

在 Glibc 堆內(nèi)存管理的復(fù)雜體系中,分配區(qū)(Arena)就像是一個(gè)大型的內(nèi)存資源調(diào)配中心,它在內(nèi)存管理中扮演著至關(guān)重要的角色,是理解整個(gè)內(nèi)存管理機(jī)制的關(guān)鍵所在。

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

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

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

Arena的數(shù)據(jù)結(jié)構(gòu):

struct malloc_state
{
  /* Serialize access.  */
  mutex_t mutex;//互斥量,用于多線程共享一個(gè)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;//指向當(dāng)前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];//位圖,標(biāo)記bins中是否存在內(nèi)存塊

/* 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)的分類與管理

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

lang=c
/* 該數(shù)據(jù)結(jié)構(gòu)只在子Arena中使用,用于記錄當(dāng)前堆信息。 */
typedef struct _heap_info
{
  mstate ar_ptr; /* Arena for this heap. */ // 指向該堆所在的Arena
  struct _heap_info *prev; /* Previous heap. */ //由于一個(gè)子Arena管理多個(gè)堆,因此
  size_t size;   /* Current size in bytes. */ //當(dāng)前堆分配給用戶使用的大小,剩余部分為預(yù)留區(qū)域
  size_t mprotect_size; /* Size in bytes that has been mprotected
                           PROT_READ|PROT_WRITE.  */ //從代碼來(lái)看,和size并無(wú)區(qū)別(本人意見(jiàn))
  /* 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]; //用于對(duì)齊
} heap_info;

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

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

①堆的申請(qǐng)

第一類的堆無(wú)需申請(qǐng),只是調(diào)用brk進(jìn)行堆邊界的拓展即可。這里主要對(duì)第二類堆的申請(qǐng)進(jìn)行說(shuō)明。

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

②堆的釋放

這里堆的釋放是指glibc將申請(qǐng)的堆內(nèi)存歸還給內(nèi)核。

對(duì)于第一類堆,可以認(rèn)為只有堆大小的縮減,當(dāng)堆的頂部空閑的內(nèi)存滿足一定條件時(shí),可以通過(guò)brk將堆的邊界下移,top chunk指向地址不變,但大小變小了。

對(duì)于第二類堆,當(dāng)一個(gè)堆中的內(nèi)存已經(jīng)完全被釋放時(shí),就會(huì)將該該堆通過(guò)munmap歸還給內(nèi)核,同時(shí)將top chunk重新指向上一個(gè)堆內(nèi)的可用內(nèi)存地址。

可以這么理解,堆由兩部分組成,一部分是已經(jīng)分配出去的內(nèi)存,另一部分是預(yù)留的內(nèi)存(top,因?yàn)樗偸谴嬖谟诘刂纷罡卟糠郑呀?jīng)分配出去的內(nèi)存一部分由free釋放,成為了空閑內(nèi)存(內(nèi)存碎片),由此除預(yù)留部分部分之外,分為兩種內(nèi)存,空閑內(nèi)存和已使用內(nèi)存。

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

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

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

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

圖片

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

struct malloc_chunk {
    INTERNAL_SIZE_T prev_size; // 前一個(gè)Chunk的大小(如果前一個(gè)Chunk是空閑的)
    INTERNAL_SIZE_T size;      // 當(dāng)前Chunk的大小,包括頭部和數(shù)據(jù)部分,并且包含一些標(biāo)志位
    struct malloc_chunk* fd;   // 雙向鏈表指針,指向下一個(gè)空閑Chunk(僅當(dāng)Chunk空閑時(shí)有效)
    struct malloc_chunk* bk;   // 雙向鏈表指針,指向前一個(gè)空閑Chunk(僅當(dāng)Chunk空閑時(shí)有效)
    // 對(duì)于大內(nèi)存塊,還會(huì)有以下兩個(gè)指針,用于快速查找不同大小的Chunk
    struct malloc_chunk* fd_nextsize; 
    struct malloc_chunk* bk_nextsize; 
};

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

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

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

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

如果在所有的空閑鏈表中都沒(méi)有找到合適的 Chunk,malloc會(huì)嘗試使用top chunk。top chunk是位于堆頂部的一個(gè)空閑 Chunk,當(dāng)其他空閑鏈表中沒(méi)有合適的內(nèi)存塊時(shí),malloc會(huì)從top chunk中分割出一部分來(lái)滿足內(nèi)存請(qǐng)求,如果top chunk的大小小于請(qǐng)求的內(nèi)存大小,malloc會(huì)通過(guò)系統(tǒng)調(diào)用(brk或mmap)向操作系統(tǒng)申請(qǐng)更多的內(nèi)存。

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

四、堆內(nèi)存管理的分配

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

4.1 malloc函數(shù)

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

malloc 函數(shù)的主要功能就是從堆內(nèi)存中分配一塊指定大小的連續(xù)內(nèi)存空間,并返回一個(gè)指向該內(nèi)存塊起始地址的指針。這個(gè)返回的指針類型是void*,也就是無(wú)類型指針。這是因?yàn)?malloc 函數(shù)在分配內(nèi)存時(shí),并不知道這塊內(nèi)存將來(lái)會(huì)被用于存儲(chǔ)什么類型的數(shù)據(jù),所以它返回一個(gè)通用的無(wú)類型指針,需要我們?cè)谑褂脮r(shí)將其強(qiáng)制轉(zhuǎn)換為實(shí)際所需的數(shù)據(jù)類型指針。例如,如果我們需要分配一塊內(nèi)存來(lái)存儲(chǔ)整數(shù),就需要將 malloc 返回的指針轉(zhuǎn)換為int*類型;如果要存儲(chǔ)字符,就轉(zhuǎn)換為char*類型。

(1)分配機(jī)制

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

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

(2)示例代碼

下面通過(guò)一段簡(jiǎn)單的 C 語(yǔ)言代碼示例,來(lái)展示 malloc 函數(shù)的具體用法。假設(shè)我們要?jiǎng)討B(tài)分配一個(gè)包含 10 個(gè)整數(shù)的數(shù)組,并對(duì)其進(jìn)行初始化和輸出:

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

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

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

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

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

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

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

    return 0;
}

4.2 free函數(shù)

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

(1)釋放機(jī)制

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

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

(2)示例代碼

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

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

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

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

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

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

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

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

    return 0;
}

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

五、案例分析:GlibC Malloc for Exploiters 項(xiàng)目

5.1項(xiàng)目介紹

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

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

5.2技術(shù)分析與應(yīng)用

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

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

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

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

責(zé)任編輯:武曉燕 來(lái)源: 深度Linux
相關(guān)推薦

2024-11-07 09:37:46

2009-10-22 17:39:34

CLR內(nèi)存管理

2025-06-03 04:10:00

2009-09-02 09:23:26

.NET內(nèi)存管理機(jī)制

2013-07-23 06:47:55

Android內(nèi)存機(jī)制Android堆和棧Android開發(fā)學(xué)習(xí)

2022-06-01 16:01:58

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

2010-09-26 13:23:13

JVM內(nèi)存管理機(jī)制

2025-04-09 05:22:00

2013-09-29 15:11:46

Linux運(yùn)維內(nèi)存管理

2010-07-23 09:34:48

Python

2009-06-03 15:52:34

堆內(nèi)存棧內(nèi)存Java內(nèi)存分配

2021-03-08 09:00:00

Java編程內(nèi)存

2025-01-14 10:09:43

硬中斷Linux系統(tǒng)

2020-11-08 14:32:01

JavaScript變量內(nèi)存管理

2022-02-28 10:25:17

Python參數(shù)傳遞拷貝

2016-10-09 14:41:40

Swift開發(fā)ARC

2010-12-10 15:40:58

JVM內(nèi)存管理

2011-06-29 17:20:20

Qt 內(nèi)存 QOBJECT

2025-04-15 06:00:00

2022-02-23 16:49:19

Linux內(nèi)存數(shù)據(jù)結(jié)構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 欧美激情国产精品 | 欧美一区二区在线 | 国产精品成av人在线视午夜片 | 亚洲视屏 | 亚洲人精品午夜 | 国产精品地址 | 涩涩视频在线观看 | 欧美成人精品一区二区男人看 | 羞羞视频在线观看 | 欧美极品在线观看 | 中文字幕在线一区 | 亚洲人成在线播放 | 久久久久久久99 | 天天草天天干天天 | 在线中文字幕亚洲 | 国产免费一区二区 | 911精品美国片911久久久 | 国产精品久久精品 | www.日韩 | 亚洲精品成人在线 | 国产精品99久久久久久宅男 | 在线免费观看一区二区 | 国产免费人成xvideos视频 | 国产视频第一页 | 欧美2区| 国产专区在线 | 午夜成人免费视频 | 日韩三级 | а天堂中文最新一区二区三区 | 中文字幕 欧美 日韩 | 国产黄色在线 | 一区二区三区高清 | 日韩av免费在线电影 | 国产一区在线看 | 91久久夜色| 亚洲一区二区三区在线观看免费 | 亚洲国产成人av好男人在线观看 | 中文字幕乱码一区二区三区 | 久久久久免费精品国产小说色大师 | 国产精品一区二区久久久久 | 色av一区二区三区 |