static void
_int_free (mstate av, mchunkptr p, int have_lock)
{
INTERNAL_SIZE_T size; /* free시킬 크기 */
mfastbinptr *fb; /* 관련된 fastbin */
mchunkptr nextchunk; /* 인접한 청크 */
INTERNAL_SIZE_T nextsize; /* 인접한 청크의 크기 */
int nextinuse; /* 물리적으로 다음 청크가 사용줄일 때 셋팅 */
INTERNAL_SIZE_T prevsize; /* 물리적으로 이전 청크의 크기 */
mchunkptr bck; /* 링킹을 위한 임시 변수 */
mchunkptr fwd; /* 링킹을 위한 임시 변수 */
size = chunksize (p); // free 시킬 청크의 크기를 저장한다.
/* 성능을 해치지 않는 보안검사: 할당자는 절대로 주소 공간의 끝부분에 할당하지 않는다.
그러므로 우리는 우연히 나타나거나 간섭자들에 의해 조작된 몇몇 크기 값들을 결정할 수 있다.*/
if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)// 청크의 포인터가 정상인지 검사
|| __builtin_expect (misaligned_chunk (p), 0))
malloc_printerr ("free(): invalid pointer");
/* 우리는 각 청크가 최소 크기를 가지고 있고, MALLLOC_ALIGNMENT(2 * sizeof(size_t))의
배수라는 것을 안다.*/
if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size)))
malloc_printerr ("free(): invalid size");
check_inuse_chunk(av, p); // 물리적으로 다음 청크의 prev_in_use 비트를 확인한다.
#if USE_TCACHE // TCACHE를 사용할 때 tcache에 넣을 수 있으면 바로 넣는다.
{
size_t tc_idx = csize2tidx (size);
if (tcache
&& tc_idx < mp_.tcache_bins
&& tcache->counts[tc_idx] < mp_.tcache_count)
{
tcache_put (p, tc_idx);
return;
}
}
#endif
/*
fastbin에 넣을 크기라면, fastbin에 넣는다. 그래서 이것은 malloc에서 바로 발견되고, 빠르게 사용된다.
*/
// fastbin에 들어갈 크기인지 검사
if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())
#if TRIM_FASTBINS
/*
만약 TRIM_FASTBINS이 셋팅되어 있다면, top 청크에 인접한 청크를 fastbin에 두지 않는다.
*/
&& (chunk_at_offset(p, size) != av->top)
#endif
) {
// 물리적으로 다음청크의 크기가 정상적인지 검사
if (__builtin_expect (chunksize_nomask (chunk_at_offset (p, size))
<= 2 * SIZE_SZ, 0)
|| __builtin_expect (chunksize (chunk_at_offset (p, size))
>= av->system_mem, 0))
{
bool fail = true;
/* 우린는 이 시점에서 잠금이 걸려있지 않고 시스템 메모리에서 동시에 발생하는 변조는 거짓된 참값을 유발한다.
잠금을 한 뒤에 검사를 나중에 다시 한다. */
if (!have_lock) // 잠금이 걸려있지 않으면
{
__libc_lock_lock (av->mutex); // 잠금을 한다.
fail = (chunksize_nomask (chunk_at_offset (p, size)) <= 2 * SIZE_SZ // 청크 크기 검사
|| chunksize (chunk_at_offset (p, size)) >= av->system_mem);
__libc_lock_unlock (av->mutex); // 잠금 해제한다.
}
if (fail) // 청크 크기 검사에 실패하면
malloc_printerr ("free(): invalid next size (fast)");
}
free_perturb (chunk2mem(p), size - 2 * SIZE_SZ);
atomic_store_relaxed (&av->have_fastchunks, true);
unsigned int idx = fastbin_index(size); // fastbin index
fb = &fastbin (av, idx); // index에 맞는 fastbin주소 반환
/* fastbin의 single linked list를 구성. P->FD = *FB; *FB = P; */
mchunkptr old = *fb, old2; // 가장 최근에 free 했던 청크 저장
if (SINGLE_THREAD_P) //단일 스레드일 때
{
/* bin의 가장 위에 있는 청크(가장 최근에 free한 청크)가 우리가 free할 청크인지 확인한다.
(ex. double free bug) */
if (__builtin_expect (old == p, 0)) // 가장 최근에 free 했던 청크가 free 시킬 청크랑 같다면
malloc_printerr ("double free or corruption (fasttop)");
p->fd = old;
*fb = p;
}
else //단일 스레드가 아니면
do
{
/* bin의 가장 위에 있는 청크(가장 최근에 free한 청크)가 우리가 free할 청크인지 확인한다.
(ex. double free bug)*/
if (__builtin_expect (old == p, 0))
malloc_printerr ("double free or corruption (fasttop)");
p->fd = old2 = old;
}
while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2))
!= old2);
/* fastbin에 가장 나중에 들어간 청크의 크기가 우리가 추가할 청크의 크기와 같은지 확인한다.
우리는 잠금이 걸려있을때만 0LD를 역참조 할 수 있다. 그렇지 않으면, 이미 한번 할당되었을 것이다.
*/
if (have_lock && old != NULL //
&& __builtin_expect (fastbin_index (chunksize (old)) != idx, 0))
malloc_printerr ("invalid fastbin entry (free)");
}
/*
다른 mmap으로 할당되지 않은 청크들을 도착하면 병합한다.
*/
else if (!chunk_is_mmapped(p)) {
/* 단일 스레드면 아레나를 잠그지 않는다. */
if (SINGLE_THREAD_P)
have_lock = true;
if (!have_lock) //잠금이 안걸려 있으면 잠금을 건다.
__libc_lock_lock (av->mutex);
nextchunk = chunk_at_offset(p, size); //물리적으로 다음 청크의 주소를 저장한다.
/* 가벼운 검사: 그 블럭이 이미 top chunk인지 확인 */
if (__glibc_unlikely (p == av->top)) // top chunk를 free하려 할 때
malloc_printerr ("double free or corruption (top)");
/* 또는 물리적으로 다음 청크가 아레나의 경계 밖에 있는지 확인한다. */
if (__builtin_expect (contiguous (av)
&& (char *) nextchunk
>= ((char *) av->top + chunksize(av->top)), 0))
malloc_printerr ("double free or corruption (out)");
/* 또는 그 블럭이 실제로 쓰지 않는다고 되어있는지 확인한다.*/
if (__glibc_unlikely (!prev_inuse(nextchunk)))
malloc_printerr ("double free or corruption (!prev)");
nextsize = chunksize(nextchunk); //다음 청크의 크기 저장&다음 청크의 크기가 정상인지 검사
if (__builtin_expect (chunksize_nomask (nextchunk) <= 2 * SIZE_SZ, 0)
|| __builtin_expect (nextsize >= av->system_mem, 0))
malloc_printerr ("free(): invalid next size (normal)");
free_perturb (chunk2mem(p), size - 2 * SIZE_SZ);
/* 이전 청크가 free된 상태이면 뒤의 청크를 병합시킨다. */
if (!prev_inuse(p)) {
prevsize = prev_size (p);
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
if (__glibc_unlikely (chunksize(p) != prevsize))
malloc_printerr ("corrupted size vs. prev_size while consolidating");
unlink(av, p, bck, fwd);
}
if (nextchunk != av->top) { //다음 청크가 top chunk가 아니면
/* inuse bit를 얻어온다. */
nextinuse = inuse_bit_at_offset(nextchunk, nextsize);
/* 다음 청크와 병합시킨다.*/
if (!nextinuse) {
unlink(av, nextchunk, bck, fwd);
size += nextsize;
} else
clear_inuse_bit_at_offset(nextchunk, 0);
/*
그 청크를 unsorted bin에 넣는다. 청크들은 그들이 malloc에서 1번
재할당될 기회가 주어지기 전까지는 보통 bins에 들어가지 않는다.
*/
/* unsorted bin에 넣는 과정 */
bck = unsorted_chunks(av);
fwd = bck->fd;
if (__glibc_unlikely (fwd->bk != bck))
malloc_printerr ("free(): corrupted unsorted chunks");
p->fd = fwd;
p->bk = bck;
if (!in_smallbin_range(size))
{
p->fd_nextsize = NULL;
p->bk_nextsize = NULL;
}
bck->fd = p;
fwd->bk = p;
set_head(p, size | PREV_INUSE);
set_foot(p, size);
check_free_chunk(av, p);
}
/*
만약 그 청크가 현재 메모리의 끝부분에 접한다면 top chunk와 병합한다.
*/
else {
size += nextsize;
set_head(p, size | PREV_INUSE);
av->top = p;
check_chunk(av, p);
}
/*
만약 큰 공간을 free시킨다면, 인접한 청크를 병합시킨다.그래서 만약 사용되지 않는 메모리의 총량이
trim_threshold를 넘는다면, malloc_trim에게 top chunk를 줄이도록 요청한다.
max_fast가 0이 아니라면, 우리는 top chunk에 인접한 fastbins가 있는지 모른다. 그래서
우리는 fastbins가 병합되지 않는한, 한계가 되어있는지 말할 수 없다. 하지만
우리는 각각이 free될 때마다 병합되는 것을 원치 않는다. 절충안으로서, 병합은
FASTBIN_CONSOLIDATION_THRESHOLD에 도달할 때 행해진다.
*/
if ((unsigned long)(size) >= FASTBIN_CONSOLIDATION_THRESHOLD) {
if (atomic_load_relaxed (&av->have_fastchunks))
malloc_consolidate(av);
if (av == &main_arena) { // main_arena에 속해있는 청크라면
#ifndef MORECORE_CANNOT_TRIM
if ((unsigned long)(chunksize(av->top)) >= //top chunk 크기가 한계를 초과하면
(unsigned long)(mp_.trim_threshold))
systrim(mp_.top_pad, av);
#endif
} else {
/* top chunk가 크지 않을지라도, 항상 heap_trim()함수를 시도한다. 왜냐하면
그에 상응하는 heap이 나가기 떄문이다.
*/
heap_info *heap = heap_for_ptr(top(av));
assert(heap->ar_ptr == av);
heap_trim(heap, mp_.top_pad);
}
}
if (!have_lock) //잠금이 안되어 있다면
__libc_lock_unlock (av->mutex);
}
/*
그 청크가 mmap을 통해 할당되어있다면 munmap을 통해 해제시킨다.
*/
else {
munmap_chunk (p);
}
}