0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

malloc跟free的源碼分析

科技綠洲 ? 來源:Linux開發(fā)架構(gòu)之路 ? 作者:Linux開發(fā)架構(gòu)之路 ? 2023-11-09 11:39 ? 次閱讀

malloc

本文梳理了一下malloc跟free的源碼。malloc()函數(shù)在源代碼中使用宏定義為public_mALLOc()。public_mALLOc()函數(shù)只是簡(jiǎn)單的封裝_int_malloc()函數(shù),_int_malloc()函數(shù)才是內(nèi)存分配的核心實(shí)現(xiàn)。

public_mALLOc()

Void_t* public_mALLOc(size_t bytes)
{
mstate ar_ptr;
Void_t *victim;
__malloc_ptr_t (*hook) (size_t, __const __malloc_ptr_t)
= force_reg (__malloc_hook);
if (__builtin_expect (hook != NULL, 0))
return (*hook)(bytes, RETURN_ADDRESS (0));

首先檢查是否存在__malloc_hook。如果存在,則調(diào)用hook函數(shù)。注意hook函數(shù)的傳參為請(qǐng)求分配的內(nèi)存大小。

arena_lookup(ar_ptr);
arena_lock(ar_ptr, bytes);
if(!ar_ptr)
return 0;
victim = _int_malloc(ar_ptr, bytes);

獲取分配區(qū)指針,如果獲取分配區(qū)失敗,返回退出,否則,調(diào)用_int_malloc()函數(shù)分配內(nèi)存。

if(!victim)
{
/* Maybe the failure is due to running out of mmapped areas. */
if(ar_ptr != &main_arena)
{
(void)mutex_unlock(&ar_ptr->mutex);
ar_ptr = &main_arena;
(void)mutex_lock(&ar_ptr->mutex);
victim = _int_malloc(ar_ptr, bytes);
(void)mutex_unlock(&ar_ptr->mutex);
}

如果_int_malloc()函數(shù)分配內(nèi)存失敗,并且使用的分配區(qū)不是主分配區(qū),這種情況可能是mmap區(qū)域的內(nèi)存被用光了。如果目前主分配區(qū)還可以從堆中分配內(nèi)存,則需要再嘗試從主分配區(qū)中分配內(nèi)存。首先釋放所使用分配區(qū)的鎖,然后獲得主分配區(qū)的鎖,并調(diào)用_int_malloc()函數(shù)分配內(nèi)存,最后釋放主分配區(qū)的鎖。

else
{
#if USE_ARENAS
/* ... or sbrk() has failed and there is still a chance to mmap() */
ar_ptr = arena_get2(ar_ptr->next ? ar_ptr : 0, bytes);
(void)mutex_unlock(&main_arena.mutex);
if(ar_ptr)
{
victim = _int_malloc(ar_ptr, bytes);
(void)mutex_unlock(&ar_ptr->mutex);
}
#endif
}
}

如果_int_malloc()函數(shù)分配內(nèi)存失敗,并且使用的分配區(qū)是主分配區(qū),查看是否有非主分配區(qū),如果有,調(diào)用arena_get2()獲取分配區(qū),然后對(duì)主分配區(qū)解鎖,如果arena_get2()返回一個(gè)非分配區(qū),嘗試調(diào)用_int_malloc()函數(shù)從該非主分配區(qū)分配內(nèi)存,最后釋放該非主分配區(qū)的鎖。

else
(void)mutex_unlock(&ar_ptr->mutex);
assert(!victim || chunk_is_mmapped(mem2chunk(victim)) || ar_ptr ==
arena_for_chunk(mem2chunk(victim)));
return victim;
}

如果_int_malloc()函數(shù)分配內(nèi)存成功,釋放所使用的分配區(qū)的鎖。返回分配的chunk。

_int_malloc

_int_malloc函數(shù)是內(nèi)存分配的核心,根據(jù)分配的內(nèi)存塊的大小,該函數(shù)中實(shí)現(xiàn)了了四種分配內(nèi)存的路徑。先給出_int_malloc()函數(shù)的定義及臨時(shí)變量的定義:

