malloc()
in glibc 2.23_int_malloc()
Request size가 fastbin size에 해당되지 않을 경우 다음으로 smallbin size에 해당되는지 확인하는 routine을 거칩니다. 구현 코드는 다음과 같습니다.
3405 if (in_smallbin_range (nb))
3406 {
3407 idx = smallbin_index (nb);
3408 bin = bin_at (av, idx);
3409
3410 if ((victim = last (bin)) != bin)
3411 {
3412 if (victim == 0) /* initialization check */
3413 malloc_consolidate (av);
3414 else
3415 {
3416 bck = victim->bk;
3417 if (__glibc_unlikely (bck->fd != victim))
3418 {
3419 errstr = "malloc(): smallbin double linked list corrupted";
3420 goto errout;
3421 }
3422 set_inuse_bit_at_offset (victim, nb);
3423 bin->bk = bck;
3424 bck->fd = bin;
3425
3426 if (av != &main_arena)
3427 victim->size |= NON_MAIN_ARENA;
3428 check_malloced_chunk (av, victim, nb);
3429 void *p = chunk2mem (victim);
3430 alloc_perturb (p, bytes);
3431 return p;
3432 }
3433 }
3434 }
Request size가 전체 smallbin range에 해당되는지 확인합니다. 만약 해당되지 않을 경우 Largebin 범위로 넘어가며, 해당될 경우 몇 가지 초기화 작업을 진행합니다.
smallbin_index()
매크로를 호출하여 request size가 해당되는 smallbin의 index를 구합니다. Smallbin의 자세한 구조는 별도의 게시글에서 정리 후 추가할 예정입니다.
다음으로 현재 arena에서 idx에 해당되는 bin을 가져옵니다.
last()
매크로의 경우 해당 bin에서 가장 마지막 chunk를 가져옵니다. 따라서 victim에 저장된 값이 현재 bin의 주소와 같지 않을 경우 사용 가능한 freed chunk가 존재함을 의미합니다. Freed chunk가 존재하지 않을 경우 아래의 과정을 진행하지 않고 구문으로 이동합니다.
만약 victim이 '0' 일 경우는 initialization 과정에 해당되며 malloc_consolidate()
함수를 호출합니다.
bck
에 victim -> bk
값을 저장한 뒤 bck -> fd
값이 'victim' 과 일치하는지 확인합니다. 이 과정은 Doubly Linked List가 변조되지 않았는지 확인하는 과정으로, 정상적인 smallbin의 경우 현재 chunk의 이전 chunk는 당연히 'fd' 값으로 현재 chunk를 가리켜야 함을 의미합니다.
적합한 Free chunk가 존재할 경우 이를 Allocated Chunk로 변환하기 위한 과정이 수행됩니다.
먼저 set_inuse_bit_at_offset()
매크로를 호출하여 할당할 chunk와 인접한 다음 chunk의 prev_inuse
flag를 set 상태로 변경합니다. 이는 smallbin의 경우 free 과정에서 prev_inuse
flag를 unset 하기 때문에 수행됩니다.
다음으로 bin -> bk
를 bck
로 변경하고 bck -> fd
를 bin
으로 변경합니다. 이는 double linked list에서 할당 대상인 'victim' 을 제거하는 역할을 합니다.
마지막으로 해당 chunk가 main_arena에 속하는지 여부를 검사한 뒤에 최종적으로 'victim' 의 mem
주소를 반환합니다.