tcache_init:初始化 tcache_perthread_struct
static void
tcache_init(void)
{
mstate ar_ptr; // arena
void *victim = 0;
const size_t bytes = sizeof(tcache_perthread_struct);
// 是否关闭 tcache
if (tcache_shutting_down)
return;
// 获取arena,如果没有就创建一个
arena_get(ar_ptr, bytes);
// 申请内存,就是tcache_perthread_struct大小
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_perthread_struct结构体为0
tcache = (tcache_perthread_struct *)victim;
memset(tcache, 0, sizeof(tcache_perthread_struct));
}
}
主要是在arena中申请第一块内存,保存到tcache变量中,是tcache_perthread_struct
结构体,每个线程都有自己的该结构体
tcache_perthread_struct 结构体
/* 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;
/* This field exists to detect double frees. */
uintptr_t key;
} tcache_entry;
/* 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
{
uint16_t counts[TCACHE_MAX_BINS];
tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;
static __thread bool tcache_shutting_down = false;
static __thread tcache_perthread_struct *tcache = NULL;
/* Process-wide key to try and catch a double-free in the same thread. */
static uintptr_t tcache_key;
一些宏定义
#if USE_TCACHE
/* We want 64 entries. This is an arbitrary limit, which tunables can reduce. */
#define TCACHE_MAX_BINS 64
#define MAX_TCACHE_SIZE tidx2usize(TCACHE_MAX_BINS - 1)
/* Only used to pre-fill the tunables. */
#define tidx2usize(idx) (((size_t)idx) * MALLOC_ALIGNMENT + MINSIZE - SIZE_SZ)
/* When "x" is from chunksize(). */
#define csize2tidx(x) (((x)-MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)
/* When "x" is a user-provided size. */
#define usize2tidx(x) csize2tidx(request2size(x))
/* With rounding and alignment, the bins are...
idx 0 bytes 0..24 (64-bit) or 0..12 (32-bit)
idx 1 bytes 25..40 or 13..20
idx 2 bytes 41..56 or 21..28
etc. */
/* This is another arbitrary limit, which tunables can change. Each
tcache bin will hold at most this number of chunks. */
#define TCACHE_FILL_COUNT 7
/* Maximum chunks in tcache bins for tunables. This value must fit the range
of tcache->counts[] entries, else they may overflow. */
// 65535
#define MAX_TCACHE_COUNT UINT16_MAX
#endif
几个主要内容:
tidx2usize
计算bin中索引对应的大小csize2tidx
计算指定大小在bin中的索引TCACHE_MAX_BINS
是最大的bin数量,最多可以有64个项默认情况下,tcachebin的最大大小是:0x410TCACHE_FILL_COUNT
指定tcache填满数量
全局变量 mp_
/* There is only one instance of the malloc parameters. */
static struct malloc_par mp_ =
{
.top_pad = DEFAULT_TOP_PAD,
.n_mmaps_max = DEFAULT_MMAP_MAX,
.mmap_threshold = DEFAULT_MMAP_THRESHOLD,
.trim_threshold = DEFAULT_TRIM_THRESHOLD,
#define NARENAS_FROM_NCORES(n) ((n) * (sizeof(long) == 4 ? 2 : 8))
.arena_test = NARENAS_FROM_NCORES(1)
#if USE_TCACHE
,
.tcache_count = TCACHE_FILL_COUNT,
.tcache_bins = TCACHE_MAX_BINS,
.tcache_max_bytes = tidx2usize(TCACHE_MAX_BINS - 1),
.tcache_unsorted_limit = 0 /* No limit. */
#endif
};
tcache的申请
malloc.c
的__libc_malloc
中:从tcache申请内存是链表断链的过程
#if USE_TCACHE // 如果使用tcache
/* int_free also calls request2size, be careful to not pad twice. */
size_t tbytes;
// 获取需要申请的大小,如果超过最大大小(64位0x7FFFFFFFFFFFFFFF,32位0x7FFFFFFF)返回 false
// 如果是标记的内存,做一个处理(待分析)
// 调用request2size()返回申请的大小到tbytes,如果小于最小对齐大小,则返回最小大小,否则返回申请的大小
if (!checked_request2size(bytes, &tbytes))
{
__set_errno(ENOMEM);
return NULL;
}
// 计算 tcache 索引
size_t tc_idx = csize2tidx(tbytes);
// 初始化tcache
MAYBE_INIT_TCACHE();
DIAG_PUSH_NEEDS_COMMENT;
// tcache 索引
if (tc_idx < mp_.tcache_bins && tcache && tcache->counts[tc_idx] > 0)
{
// 从tcachebin中对应的索引处断链取出一个entry
victim = tcache_get(tc_idx);
return tag_new_usable(victim);
}
DIAG_POP_NEEDS_COMMENT;
#endif
-
计算申请的大小对应tcachebin索引号
-
如果tcache结构体没初始化,就初始化(会在第一次申请内存的时候初始化)
-
如果tcache索引号,小于最大bin数量,且tcache初始化完毕了,且该索引号对应的剩余大小大于0,就可以进行申请
- 从tcachebin中对应的索引处断链取出一个entry
- 如果启用了tag相关的选项,会进行一些处理
- 返回取出来的地址
tcache_get
/* 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];
// 内存对齐检查
if (__glibc_unlikely(!aligned_OK(e)))
malloc_printerr("malloc(): unaligned tcache chunk detected");
// 取出链表下一个到 entries
tcache->entries[tc_idx] = REVEAL_PTR(e->next);
// counts 减少
--(tcache->counts[tc_idx]);
// key清零
e->key = 0;
return (void *)e;
}
这里是断链取出entry的过程,这里有个内存对齐检查:
#define aligned_OK(m) (((unsigned long)(m) & MALLOC_ALIGN_MASK) == 0)
// sysdeps/generic/malloc-size.h
/* The corresponding bit mask value. */
#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)
tag_new_usable
static __always_inline void *
tag_new_usable(void *ptr)
{
// 如果mtag_enabled=1,ptr有值
if (__glibc_unlikely(mtag_enabled) && ptr)
{
// 获取 chunk header ptr
mchunkptr cp = mem2chunk(ptr);
// tag 相关处理
ptr = __libc_mtag_tag_region(__libc_mtag_new_tag(ptr), memsize(cp));
}
// 返回 ptr
return ptr;
}