static Void_t* _int_malloc(mstate av, size_t bytes)
{
INTERNAL_SIZE_T nb; /* normalized request size */
unsigned int idx; /* associated bin index */
mbinptr bin; /* associated bin */

mchunkptr victim; /* inspected/selected chunk */
INTERNAL_SIZE_T size; /* its size */
int victim_index; /* its bin index */

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;

/*
Convert request size to internal form by adding SIZE_SZ bytes
overhead plus possibly more to obtain necessary alignment and/or
to obtain a size of at least MINSIZE, the smallest allocatable
size. Also, checked_request 2size traps (returning 0) request sizes
that are so large that they wrap around zero when padded and
aligned.
*/

checked_request2size()函數(shù)將需要分配的內(nèi)存大小bytes轉(zhuǎn)換為需要分配的chunk大小nb,Ptmalloc內(nèi)部分配都是以chunk為單位,根據(jù)chunk的大小,決定如何獲得滿足條件的chunk。

分配fast bin chunk

/*
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 we
can try it without checking, which saves some time on this fast path.
*/
if ((unsigned long)(nb) <= (unsigned long)(get_max_fast ()))
{
idx = fastbin_index(nb);
mfastbinptr* fb = &fastbin (av, idx);
#ifdef ATOMIC_FASTBINS
mchunkptr pp = *fb;
do
{
victim = pp;
if (victim == NULL)
break;
} while ((pp = catomic_compare_and_exchange_val_acq (fb, victim->fd, victim)) != victim);
#else
victim = *fb;
#endif
if (victim != 0)
{
if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
{
errstr = "malloc(): memory corruption (fast)";
errout:
malloc_printerr (check_action, errstr, chunk2mem (victim));
return NULL;
}
#ifndef ATOMIC_FASTBINS
*fb = victim->fd;
#endif
check_remalloced_chunk(av, victim, nb);
void *p = chunk2mem(victim);
if (__builtin_expect (perturb_byte, 0))
alloc_perturb (p, bytes);
return p;
}
}

如果所需的chunk大小小于等于fast bins中的最大chunk大小,首先嘗試從fast bin中分配chunk。1.根據(jù)所需的chunk大小獲得該chunk所屬的fast bin的index,根據(jù)該index獲得所屬fast bin的空閑chunk鏈表頭指針。如果沒有開啟ATOMIC_FASTBINS優(yōu)化,則按以下步驟:2.將頭指針的下一個(gè)chunk作為空閑chunk鏈表的頭部。3.取出第一個(gè)chunk,并調(diào)用chunk2mem()函數(shù)返回用戶所需的內(nèi)存塊。如果開啟了ATOMIC_FASTBINS優(yōu)化,則步驟與上述類似,只是在刪除fastbin頭節(jié)點(diǎn)的時(shí)候使用了lock-free技術(shù),加快了分配速度。

check

fastbin分配對(duì)size做了檢查,如果分配chunk的size不等于分配時(shí)的idx,就會(huì)報(bào)錯(cuò)。使用chunksize()和fastbin_index函數(shù)計(jì)算chunk的size大小,所以我們無需管size的后三位(size_sz=8的情況下無需管后四位),只需保證前幾位與idx相同即可。

分配small bin chunk

/*
If a small request, check regular bin. Since these "smallbins"
hold one size each, no search ing within bins is necessary.
(For a large request, we need to wait until unsorted chunks are
processed to find best fit. But for small ones, fits are exact
anyway, so we can check now, which is faster.)
*/
if (in_smallbin_range(nb))
{
idx = smallbin_index(nb);
bin = bin_at(av,idx);

if ( (victim = last(bin)) != bin)
{
if (victim == 0) /* initialization check */
malloc_consolidate(av);
else
{
bck = victim->bk;
if (__builtin_expect (bck->fd != victim, 0))
{
errstr = "malloc(): smallbin double linked list corrupted";
goto errout;
}
set_inuse_bit_at_offset(victim, nb);
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);
if (__builtin_expect (perturb_byte, 0))
alloc_perturb (p, bytes);
return p;
}
}
}

如果所需的chunk大小屬于small bin,則會(huì)執(zhí)行如下步驟:1.查找chunk對(duì)應(yīng)small bins數(shù)組的index,根據(jù)index獲得某個(gè)small bin的空閑chunk雙向循環(huán)鏈表表頭。2.將最后一個(gè)chunk賦值給victim,如果victim與表頭相同,表示該鏈表為空,不能從small bin的空閑chunk鏈表中分配,這里不做處理,等后面的步驟來處理。3.victim與表頭不同有兩種情況。

  • victim為0
    1.表示small bin還沒有初始化為雙向循環(huán)鏈表,調(diào)用malloc_consolidete()函數(shù)將fast bins中的chunk合并。
  • victim不為0
    1.設(shè)置victim chunk的inuse標(biāo)志,該標(biāo)志處于vimctim chunk的下一個(gè)相鄰chunk的size字段的第一個(gè)bit。2.做與unlink類似的操作將chunk從small bin中脫鏈。

4.判斷當(dāng)前分配區(qū)是否屬于非主分配區(qū),如果是,將victim chunk的size字段中的標(biāo)志非主分配區(qū)的標(biāo)志bit清零。5.調(diào)用chunk2mem()函數(shù)獲得chunk的實(shí)際可用的內(nèi)存指針,將該內(nèi)存指針返回給應(yīng)用層。

check

申請(qǐng)的chunk需滿足chunk->bk->fd = chunk

分配large bin chunk

/*
If this is a large request, consolidate fastbins before continuing.
While it might look excessive to kill all fastbins before
even seeing if there is space available, this avoids
fragmentation problems normally associated with fastbins.
Also, in practice, programs tend to have runs of either small or
large requests, but less often mixtures, so consolidation is not
invoked all that often in most programs. And the programs that
it is called frequently in otherwise tend to fragment.
*/

else
{
idx = largebin_index(nb);
if (have_fastchunks(av))
malloc_consolidate(av);
}

所需chunk不屬于small bins,那么就在large bins的范圍內(nèi),則

1.根據(jù)chunk的大小獲得對(duì)應(yīng)large bin的index

2.判斷當(dāng)前分配區(qū)的fast bins中是否包含chunk,如果存在,調(diào)用malloc_consolidate()函數(shù)合并fast bins中的chunk,并將這些空閑chunk加入unsorted bin中。

