glibc-2.23学习笔记(一)—— malloc部分源码分析
生活随笔
收集整理的這篇文章主要介紹了
glibc-2.23学习笔记(一)—— malloc部分源码分析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
glibc-2.23學習筆記(一)—— malloc部分源碼分析
- 搭建Glibc源碼調試環境
- 1.下載并解壓glibc源碼
- 2.配置gdb
- 3.編譯測試程序
- 第一次調用
- 源碼分析
- __libc_malloc
- _int_malloc
- 函數聲明
- 局部變量
- start
- fast bin部分
- small bin部分
- large bin部分
- binmap部分
- top chunk部分
- 參考資料
搭建Glibc源碼調試環境
1.下載并解壓glibc源碼
sudo apt-get install glibc-source cd /usr/src/glibc sudo tar xvf glibc-2.23.tar.xz2.配置gdb
打開gdb配置文件
sudo vim ~/.gdbinit在首行加入以下內容
directory /usr/src/glibc/glibc-2.23/malloc:/usr/src/glibc/glibc-2.23/elf3.編譯測試程序
//test.c #include <stdio.h> #include <stdlib.h>int main() {malloc(0x10); //第一次調用malloc(0x10); //重點分析此處return 0; } //gcc test.c -o test第一次調用
第一次調用malloc時會從__malloc_hook中取出malloc_hook_ini函數指針并執行
源碼分析
__libc_malloc
void * __libc_malloc (size_t bytes) {mstate ar_ptr;void *victim;/* 判斷__malloc_hook中是否有值,有值就當成函數指針調用 */void *(*hook) (size_t, const void *)= atomic_forced_read (__malloc_hook);if (__builtin_expect (hook != NULL, 0))return (*hook)(bytes, RETURN_ADDRESS (0));/* 獲取分配區指針,并鎖住分配區內存 */arena_get (ar_ptr, bytes);/* 分配內存 */victim = _int_malloc (ar_ptr, bytes);/* 內存分配失敗,嘗試尋找其他可用的arena進行分配 */if (!victim && ar_ptr != NULL){LIBC_PROBE (memory_malloc_retry, 1, bytes);ar_ptr = arena_get_retry (ar_ptr, bytes);victim = _int_malloc (ar_ptr, bytes);}//解除分配區內存鎖if (ar_ptr != NULL)(void) mutex_unlock (&ar_ptr->mutex);/* 通過倒數第二個比特位判斷內存屬性 */assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||ar_ptr == arena_for_chunk (mem2chunk (victim)));return victim; }_int_malloc
函數聲明
static void * _int_malloc (mstate av, size_t bytes){局部變量
/* 對齊后的所需內存大小 */INTERNAL_SIZE_T nb; /* normalized request size *//* 保存所需chunk在bins中的下標 */unsigned int idx; /* associated bin index *//* 保存bin */mbinptr bin; /* associated bin *//* 保存候選chunk */mchunkptr victim; /* inspected/selected chunk *//* 保存chunk的size */INTERNAL_SIZE_T size; /* its size *//* 保存候選chunk在bins中的下標 */int victim_index; /* its bin index *//* 保存從候選chunk分配內存后剩余內存的指針 */mchunkptr remainder; /* remainder from a split *//* 保存剩余部分內存大小 */unsigned long remainder_size; /* its size */unsigned int block; /* bit map traverser */unsigned int bit; /* bit map traverser */unsigned int map; /* current word of binmap */mchunkptr fwd; /* misc temp for linking */mchunkptr bck; /* misc temp for linking */const char *errstr = NULL;start
/*Convert request size to internal form by adding SIZE_SZ bytesoverhead plus possibly more to obtain necessary alignment and/orto obtain a size of at least MINSIZE, the smallest allocatablesize. Also, checked_request2size traps (returning 0) request sizesthat are so large that they wrap around zero when padded andaligned.*//* 取得對齊后的size值 */checked_request2size(bytes, nb);/* There are no usable arenas. Fall back to sysmalloc to get a chunk frommmap. *//* 沒有可用的arena,隨機分配一塊內存并返回 */if (__glibc_unlikely(av == NULL)){void* p = sysmalloc(nb, av);if (p != NULL)alloc_perturb(p, bytes);return p;}fast bin部分
/*If the size qualifies as a fastbin, first check corresponding bin.This code is safe to execute even if av is not yet initialized, so wecan try it without checking, which saves some time on this fast path.*//* 如果所需大小小于等于fast bins中的最大size,則嘗試從fast bins中分配第一次調用malloc時,max_fast為0 */if ((unsigned long)(nb) <= (unsigned long)(get_max_fast())){idx = fastbin_index (nb); //計算所需大小在fast bins中的下標mfastbinptr *fb = &fastbin (av, idx); //嘗試從對應下標中取出堆塊指針mchunkptr pp = *fb;/* 若存在可用的bin,將bin從鏈表中取出,并取出當前bin的fd放入鏈表尾,fd的值不能和當前bin相同 */do{victim = pp;if (victim == NULL)break;}while ((pp = catomic_compare_and_exchange_val_acq (fb, victim->fd, victim))!= victim);if (victim != 0) // 若候選chunk存在{/* 檢查size位是否屬于fast bins */if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0)){errstr = "malloc(): memory corruption (fast)";errout:malloc_printerr (check_action, errstr, chunk2mem (victim), av);return NULL;}check_remalloced_chunk (av, victim, nb); //各種檢測,包括堆塊大小,是否對齊等等void *p = chunk2mem (victim); //返回chunk地址(不包含head)alloc_perturb (p, bytes);return p; //返回應用層}}small bin部分
/*If a small request, check regular bin. Since these "smallbins"hold one size each, no searching within bins is necessary.(For a large request, we need to wait until unsorted chunks areprocessed to find best fit. But for small ones, fits are exactanyway, so we can check now, which is faster.)*//* 若所需大小屬于small bins,則嘗試在small bins中分配 */if (in_smallbin_range (nb)){idx = smallbin_index (nb); //獲取所需大小對應下標bin = bin_at (av, idx); //從arena獲取下標對應的bin/* 如果victim等于表頭,表示該鏈表為空 */if ((victim = last (bin)) != bin){/* 如果候選chunk為0表示還沒有創建雙向循環鏈表 */if (victim == 0) /* initialization check */malloc_consolidate (av); /* 第一次malloc時會調用這個函數合并所有的fast bin */else{/* 否則嘗試將victim從small bin中取出 */bck = victim->bk;if (__glibc_unlikely (bck->fd != victim)){errstr = "malloc(): smallbin double linked list corrupted";goto errout;}set_inuse_bit_at_offset (victim, nb); //設置候選chunk的inuse標志//該標志位于下一個chunk size位的第0個bit/* 將bin從鏈表中取出,相當于unlink */bin->bk = bck;bck->fd = bin;if (av != &main_arena)victim->size |= NON_MAIN_ARENA;check_malloced_chunk (av, victim, nb); //各種檢測void *p = chunk2mem (victim); //獲得用戶部分可用的指針alloc_perturb (p, bytes);return p; //返回}}}large bin部分
/*If this is a large request, consolidate fastbins before continuing.While it might look excessive to kill all fastbins beforeeven seeing if there is space available, this avoidsfragmentation problems normally associated with fastbins.Also, in practice, programs tend to have runs of either small orlarge requests, but less often mixtures, so consolidation is notinvoked all that often in most programs. And the programs thatit is called frequently in otherwise tend to fragment.*//* 若所需大小不屬于small bins,則可能位于large bins中 */else {idx = largebin_index (nb); //計算所需大小對應large bins的下標if (have_fastchunks (av)) //判斷是否存在屬于fast bins的空閑chunkmalloc_consolidate (av); //合并所有的fast bin}/*Process recently freed or remaindered chunks, taking one only ifit is exact fit, or, if this a small request, the chunk is remainder fromthe most recent non-exact fit. Place other traversed chunks inbins. Note that this step is the only place in any routine wherechunks are placed in bins.The outer loop here is needed because we might not realize untilnear the end of malloc that we should have consolidated, so mustdo so and retry. This happens at most once, and only when we wouldotherwise need to expand memory to service a "small" request.*/for (;; ){int iters = 0;/* 反向遍歷unsorted bins雙向循環鏈表,直到候選chunk指向頭節點 */while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av)){bck = victim->bk;//判斷chunk大小是否合法if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)|| __builtin_expect (victim->size > av->system_mem, 0))/* 如果不合法就執行malloc_printerr打印錯誤信息 */malloc_printerr (check_action, "malloc(): memory corruption", chunk2mem (victim), av);size = chunksize (victim); //若合法則取出size位/*If a small request, try to use last remainder if it is theonly chunk in unsorted bin. This helps promote locality forruns of consecutive small requests. This is the onlyexception to best-fit, and applies only when there isno exact fit for a small chunk.*//* 如果這個chunk大小屬于small bins且unsorted bins中只有一個chunk,且這個chunk為last remainder chunk,且這個chunk的大小大于所需的size+MINSIZE */if (in_smallbin_range (nb) &&bck == unsorted_chunks (av) &&victim == av->last_remainder &&(unsigned long) (size) > (unsigned long) (nb + MINSIZE)){/* split and reattach remainder *//* 從這個remainder中取出所需的部分,與表頭形成雙向循環鏈表 */remainder_size = size - nb; //計算取出所需部分后的剩余部分remainder = chunk_at_offset (victim, nb); //獲得chunk指針unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder; //arena指向remainderav->last_remainder = remainder; //設置新的remainderremainder->bk = remainder->fd = unsorted_chunks (av); //remainder指向arena/* 如果剩余部分大小不屬于small bins,則只能時largebins因此需要將fd_nextsize和bk_nextsize清空,unsorted bin無需這兩個成員 */if (!in_smallbin_range (remainder_size)){remainder->fd_nextsize = NULL;remainder->bk_nextsize = NULL;}/* 設置chunk的相關信息 */set_head (victim, nb | PREV_INUSE |(av != &main_arena ? NON_MAIN_ARENA : 0));set_head (remainder, remainder_size | PREV_INUSE);set_foot (remainder, remainder_size);check_malloced_chunk (av, victim, nb);void *p = chunk2mem (victim); //取得用戶部分可用的內存指針alloc_perturb (p, bytes);return p; //返回應用層}/* remove from unsorted list *//* 將bin從unsortedbin中取出 */unsorted_chunks (av)->bk = bck;bck->fd = unsorted_chunks (av);/* Take now instead of binning if exact fit *//* 若size位等于所需大小,則設置標志位,然后將bin取出并返回用戶指針 */if (size == nb){set_inuse_bit_at_offset (victim, size);if (av != &main_arena)victim->size |= NON_MAIN_ARENA;check_malloced_chunk (av, victim, nb);void *p = chunk2mem (victim);alloc_perturb (p, bytes);return p; //返回應用層}/* place chunk in bin *//* 若size屬于small bins,則將chunk加入到bck和fwd之間,作為small bins的第一個chunk */if (in_smallbin_range (size)){victim_index = smallbin_index (size);bck = bin_at (av, victim_index);fwd = bck->fd;}/* 若size屬于large bins,則將chunk加入到bck和fwd之間,作為large bin的第一個chunk */else{victim_index = largebin_index (size);bck = bin_at (av, victim_index);fwd = bck->fd;/* maintain large bins in sorted order */if (fwd != bck) //若fwd不等于bck,說明large bins中存在空閑chunk{/* Or with inuse bit to speed comparisons */size |= PREV_INUSE;/* if smaller than smallest, bypass loop below */assert ((bck->bk->size & NON_MAIN_ARENA) == 0);/* 如果當前size比最后一個chunk size還要小,則將當前size的chunk加入到chunk size鏈表尾然后將所有大小的鏈表取出首個chunk鏈到一起,方便查找 */if ((unsigned long) (size) < (unsigned long) (bck->bk->size)){fwd = bck;bck = bck->bk;victim->fd_nextsize = fwd->fd;victim->bk_nextsize = fwd->fd->bk_nextsize;fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;}else{assert ((fwd->size & NON_MAIN_ARENA) == 0);/* 正向遍歷chunk size鏈表,找到第一個chunk大小小于等于當前大小的chunk */while ((unsigned long) size < fwd->size){fwd = fwd->fd_nextsize;assert ((fwd->size & NON_MAIN_ARENA) == 0);}/* 若已經存在相同大小的chunk,則將當前chunk插入到同大小chunk鏈表的尾部 */if ((unsigned long) size == (unsigned long) fwd->size)/* Always insert in the second position. */fwd = fwd->fd;/* 否則延伸出一個大小等于當前size的chunk鏈表,將該鏈表加入到chunk size鏈表尾 */else{victim->fd_nextsize = fwd;victim->bk_nextsize = fwd->bk_nextsize;fwd->bk_nextsize = victim;victim->bk_nextsize->fd_nextsize = victim;}bck = fwd->bk;}}else //large bins中沒有 chunk,直接將當前 chunk 加入 chunk size鏈表victim->fd_nextsize = victim->bk_nextsize = victim;}/* 將當前chunk加入large bins的空閑鏈表中 */mark_bin (av, victim_index);victim->bk = bck;victim->fd = fwd;fwd->bk = victim;bck->fd = victim;/* 最多遍歷10000個unsorted bin,節約時間 */#define MAX_ITERS 10000if (++iters >= MAX_ITERS)break;}/*If a large request, scan through the chunks of current bin insorted order to find smallest that fits. Use the skip list for this.*//* 當處理完unsorted bins后,使用最佳匹配法匹配chunk */if (!in_smallbin_range (nb)) //判斷chunk是否位于large bins中{bin = bin_at (av, idx);/* skip scan if empty or largest chunk is too small *//* 判斷large bins是否為空,以及鏈表中的最大size是否滿足所需大小 */if ((victim = first (bin)) != bin && (unsigned long) (victim->size) >= (unsigned long) (nb)){/* 遍歷chunk size鏈表,找到大于等于所需大小的chunk鏈表 */victim = victim->bk_nextsize;while (((unsigned long) (size = chunksize (victim)) < (unsigned long) (nb)))victim = victim->bk_nextsize;/* Avoid removing the first entry for a size so that the skiplist does not have to be rerouted. *//* 為了盡量不破壞鏈表結構,嘗試取出victim->fd作為候選chunk */if (victim != last (bin) && victim->size == victim->fd->size)victim = victim->fd;/* 計算剩余size,然后斷鏈 */remainder_size = size - nb;unlink (av, victim, bck, fwd);/* Exhaust *//* 若剩余部分小于MIN_SIZE,則將整個chunk分配給應用層(可以搞事情嗷) */if (remainder_size < MINSIZE){set_inuse_bit_at_offset (victim, size);if (av != &main_arena)victim->size |= NON_MAIN_ARENA;}/* Split */else{/* 獲得剩余部分chunk指針 */remainder = chunk_at_offset (victim, nb);/* We cannot assume the unsorted list is empty and thereforehave to perform a complete insert here. *//* 剩余部分作為新chunk加入到unsorted bins中 */bck = unsorted_chunks (av);fwd = bck->fd;if (__glibc_unlikely (fwd->bk != bck)){errstr = "malloc(): corrupted unsorted chunks";goto errout;}remainder->bk = bck;remainder->fd = fwd;bck->fd = remainder;fwd->bk = remainder;/* 若剩余部分大小屬于large bin,則將fd_nextsize和bk_nextsize清零因為這兩個指針對于unsorted bin無用 */if (!in_smallbin_range (remainder_size)){remainder->fd_nextsize = NULL;remainder->bk_nextsize = NULL;}/* 設置各種標志位 */set_head (victim, nb | PREV_INUSE |(av != &main_arena ? NON_MAIN_ARENA : 0));set_head (remainder, remainder_size | PREV_INUSE);set_foot (remainder, remainder_size);}check_malloced_chunk (av, victim, nb);void *p = chunk2mem (victim); //獲取用戶部分指針 alloc_perturb (p, bytes);return p; //返回應用層}}binmap部分
/*Search for a chunk by scanning bins, starting with next largestbin. This search is strictly by best-fit; i.e., the smallest(with ties going to approximately the least recently used) chunkthat fits is selected.The bitmap avoids needing to check that most blocks are nonempty.The particular case of skipping all bins during warm-up phaseswhen no chunks have been returned yet is faster than it might look.*//* 在small bins和large bins中都沒有找到大小合適的chunk嘗試從大小比所需大小更大的空閑chunk中尋找合適的 *//* 獲取下一個相鄰bin的空閑chunk鏈表,并獲取該bin對于binmap中的bit位的值binmap中標識了相應bin中是否存在空閑chunk,按照block進行管理每個block為一個int,共32bit,可以表示32個bin中是否存在空閑chunk使用binmap主要時為了加快查找空閑chunk的效率這里只查詢比所需chunk大的bin中是否有空閑chunk可用 */++idx;bin = bin_at (av, idx);block = idx2block (idx);map = av->binmap[block];bit = idx2bit (idx);for (;; ){/* Skip rest of block if there are no more set bits in this block. *//* 若bit > map,說明map為0,則該block對應的所有bins都沒有空閑chunk */if (bit > map || bit == 0){/* 遍歷下一個block,直到找到一個不為0的block或遍歷完所有的block */do{if (++block >= BINMAPSIZE) /* out of bins *//* 沒有找到合適chunk,嘗試使用top chunk分配 */goto use_top;}while ((map = av->binmap[block]) == 0);/* 設置bin指向block的第一個bit對應的bin */bin = bin_at (av, (block << BINMAPSHIFT));bit = 1; //將bit置為1,表示該block中bit1對應的bin}/* Advance to bin with set bit. There must be one. *//* 在block中遍歷對應的bin,直到找到一個不為0的bit */while ((bit & map) == 0){bin = next_bin (bin);bit <<= 1;assert (bit != 0);}/* Inspect the bin. It is likely to be non-empty *//* 將chunk加入鏈表尾 */victim = last (bin);/* If a false alarm (empty bin), clear the bit. *//* 若victim與bin鏈表頭指針相同,表示該bin中沒有空閑chunkbinmap中的相應位設置不準確,將其清零 */if (victim == bin){av->binmap[block] = map &= ~bit; /* Write through */bin = next_bin (bin);bit <<= 1;}else{size = chunksize (victim); //獲得size/* We know the first chunk in this bin is big enough to use. *//* 判斷chunk大小是否滿足 */assert ((unsigned long) (size) >= (unsigned long) (nb));remainder_size = size - nb; //計算分配后的剩余大小/* unlink */unlink (av, victim, bck, fwd); //將chunk斷鏈/* Exhaust *//* 若剩余大小小于MINSIZE,則將整個chunk分配給用戶 */if (remainder_size < MINSIZE){set_inuse_bit_at_offset (victim, size);if (av != &main_arena)victim->size |= NON_MAIN_ARENA;}/* Split */else{/* 獲得chunk指針 */remainder = chunk_at_offset (victim, nb);/* We cannot assume the unsorted list is empty and thereforehave to perform a complete insert here. *//* 剩余部分作為新chunk加入到unsorted bins中 */bck = unsorted_chunks (av);fwd = bck->fd;if (__glibc_unlikely (fwd->bk != bck)){errstr = "malloc(): corrupted unsorted chunks 2";goto errout;}remainder->bk = bck;remainder->fd = fwd;bck->fd = remainder;fwd->bk = remainder;/* advertise as last remainder *//* 若分配大小屬于small bin,將last_remainder設置為剩余部分構成的chunk */if (in_smallbin_range (nb))av->last_remainder = remainder;/* 若剩余部分大小屬于large bin,則將fd_nextsize和bk_nextsize清零因為這兩個指針對于unsorted bin無用 */if (!in_smallbin_range (remainder_size)){remainder->fd_nextsize = NULL;remainder->bk_nextsize = NULL;}/* 設置各種標志位 */set_head (victim, nb | PREV_INUSE |(av != &main_arena ? NON_MAIN_ARENA : 0));set_head (remainder, remainder_size | PREV_INUSE);set_foot (remainder, remainder_size);}check_malloced_chunk (av, victim, nb); //各種檢測void *p = chunk2mem (victim); //獲得用戶部分指針alloc_perturb (p, bytes);return p; //返回應用層}}top chunk部分
use_top:/*If large enough, split off the chunk bordering the end of memory(held in av->top). Note that this is in accord with the best-fitsearch rule. In effect, av->top is treated as larger (and thusless well fitting) than any other available chunk since it canbe extended to be as large as necessary (up to systemlimitations).We require that av->top always exists (i.e., has size >=MINSIZE) after initialization, so if it would otherwise beexhausted by current request, it is replenished. (The mainreason for ensuring it exists is that we may need MINSIZE spaceto put in fenceposts in sysmalloc.)*//* 獲得top chunk指針與大小 */victim = av->top;size = chunksize (victim);/* 必須滿足top chunk size > nb + MINSIZE的情況下才能分配 */if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)){/* 從top chunk分配內存后,剩余的部分將作為新的top chunk */remainder_size = size - nb;remainder = chunk_at_offset (victim, nb);av->top = remainder;/* 設置各種標志位 */set_head (victim, nb | PREV_INUSE |(av != &main_arena ? NON_MAIN_ARENA : 0));set_head (remainder, remainder_size | PREV_INUSE);check_malloced_chunk (av, victim, nb);void *p = chunk2mem (victim); //獲得用戶部分指針alloc_perturb (p, bytes);return p; //返回應用層}/* When we are using atomic ops to free fast chunks we can gethere for all block sizes. *//* 若top chunk也無法滿足要求,則檢查fast bins中是否存在空閑chunk若存在,則將所有的fast bins合并,然后嘗試從small bins和large bins獲取下標 */else if (have_fastchunks (av)){malloc_consolidate (av);/* restore original bin index */if (in_smallbin_range (nb))idx = smallbin_index (nb);elseidx = largebin_index (nb);}/*Otherwise, relay to handle system-dependent cases*/else{/* 所有方法都行不通,最后的解決方案是向系統申請一塊新的內存 */void *p = sysmalloc (nb, av);if (p != NULL)alloc_perturb (p, bytes);return p;}} }參考資料
《Glibc內存管理Ptmalloc源代碼分析》
總結
以上是生活随笔為你收集整理的glibc-2.23学习笔记(一)—— malloc部分源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 软件调试学习笔记(七)—— 单步步入单步
- 下一篇: glibc-2.23学习笔记(二)——