/* We overlay this structure on the user-data portion of a chunk when
the chunk is stored in the per-thread cache. */
typedef struct tcache_entry
{
struct tcache_entry *next;
} tcache_entry;
tcache_entry 구조체의 모습이다. next라는 이름의 포인터밖에 없다.
/* There is one of these for each thread, which contains the
per-thread cache (hence "tcache_perthread_struct"). Keeping
overall size low is mildly important. Note that COUNTS and ENTRIES
are redundant (we could have just counted the linked list each
time), this is for performance reasons. */
typedef struct tcache_perthread_struct
{
char counts[TCACHE_MAX_BINS];
tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;
tcache_perthread_struct 구조체의 모습이다. counts에는 free된 chunk의 갯수를 저장하는 배열이다. entries 포인터 배열은 가장 최근에 free된 청크의 주소를 기억한다.
static __thread bool tcache_shutting_down = false;
static __thread tcache_perthread_struct *tcache = NULL;
위에꺼는 tcache가 종료?되었는지를 나타내는 변수이다. true면 tcache가 종료되었다는 말이다. 아래의 것은 다양한 함수(tcache에 넣는거, 제거하는거 등)에 쓰일 변수 초기화이다.
/* Caller must ensure that we know tc_idx is valid and there's room
for more chunks. */
static __always_inline void
tcache_put (mchunkptr chunk, size_t tc_idx)
{
tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
assert (tc_idx < TCACHE_MAX_BINS);
e->next = tcache->entries[tc_idx];
tcache->entries[tc_idx] = e;
++(tcache->counts[tc_idx]);
tcache안에 넣을 때이다. chunk2mem 함수는 인자 + 2 * size_t 이다. 즉 chunk의 fd부분을 반환한다.
tc_idx즉 넣을 청크의 index가 최대 크기를 넘는지 검사한다.
e->next가 의미하는 바는 tcache_entry 구조체의 모습을 위에서 보았다면 알 수 있는데 e에는 대상 청크의 fd부분의 주소가 담겨져 있다, 따라서 e -> next가 의미하는 바는 대상 청크의 fd부분이다. 거기에 원래 있던 tc_idx에 따른 entries의 값을 넣는다.
그런 뒤 tc_idx에 따른 entries배열에 대상청크를 넣어줌으로서 linked list 형태가 만들어지게 된다.
마지막에 tc_idx에 따른 count를 증가시켜준다.
/* Caller must ensure that we know tc_idx is valid and there's
available chunks to remove. */
static __always_inline void *
tcache_get (size_t tc_idx)
{
tcache_entry *e = tcache->entries[tc_idx];
assert (tc_idx < TCACHE_MAX_BINS);
assert (tcache->entries[tc_idx] > 0);
tcache->entries[tc_idx] = e->next;
--(tcache->counts[tc_idx]);
return (void *) e;
}
tcache_get부분인데 이 부분은 청크가 tcache에서 제거될 때 사용된다.
tc_idx에 따른 entries배열의 값을 뽑아와서 e에 저장된다. 여기서 tcache는 LIFO방식이라는 것을 알 수 있다. 왜냐하면 entries배열에는 가장 최근에 free된 청크의 주소를 저장하고 있기 때문이다.
tc_idx가 최대갯수를 넘으면 프로그램을 종료시킨다.
올바른 주소값을 얻지 못하면 프로그램을 종료시킨다.
entries 배열에 e -> next 즉 2번째로 최근에 free된 함수를 넣어준다.
그런뒤 갯수를 1감소시킨다.
static void
tcache_thread_shutdown (void)
{
int i;
tcache_perthread_struct *tcache_tmp = tcache;
if (!tcache)
return;
/* Disable the tcache and prevent it from being reinitialized. */
tcache = NULL;
tcache_shutting_down = true;
/* Free all of the entries and the tcache itself back to the arena
heap for coalescing. */
for (i = 0; i < TCACHE_MAX_BINS; ++i)
{
while (tcache_tmp->entries[i])
{
tcache_entry *e = tcache_tmp->entries[i];
tcache_tmp->entries[i] = e->next;
__libc_free (e);
}
}
__libc_free (tcache_tmp);
}
다음으로는 tcache를 완전히 종료시킬때이다.
종료시키기 전에 tcahe_tmp에다가 tcache를 임시저장한다.
tcache의 값이 NULL이라면 tcache가 이미 종료되어있다는 것이므로 함수를 종료한다.
tcache에 NULL값을 넣어준다.
tcache가 꺼졌다는 표시로 tcache_shutting_down에 참을 셋팅해준다.
다음에는 모든 tcache의 chunk를 free하는 과정이다.
마지막으로는 tcache_tmp를 free하면서 함수가 종료된다.
static void
tcache_init(void)
{
mstate ar_ptr;
void *victim = 0;
const size_t bytes = sizeof (tcache_perthread_struct);
if (tcache_shutting_down)
return;
arena_get (ar_ptr, bytes);
victim = _int_malloc (ar_ptr, bytes);
if (!victim && ar_ptr != NULL)
{
ar_ptr = arena_get_retry (ar_ptr, bytes);
victim = _int_malloc (ar_ptr, bytes);
}
if (ar_ptr != NULL)
__libc_lock_unlock (ar_ptr->mutex);
/* In a low memory situation, we may not be able to allocate memory
- in which case, we just keep trying later. However, we
typically do this very early, so either there is sufficient
memory, or there isn't enough memory to do non-trivial
allocations anyway. */
if (victim)
{
tcache = (tcache_perthread_struct *) victim;
memset (tcache, 0, sizeof (tcache_perthread_struct));
}
}
tcache_init함수인데 복잡해보여도 기능은 간단하다. tcache가 종료되어있다면 이 함수를 종료하고, arena를 얻어오는 것을 시도. 안되면 다시 시도한다.
성공적으로 얻어왔으면, 얻어온 값들을 memset으로 초기화 시켜준다.
'힙(heap) > glibc' 카테고리의 다른 글
__libc_free함수 분석 (0) | 2019.08.09 |
---|---|
__libc_malloc함수 분석 (0) | 2019.08.09 |
__builtin_expect, likely, unlikely (0) | 2019.08.09 |
__libc_calloc 함수 분석 (0) | 2019.08.09 |
힙 보안검사(지속적으로 추가 예정) (0) | 2019.08.05 |