下面的源代碼實(shí)現(xiàn)從last remainder chunk,large bins和top chunk中分配所需的chunk,這里包含了多個(gè)多層循環(huán),在這些循環(huán)中,主要工作是分配前兩步都未分配成功的small bin chunk、large bin chunk和large chunk。最外層的循環(huán)用于重新嘗試分配small bin chunk,因?yàn)槿绻谇耙徊椒峙鋝mallbin chunk不成功,并沒有調(diào)用malloc_consolidate()函數(shù)合并fast bins中的chunk,將空閑chunk加入unsorted bin中,如果第一嘗試從last remainder chunk、top chunk中分配small bin chunk都失敗以后,如果fast bins中存在空閑chunk,會(huì)調(diào)用malloc_consolidate()函數(shù),那么在usorted bin中就可能存在合適的small bin chunk供分配,所以需要再次嘗試。

/*
Process recently freed or remaindered chunks, taking one only if
it is exact fit, or, if this a small request, the chunk is remainder from
the most recent non - exact fit. Place other traversed chunks in
bins. Note that this step is the onl y place in any routine where
chunks are placed in bins.


The outer loop here is needed because we might not realize until
near the end of malloc that we should have consolidated, so must
do so and retry. This happens at most once, and only when we would
otherwise need to expand memory to service a "small" request.
*/
for(;;)
{
int iters = 0;
while ( (victim = unsorted_chunks(av)->bk) != unsorted_chunks(av))
{

反向遍歷unsorted bin的雙向鏈表,遍歷結(jié)束的條件是循環(huán)鏈表中只剩一個(gè)頭節(jié)點(diǎn)。

bck = victim->bk;
if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)
|| __builtin_expect (victim->size > av->system_mem, 0))
malloc_printerr (check_action, "malloc(): memory corruption",
chunk2mem (victim));
size = chunksize(victim);

1.檢查當(dāng)前遍歷的chunk是否合法,chunk的大小不能小于等于2*SIZE_SZ,也不能超過該分配區(qū)總的內(nèi)存分配量。

2.獲取chunk的大小并賦值給size。

if (in_smallbin_range(nb) &&
bck == unsorted_chunks(av) &&
victim == av->last_remainder &&
(unsigned long)(size) > (unsigned long)(nb + MINSIZE))
{

如果需要分配一個(gè)small bin chunk,并且unsorted bin中只有一個(gè)chunk,并且這個(gè)chunk為last remainder chunk,并且這個(gè)chunk的大小大于所需chunk的大小加上MINSIZE,在滿足這些條件的情況下,可以使用這個(gè)chunk切分出需要的small bin chunk。這是唯一的從unsorted bin中分配small bin chunk的情況。

/* split and reattach remainder */
remainder_size = size - nb;
remainder = chunk_at_offset(victim, nb);
unsorted_chunks(av)->bk = unsorted_chunks(av)->fd = remainder;
av->last_remainder = remainder;
remainder->bk = remainder->fd = unsorted_chunks(av);
if (!in_smallbin_range(remainder_size))
{
remainder->fd_nextsize = NULL;
remainder->bk_nextsize = NULL;
}

1.從chunk中切分出所需大小的chunk。

2.計(jì)算切分后剩下chunk的大小,將剩下的chunk加入unsorted bin的鏈表中,并將剩下的chunk作為分配區(qū)的last remainder chunk。

3.如果剩下的chunk屬于large bin chunk,將該chunk的fd_nextsize和bk_nextsize設(shè)置為NULL,因?yàn)檫@個(gè)chunk僅僅存在于unsorted bin中,并且unsorted bin中有且僅有這一個(gè)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);
if (__builtin_expect (perturb_byte, 0))
alloc_perturb (p, bytes);
return p;
}

設(shè)置分配出的chunk和last remainder chunk的相關(guān)信息。,調(diào)用chunk2mem()獲得chunk中可用的內(nèi)存指針,返回給應(yīng)用層,退出。

/* remove from unsorted list */
unsorted_chunks(av)->bk = bck;
bck->fd = unsorted_chunks(av);

將雙向循環(huán)鏈表中的最后一個(gè)chunk移除。

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);
if (__builtin_expect (perturb_byte, 0))
alloc_perturb (p, bytes);
return p;
}

如果當(dāng)前chunk與所需的chunk大小一致 1.設(shè)置當(dāng)前chunk處于inuse狀態(tài) 2.設(shè)置分配區(qū)標(biāo)志位 3.調(diào)用chunk2mem()獲得chunk中的可用內(nèi)存指針,返回給應(yīng)用層。

/* place chunk in bin */
if (in_smallbin_range(size))
{
victim_index = smallbin_index(size);
bck = bin_at(av, victim_index);
fwd = bck->fd;
}

如果當(dāng)前chunk屬于small bins,獲得當(dāng)前chunk所屬small bin的index,并將該small bin的鏈表表頭復(fù)制給bck,第一個(gè)chunk賦值給fwd,也就是當(dāng)前的chunk會(huì)插入到bck和fwd之間,作為small bin比鏈表的第一個(gè)chunk。

