第一次阅读malloc.c源文件,从头向下看记录了一些比较感兴趣的内容。
__malloc_assert
没啥用,针对malloc断言的处理内容,在源码中比较靠前的位置。
static void
__malloc_assert (const char *assertion, const char *file, unsigned int line,
const char *function)
{
(void) __fxprintf (NULL, "%s%s%s:%u: %s%sAssertion `%s' failed.\n",
__progname, __progname[0] ? ": " : "",
file, line,
function ? function : "", function ? ": " : "",
assertion);
fflush (stderr);
abort ();
}
tcache
对于tcache有如下宏定义,
#if USE_TCACHE
/* We want 64 entries. This is an arbitrary limit, which tunables can reduce. */
// 我们想要 64 个条目。这是一个任意限制,可以通过可调参数(TCACHE_MAX_BINS)减少。
# 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(). */
// 当 "x" 来自 chunksize() 。
# define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)
/* When "x" is a user-provided size. */
// 当 "x" 是用户提供的大小时。
# 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. */
// 这是另一个任意限制,可调参数可以更改。每个 tcache 桶最多将容纳此数量的块
# 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. */
// 对于可调参数,tcache 桶中的最大块数。此值必须适合 tcache->counts[] 条目的范围,否则可能会溢出。(没太懂,可能是说tcache大小不能太大?UINT16_MAX应该是 65536)
# define MAX_TCACHE_COUNT UINT16_MAX
#endif
mallocinfo
此版本的 malloc 支持标准的 SVID/XPG mallinfo 例程,该例程返回一个包含使用属性和统计信息的结构。它应该在任何符合 SVID/XPG 的系统上工作,该系统有一个定义 struct mallinfo 的 /usr/include/malloc.h 文件。
/* SVID2/XPG mallinfo structure */
struct mallinfo
{
int arena; /* non-mmapped space allocated from system */
int ordblks; /* number of free chunks */
int smblks; /* number of fastbin blocks */
int hblks; /* number of mmapped regions */
int hblkhd; /* space in mmapped regions */
int usmblks; /* always 0, preserved for backwards compatibility */
int fsmblks; /* space available in freed fastbin blocks */
int uordblks; /* total allocated space */
int fordblks; /* total free space */
int keepcost; /* top-most, releasable (via malloc_trim) space */
};
// 注释
// int arena; /* 从系统分配的非 mmapped 空间 /
// int ordblks; / 空闲块的数量 /
// int smblks; / 快速分配块的数量 /
// int hblks; / mmapped 区域的数量 /
// int hblkhd; / mmapped 区域的空间 /
// int usmblks; / 始终为 0,保留用于向后兼容 /
// int fsmblks; / 被释放的快速分配块可用的空间 /
// int uordblks; / 总分配空间 /
// int fordblks; / 总空闲空间 /
// int keepcost; / 最上层可释放的空间(通过 malloc_trim) */
所需的主要声明是通过 mallinfo() 返回(按值传递)的 mallinfo 结构。SVID/XPG malloinfo 结构包含一些在此版本的 malloc 中甚至没有意义的字段。这些字段则由 mallinfo() 填充其他可能感兴趣的数字。
__libc_malloc
__libc_malloc
是 GNU C 库(glibc)中的一个低级内存分配函数。它的主要功能是从堆中分配指定数量的内存。这个函数通常在库内部使用,并不建议用户直接调用。开发者一般使用标准的 malloc
函数,malloc
函数最终会调用 __libc_malloc
来进行实际的内存分配。
使用 __libc_malloc
的好处在于,glibc 可以更好地管理内存分配,处理内存碎片,并在后台优化性能。因此,虽然用户通常不会直接与 __libc_malloc
交互,但它在内存管理中起到了基础性的重要作用。
他在文件中的原型这样定义:
void* __libc_malloc(size_t);
libc_hidden_proto (__libc_malloc)
值得一提的是libc_hidden_proto
是一个宏,用于定义内部函数,使其在编译时是可用的,但不会暴露在公共接口中,从而防止他们被用户代码直接调用。通常来说,该宏的定义会涉及到条件编译,在使用gcc时可能会使用其特有的__attribute__((visibility("hidden")))
来改变函数符号的可见性。
#ifdef __GNUC__
#define libc_hidden_proto(name) name __attribute__((visibility("hidden")))
#else
#define libc_hidden_proto(name) name
#endif
malloc(size_t n)
返回指向新分配的至少 n 字节的块的指针,如果没有可用空间则返回 null。此外,在失败时,errno 在 ANSI C 系统上被设置为 ENOMEM。
如果 n 为零,malloc 返回一个minmun_sized chunk(在大多数 32 位系统上,最小大小为 16 字节,在 64 位系统上为 24 或 32 字节。)在大多数系统上,size_t 是一种无符号类型,因此带有负参数的调用被解释为请求大量空间,这通常会失败。n 的最大支持值因系统而异,但在所有情况下都小于 size_t 的最大可表示值。
realloc(void* p, size_t n)
返回一个指向大小为 n 的块的指针,该块包含与块 p 相同的数据,直到 (n, p 的大小) 字节中的最小值,如果没有可用空间,则返回 null。 返回的指针可能与 p 相同,也可能不同。该算法在可能的情况下优先扩展 p,否则采用等效的 malloc-copy-free 序列。 如果 p 为 null,realloc 等同于 malloc。 如果没有可用空间,realloc 返回 null,errno 被设置(如果在 ANSI 下),并且 p 不会被释放。 如果 n 小于 p 当前持有的字节数,新的未使用空间将被截断并在可能的情况下被释放。除非设置了 #define REALLOC_ZERO_BYTES_FREES
,否则使用零的大小参数的 realloc 将重新分配一个最小大小的块。 通过 mmap 内部获得的大块将始终使用 malloc-copy-free 序列进行扩展,除非系统支持 MREMAP(目前仅限于 linux)。 不支持旧的 UNIX realloc 约定,即允许将最后释放的块用作 realloc 的参数。
mallopt(int parameter_number, int parameter_value)
该函数可使用一些键值对设置一些参数,对malloc的策略有影响
M_MXFAST
该参数决定fastbins
最大大小,针对该参数我有一点疑惑,因为我在malloc.c或者malloc.h见到的都是#define M_MXFAST 1
,按道理来讲在该版本应该是64才对,我对此有疑问,那么该参数到底是在哪里进行设置的呢?
malloc_state
通过维护mstate对象,可以很好的管理内存分配。这里的mastate就是malloc_state结构体。里面主要维护以下信息,堆的基地址和大小、堆顶,free_chunk信息。
malloc_chunk
这里可以看到malloc_chunk的定义。
struct malloc_chunk {
INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
Size & Alignment
大小和对齐检查一直是绕不过的一个坎。
chunk & mem
用户指针和malloc头部的转换,可以看到其实就是加减2*SIZE_SZ
#define chunk2mem(p) ((void*)((char*)(p) + 2*SIZE_SZ))
#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ))
MIN_CHUNK_SIZE
最小chunk块大小的计算方式,这里很有趣
/* The smallest possible chunk */
#define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize))
MINSIZE
这个MINSIZE计算的是最小块对齐之后的size大小,这里由于已经对齐了,所以看不出来作用。不过这里的对齐方式很值得深思,以后还很常用到。
#define MINSIZE \
(unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
举个例子,(0x20+0xf)*~0xf
最终结果为对0x10对齐的0x20
好消息
好消息是我找到了malloc在3500行,坏消息是中间没做笔记
malloc 分配逻辑
下面我将介绍malloc函数分配相关的一些知识,旨在帮助新人更容易入门以及方便以后随时快速回忆相关知识
要点介绍
提前说明一下要点。当然这些要点可能在下面不会提及,但是他们都是我认为对于堆学习比较重要的知识点
- bin chunk mem
- fastbin smallbin largebin tcachebin unsortedbin
- last_remainder top_chunk binmap
- mallocinfo malloc_state chunk&mem Size&Alignment
- _libc_malloc _int_malloc malloc
malloc分配流程
略,下次再写吧