else
{
victim_index = largebin_index(size);
bck = bin_at(av, victim_index);
fwd = bck->fd;

如果當(dāng)前chunk屬于large bins,獲得當(dāng)前chunk所屬large bin的index,并將該large bin的鏈表表頭賦值給bck,第一個(gè)chunk賦值給fwd,也就是當(dāng)前的chunk會(huì)插入到bck和fwd之間,作為large bin鏈表的第一個(gè)chunk。

/* maintain large bins in sorted order */
if (fwd != bck)
{
/* Or with inuse bit to speed comparisons */
size |= PREV_INUSE;
/* if smalle r than smallest, bypass loop below */
assert((bck->bk->size & NON_MAIN_ARENA) == 0);

如果fwd不等于bck,意味著large bin中有空閑chunk存在,由于large bin中的空閑chunk是按照大小排序的,需要將當(dāng)前從unsorted bin中取出的chunk插入到large bin中合適的位置。將當(dāng)前的chunk的size的inuse標(biāo)志bit置位,相當(dāng)于加1,便于加快chunk大小的比較,找到合適的地方插入當(dāng)前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;
}

如果當(dāng)前的chunk比large bin的最后一個(gè)chunk的大小還小,那么當(dāng)前chunk1就插入到large bin的鏈表的最后,作為最后一個(gè)chunk。可以看出large bin中的chunk是按照從大到小的順序排序的,同時(shí)一個(gè)chunk存在于兩個(gè)雙向循環(huán)鏈表中,一個(gè)鏈表包含了large bin中所有的chunk,另一個(gè)鏈表為chunk size鏈表,該鏈表從每個(gè)相同大小的chunk中取除第一個(gè)chunk按照大小順序鏈接在一起,便于一次跨域多個(gè)相同大小的chunk遍歷下一個(gè)不同大小的chunk,這樣可以加快在large bin鏈表中的遍歷速度。

else
{
assert((fwd->size & NON_MAIN_ARENA) == 0);
while ((unsigned long) size < fwd->size)
{
fwd = fwd->fd_nextsize;
assert((fwd->size & NON_MAIN_ARENA) == 0);
}

正向遍歷chunk size鏈表,直到找到第一個(gè)chunk大小小于等于當(dāng)前chunk大小的chunk退出循環(huán)。

if ((unsigned long) size == (unsigned long) fwd->size)
/* Always insert in the second position. */
fwd = fwd->fd;

如果從large bin鏈表中找到了與當(dāng)前chunk大小相同的chunk,則統(tǒng)一大小的chunk已經(jīng)存在,那么chunk size一定包含了fwd指向的chunk,為了不久改chunk size鏈表,當(dāng)前chunk只能插入fwd之后。

else
{
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
}

如果chunk size鏈表中還沒有包含當(dāng)前chunk大小的chunk,也就是說當(dāng)前chunk的大小大于fwd的大小,則將當(dāng)前chunk作為該chunk size的代表加入chunk size鏈表,chunk size鏈表也是按照由大到小的順序排序。

bck = fwd->bk;
}
}
else
victim->fd_nextsize = victim->bk_nextsize = victim;
}

如果large bin鏈表中沒有chunk,直接將當(dāng)前chunk加入chunk size鏈表。

mark_bin(av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;

將當(dāng)前chunk插入到對(duì)應(yīng)的空閑的chunk鏈表中,并將large bin所對(duì)應(yīng)binmap的相應(yīng)bit置位。

#define MAX_ITERS 10000
if (++iters >= MAX_ITERS)
break;
}

如果unsorted bin中的chunk超過了10000個(gè),最多遍歷一萬個(gè)就退出,避免長(zhǎng)時(shí)間處理unsorted bin影響內(nèi)存分配的效率。當(dāng)unsorted bin中的空閑chunk加入到相應(yīng)的small bins和large bins后,將使用最佳匹配法分配large bin chunk。

/*
If a large request, scan through the chunks of current bin in
sorted order to find smallest that fits. Use the skip list for this.
*/
if (!in_smallbin_range(nb))
{
bin = bin_at(av, idx);

/* skip scan if empty or largest chunk is too small */
if ((victim = first(bin)) != bin &&
(unsigned long)(victim->size) >= (unsigned long)(nb))
{

如果所需分配的chunk為large bin chunk,查詢對(duì)應(yīng)的large bin鏈表,如果large bin鏈表為空,或者鏈表中最大的chunk也不能滿足要求,則不能從large bin中分配。否則,遍歷large bin鏈表,找到合適的chunk。

victim = victim->bk_nextsize;
while (((unsigned long)(size = chunksize(victim)) <
(unsigned long)(nb)))
victim = victim->bk_nextsize;

反向遍歷chunk size鏈表,直到找到第一個(gè)大于等于所需chunk大小的chunk退出循環(huán)。

/* Avoid removing the first entry for a size so that the skip
list does not have to be rerouted. */
if (victim != last(bin) && victim->size == victim->fd->size)
victim = victim->fd;

如果large bin鏈表中選取的chunk civtim不是鏈表中的最后一個(gè)chunk,并且與victim大小相同的chunk不止一個(gè),那么意味著victim為chunk size鏈表中的節(jié)點(diǎn),為了不調(diào)整chunk size鏈表,需要避免將chunk size鏈表中取出,所以取victim->fd節(jié)點(diǎn)對(duì)應(yīng)的chunk作為候選chunk。由于large bin鏈表中的chunk也是按照大小排序,同一大小的chunk有多個(gè)時(shí),這些chunk必定排在一起,所以victim->fd節(jié)點(diǎn)對(duì)應(yīng)的chunk的大小必定與victim的大小一樣。

remainder_size = size - nb;
unlink(victim, bck, fwd);

計(jì)算將victim切分后剩余大小,并調(diào)用unlink()宏函數(shù)將victim從large bin鏈表中取出。

if (remainder_size < MINSIZE)
{
set_inuse_bit_at_offset(victim, size);
if (av != &main_arena)
victim->size |= NON_MAIN_ARENA;
}

如果將victim切分后剩余大小小于MINSIZE,則將整個(gè)victim分配給應(yīng)用層,設(shè)置victim的inuse標(biāo)志,inuse標(biāo)志位于下一個(gè)相鄰的chunk的size字段中。如果當(dāng)前分配區(qū)不是主分配區(qū),將victim的size字段中的非主分配區(qū)標(biāo)志置位。

/* Split */
else
{
remainder = chunk_at_offset(victim, nb);
/* We cannot assume the unsorted list is empty and therefore
have to perform a complete insert here. */
bck = unsorted_chunks(av);
fwd = bck->fd;
if (__builtin_expect (fwd->bk != bck, 0))
{
errstr = "malloc(): corrupted unsorted chunks";
goto errout;
}
remainder->bk = bck;
remainder->fd = fwd;
bck->fd = remainder;
fwd->bk = remainder;
if (!in_smallbin_range(remainder_size))
{
remainder->fd_nextsize = NULL;
remainder->bk_nextsize = NULL;
}

從victim中切分出所需的chunk,剩余部分作為一個(gè)新的chunk加入到unsorted bin中。如果剩余部分chunk屬于large bins,將剩余部分chunk的chunk size鏈表指針設(shè)置為NULL,因?yàn)閡nsorted bin中的chunk是不排序的,這兩個(gè)指針無用,必須清零。

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);
}

設(shè)置victim和remainder的狀態(tài),由于remainder為空閑chunk,所以需要設(shè)置該chunk的foot。

check_malloced_chunk(av, victim, nb);
void *p = chunk2mem(victim);
if (__builtin_expect (perturb_byte, 0))
alloc_perturb (p, bytes);
return p;
}
}

從large bin中使用最佳匹配法找到了合適的chunk,調(diào)用chunk2mem()獲得chunk中可用的內(nèi)存指針,返回給應(yīng)用層,退出。如果通過上面的方式從最合適的small bin或large bin中都沒有分配到需要的chunk,則查看比當(dāng)前bin的index大的small bin或large bin是否有空閑chunk可利用來分配所需的chunk。源代碼實(shí)現(xiàn)如下:

++idx;
bin = bin_at(av,idx);
block = idx2block(idx);
map = av->binmap[block];
bit = idx2bit(idx);

獲取下一個(gè)相鄰bin的空閑chunk鏈表,并獲取該bin對(duì)于binmap中的bit位的值。Binmap中的標(biāo)識(shí)了相應(yīng)的bin中是否有空閑 chunk 存在。Binmap按block管理,每個(gè)block為一個(gè)int,共32個(gè)bit,可以表示32個(gè)bin中是否有空閑chunk存在。使用binmap可以加快查找bin是否包含空閑chunk。這里只查詢比所需chunk大的bin中是否有空閑chunk 可用。

for (;;)
{
/* Skip rest of block if there are no more set bits in this block. */
if (bit > map || bit == 0)
{
do
{
if (++block >= BINMAPSIZE) /* out of bins */
goto use_top;
}while ( (map = av->binmap[block]) == 0);
bin = bin_at(av, (block << BINMAPSHIFT));
bit = 1;
}

Idx2bit()宏將idx指定的位設(shè)置為1,其它位清零,map表示一個(gè)block值,如果bit大于map,意味著map為0,該block所對(duì)應(yīng)的所有bins中都沒有空閑chunk,于是遍歷binmap的下一個(gè)block,直到找到一個(gè)不為0的block或者遍歷完所有的 block。退出循環(huán)遍歷后,設(shè)置 bin 指向 block 的第一個(gè) bit 對(duì)應(yīng)的 bin,并將 bit 置為 1,表示該 block 中 bit 1 對(duì)應(yīng)的 bin,這個(gè) bin 中如果有空閑 chunk,該 chunk 的大小一定滿足要求。

/* Advance to bin with set bit. There must be one. */
while ((bit & map) == 0)
{
bin = next_bin(bin);
bit <<= 1;
assert(bit != 0);
}
/* Inspect the bin. It is likely to be non - empty */
victim = last(bin);

在一個(gè)block遍歷對(duì)應(yīng)的bin,直到找到一個(gè)bit不為0退出遍歷,則該bit對(duì)于的bin中有空閑chunk存在。將bin鏈表中的最后一個(gè)chunk賦值為victim。

/* If a false alarm (empty bin), clear the bit. */
if (victim == bin)
{
av->binmap[block] = map &= ~bit;
/* Write through */
bin = next_bin(bin);
bit <<= 1;
}

如果victim與bin鏈表頭指針相同,表示該bin中沒有空閑chunk,binmap中的相應(yīng)位設(shè)置不準(zhǔn)確,將binmap的相應(yīng)bit位清零,獲取當(dāng)前bin下一個(gè)bin,將bit移到下一個(gè)bit位,即乘以2。

else
{
size = chunksize(victim);
/* We know the first chunk in this bin is big enough to use. */
assert((unsigned long)(size) >= (unsigned long)(nb));
remainder_size = size - nb;
/* unlink */
unlink(victim, bck, fwd);

當(dāng)前bin中的最后一個(gè)chunk滿足要求,獲取該chunk的大小,計(jì)算切分出所需chunk后剩余部分的大小,然后將victim從bin的鏈表中取出。

/* Exhaust */
if (remainder_size < MINSIZE)
{
set_inuse_bit_at_offset(victim, size);
if (av != &main_arena)
victim->size |= NON_MAIN_ARENA
}

如果剩余部分的大小小于MINSIZE,將整個(gè)chunk分配給應(yīng)用層(代碼在后面),設(shè)置victim的狀態(tài)為inuse,如果當(dāng)前分配區(qū)為非分配區(qū),設(shè)置victim的非主分配區(qū)標(biāo)志位。

/* Split */
else
{
remainder = chunk_at_offset(victim, nb);
/* We cannot assume the unsorted list is empty and therefore
have to perform a complete insert here. */
bck = unsorted_chunks(av);
fwd = bck->fd;
if (__builtin_expect (fwd->bk != bck, 0))
{
errstr = "malloc(): corrupted unsorted chunks 2";
goto errout;
}
remainder->bk = bck;
remainder->fd = fwd;
bck->fd = remainder;
fwd->bk = remainder;
/* advertise as last remainder */
if (in_smallbin_range(nb))
av->last_remainder = remainder;
if (!in_smallbin_range(remainder_size))
{
remainder->fd_nextsize = NULL;
remainder->bk_nextsize = NULL;
}

從victim中切分出所需的chunk,剩余部分作為一個(gè)新的chunk加入到unsorted bin中。如果剩余部分chunk屬于large bins,將剩余部分chunk的chunk size鏈表指針設(shè)置為NULL,因?yàn)閡nsorted bin中的chunk是不排序的,這兩個(gè)指針無用,必須清零。

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);
}

設(shè)置victim和remainder的狀態(tài),由于remainder為空閑chunk,所以需要設(shè)置該chunk的foot。

check_malloced_chunk(av, victim, nb);
void *p = chunk2mem(victim);
if (__builtin_expect (perturb_byte, 0))
alloc_perturb (p, bytes);
return p;
}
}

調(diào)用chunk2mem()獲得chunk中可用的內(nèi)存指針,返回給應(yīng)用層,退出。如果從所有的bins中都沒有獲得所需的chunk,可能的情況為bins中沒有空閑chunk,或者所需的chunk大小很大,下一步將嘗試從top chunk中分配所需chunk。源代碼實(shí)現(xiàn)如下:

use_top:
victim = av->top;
size = chunksize(victim);

將當(dāng)前分配區(qū)的top chunk賦值給victim,并獲得victim的大小。

if ((unsigned long)(size) >= (unsigned long)(nb + MINSIZE))
{
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);
if (__builtin_expect (perturb_byte, 0))
alloc_perturb (p, bytes);
return p;
}

由于top chunk切分出所需chunk后,還需要MINSIZE的空間來作為fencepost,所需必須滿足top chunk的大小大于所需chunk的大小加上MINSIZE這個(gè)條件,才能從top chunk中分配所需chunk。從top chunk切分出所需chunk的處理過程跟前面的chunk切分類似,不同的是,原top chunk切分后的剩余部分將作為新的top chunk,原top chunk的fencepost仍然作為新的top chunk的fencepost,所以切分之后剩余的chunk不用set_foot。

#ifdef ATOMIC_FASTBINS
/* When we are using atomic ops to free fast chunks we can get
here for all block sizes. */
else if (have_fastchunks(av))
{
malloc_consolidate(av);
/* restore original bin index */
if (in_smallbin_range(nb))
idx = smallbin_index(nb);
else
idx = largebin_index(nb);
}

如果top chunk也不能滿足要求,查看fast bins中是否有空閑chunk存在,由于開啟了ATOMIC_FASTBINS優(yōu)化情況下,free屬于fast bins的chunk時(shí)不需要獲得分配區(qū)的鎖,所以在調(diào)用_int_malloc()函數(shù)時(shí),有可能有其它線程已經(jīng)向fast bins中加入了新的空閑chunk,也有可能是所需的chunk屬于small bins,但通過前面的步驟都沒有分配到所需的chunk,由于分配small bin chunk時(shí)在前面的步驟都不會(huì)調(diào)用malloc_consolidate()函數(shù)將fast bins中的 chunk合并加入到unsorted bin中。所在這里如果fast bin中有chunk存在,調(diào)用malloc_consolidate()函數(shù),并重新設(shè)置當(dāng)前bin的index。并轉(zhuǎn)到最外層的循環(huán),嘗試重新分配small bin chunk或是large bin chunk。如果開啟了ATOMIC_FASTBINS優(yōu)化,有可能在由其它線程加入到fast bins中的chunk被合并后加入unsorted bin中,從unsorted bin中就可以分配出所需的large bin chunk了,所以對(duì)沒有成功分配的large bin chunk也需要重試。

#else
else if (have_fastchunks(av))
{
assert(in_smallbin_range(nb));
malloc_consolidate(av);
idx = smallbin_index(nb); /* restore original bin index */
}

如果top chunk也不能滿足要求,查看fast bins中是否有空閑chunk存在,如果fast bins中有空閑chunk存在,在沒有開啟ATOMIC_FASTBINS優(yōu)化的情況下,只有一種可能,那就是所需的chunk屬于small bins,但通過前面的步驟都沒有分配到所需的small bin chunk,由于分配small bin chunk時(shí)在前面的步驟都不會(huì)調(diào)用malloc_consolidate()函數(shù)將fast bins中的空閑chunk合并加入到unsorted bin中。所在這里如果fast bins中有空閑chunk存在,調(diào)用 malloc_consolidate()函數(shù),并重新設(shè)置當(dāng)前bin的index。并轉(zhuǎn)到最外層的循環(huán),嘗試重新分配small bin chunk。

else
{
void *p = sYSMALLOc(nb, av);
if (p != NULL && __builtin_expect (perturb_byte, 0))
alloc_perturb (p, bytes);
return p;
}
}
}

山窮水盡了,只能想系統(tǒng)申請(qǐng)內(nèi)存了。sYSMALLOc()函數(shù)可能分配的chun 包括small bin chunk,large bin chunk 和large chunk。

check

1.fastbin頭部的chunk的idx與fastbin的idx要一致。2.unsorted bin中的chunk1大小不能小于等于2*SIZE_SZ,也不能超過該分配區(qū)總的內(nèi)存分配量。3.將chunk從unsorted bin取出放入small bin和large bin時(shí)用到了unlink()宏,注意繞過unlink()宏中的檢測(cè)。4.切出的remainder在重新放入unsorted bin時(shí)需要滿足 unsorted_chunks(av)->fd->bk = unsorted_chunks(av)。

總結(jié)

malloc分配步驟大致如下:1.檢查有沒有_malloc_hook,有則調(diào)用hook函數(shù)。2.獲得分配區(qū)的鎖,調(diào)用函數(shù)_int_malloc()分配內(nèi)存。3.如果申請(qǐng)大小在fast bin范圍內(nèi),則從fast bin分配chunk,成功則返回用戶指針,否則進(jìn)行下一步。(當(dāng)對(duì)應(yīng)的bin為空時(shí),就會(huì)跳過第5步操作) 4.如果申請(qǐng)大小在small bin范圍內(nèi),則從small bin中分配chunk,成功則返回用戶指針,否則進(jìn)行下一步。5.調(diào)用malloc_consolidate()函數(shù)合并fast bin,并鏈接進(jìn)unsorted bin中。6.如果申請(qǐng)大小在small bin范圍內(nèi),且此時(shí)unsorted bin只有一個(gè)chunk,并且這個(gè)chunk為last remainder chunk且大小夠大,則從這個(gè)chunk中切分出需要的大小,成功則返回用戶指針,否則進(jìn)行下一步。7.反向遍歷unsorted bin,如果當(dāng)前chunk與所需chunk大小一致,則分配,成功則返回用戶指針,否則將當(dāng)前chunk放入small bin或者large bin中合適的位置。8.使用最佳匹配算法在large bin中找到合適的chunk進(jìn)行分配,成功則返回用戶指針,否則進(jìn)行下一步。9.到了這一步,說明沒有大小正好合適的chunk,則看看比當(dāng)前bin的index大的small bin或者large bin中有沒有空閑chunk可用來分配。成功則返回用戶指針,否則進(jìn)行下一步。10.嘗試從top chunk中分配,成功則返回用戶指針,否則進(jìn)行下一步。11.如果fast bin中還有chunk,調(diào)用malloc_consolidate()回到第6步(因?yàn)榈?步對(duì)應(yīng)bin為空時(shí)會(huì)跳過第五步,而fast bin合并之后有可能出現(xiàn)能夠分配的small bin)。12.到了這步還不行,則調(diào)用sYSMALLOc()函數(shù)向系統(tǒng)申請(qǐng)內(nèi)存。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 內(nèi)存
    +關(guān)注

    關(guān)注

    8

    文章

    3025

    瀏覽量

    74054
  • Free
    +關(guān)注

    關(guān)注

    0

    文章

    16

    瀏覽量

    11091
  • 源碼
    +關(guān)注

    關(guān)注

    8

    文章

    641

    瀏覽量

    29215
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4331

    瀏覽量

    62622
  • malloc
    +關(guān)注

    關(guān)注

    0

    文章

    52

    瀏覽量

    73
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    怎么使用mallocfree inside函數(shù)

    [4];char[4];char[4][4];char[4][4];char itoa(snum4,a4,10);itoa(snum5,a5,10);char*buf=NULL;buf=malloc
    發(fā)表于 09-05 13:58

    Keil STM32使用malloc/free函數(shù)

    目錄1、Keil STM32 使用 malloc/free 函數(shù)2、使用 memset 函數(shù)1、Keil STM32 使用 malloc/free 函數(shù)1、使用的代碼文件中需要包含頭文
    發(fā)表于 08-24 06:02

    使用malloc()和 free()函數(shù)動(dòng)態(tài)的分配/釋放內(nèi)存的危害

    前言本文會(huì)從以下幾個(gè)方面闡述使用malloc()和 free()函數(shù)動(dòng)態(tài)的分配/釋放內(nèi)存的危害。存在的問題在嵌入式中無法很難實(shí)現(xiàn)對(duì)內(nèi)存的動(dòng)態(tài)映射(虛擬內(nèi)存機(jī)制),尤其是裸機(jī)中。即使在嵌入式操作系統(tǒng)中
    發(fā)表于 12-14 07:56

    RTT系統(tǒng)里用mallocfree還是用rt_malloc和rt_free?同時(shí)用有影響嗎?

    RTT系統(tǒng)里用mallocfree還是用rt_malloc和rt_free,或者都可以用,有什么影響
    發(fā)表于 03-31 11:41

    ARM7的mallocfree函數(shù)是否可以使用

    想請(qǐng)教一下關(guān)于arm7的malloc等函數(shù)的問題.本人使用的是ARM7 AT91SAM7S64的芯片,開發(fā)環(huán)境是ADS1.2.在開發(fā)過程中,想使用mallocfree動(dòng)態(tài)分配部分內(nèi)存。但執(zhí)行到
    發(fā)表于 06-13 16:09

    [slab]偶現(xiàn)malloc/free時(shí)崩潰怎么解決呢

    遇到了崩潰問題,定位到是mallocfree的時(shí)候斷言,都在slab.c中malloc 斷言if ((z = zone_array[zi]) != RT_NULL){
    發(fā)表于 12-19 16:40

    SPC5Studio為什么不能使用stdlib.h標(biāo)準(zhǔn)庫(kù)中的malloc() 和free() 函數(shù)?

    SPC5Studio 不能使用stdlib.h 標(biāo)準(zhǔn)庫(kù)中的malloc() 和free() 函數(shù)。例如:char * str = (char *) malloc(1024);free(
    發(fā)表于 01-31 06:21

    C語言入門教程-malloc函數(shù)和free函數(shù)

    malloc函數(shù)和free函數(shù) 假設(shè)您的程序在執(zhí)行過程中需要分配一定量的內(nèi)存。您可以隨時(shí)調(diào)用malloc函數(shù)從堆中申請(qǐng)一塊內(nèi)存。在操作系統(tǒng)為您的程序預(yù)留出這塊內(nèi)存,之后您
    發(fā)表于 07-29 11:58 ?4653次閱讀

    通過實(shí)現(xiàn)一個(gè)簡(jiǎn)單的malloc來描述malloc背后的機(jī)制

    任何一個(gè)用過或?qū)W過C的人對(duì)malloc都不會(huì)陌生。大家都知道malloc可以分配一段連續(xù)的內(nèi)存空間,并且在不再使用時(shí)可以通過free釋放掉。但是,許多程序員對(duì)malloc背后的事情并不
    的頭像 發(fā)表于 01-27 23:30 ?4209次閱讀
    通過實(shí)現(xiàn)一個(gè)簡(jiǎn)單的<b class='flag-5'>malloc</b>來描述<b class='flag-5'>malloc</b>背后的機(jī)制

    在嵌入式設(shè)備中使用Malloc Hook的試驗(yàn)

    );static void* my_malloc_hook(size_t,const void*);static void my_free_hook(void*,const void *);void
    發(fā)表于 04-02 14:37 ?697次閱讀

    avr-libc malloc/free的實(shí)現(xiàn)

    avr-libc malloc/free的實(shí)現(xiàn)
    發(fā)表于 11-15 16:36 ?4次下載
    avr-libc <b class='flag-5'>malloc</b>/<b class='flag-5'>free</b>的實(shí)現(xiàn)

    mallocfree簡(jiǎn)介及實(shí)現(xiàn)方式說明

    malloc 分配指定大小的內(nèi)存空間,返回一個(gè)指向該空間的指針。大小以字節(jié)為單位。返回 void* 指針,需要強(qiáng)制類型轉(zhuǎn)換后才能引用其中的值。 free 釋放一個(gè)由 malloc 所分配的內(nèi)存空間。ptr 指向一個(gè)要釋放內(nèi)存的內(nèi)
    的頭像 發(fā)表于 05-14 09:56 ?4556次閱讀
    <b class='flag-5'>malloc</b>和<b class='flag-5'>free</b>簡(jiǎn)介及實(shí)現(xiàn)方式說明

    new和malloc函數(shù)詳細(xì)分析底層邏輯

    new操作符從自由存儲(chǔ)區(qū)(free store)上為對(duì)象動(dòng)態(tài)分配內(nèi)存空間,而malloc函數(shù)從堆上動(dòng)態(tài)分配內(nèi)存。自由存儲(chǔ)區(qū)是C++基于new操作符的一個(gè)抽象概念,凡是通過new操作符進(jìn)行內(nèi)存申請(qǐng),該
    的頭像 發(fā)表于 04-03 09:29 ?714次閱讀

    內(nèi)存釋放free步驟

    corresponding to mem */ void (*hook) ( __malloc_ptr_t , __const __malloc_ptr_t ) = force_reg (__free
    的頭像 發(fā)表于 11-09 11:31 ?824次閱讀

    如何實(shí)現(xiàn)一個(gè)malloc

    任何一個(gè)用過或?qū)W過C的人對(duì)malloc都不會(huì)陌生。大家都知道malloc可以分配一段連續(xù)的內(nèi)存空間,并且在不再使用時(shí)可以通過free釋放掉。但是,許多程序員對(duì)malloc背后的事情并不
    的頭像 發(fā)表于 11-13 14:31 ?785次閱讀
    如何實(shí)現(xiàn)一個(gè)<b class='flag-5'>malloc</b>