定义glibc"/>
关于chunk的一些宏定义glibc
上一篇博客我介绍了在堆管理中一个chunk的结构和布局,接下来我会分模块分析这些宏定义的含义和作用,这篇文章我们来解析下Size and alignment checks and conversions(大小和对齐检查与转换)这个模块,理解他们的含义。
#define chunk2mem(p) ((void*)((char*)(p) + 2*SIZE_SZ))
首先得对chunk结构体成员要熟悉,可以参考上一篇文章,这个宏我们看名字就大概猜出来是干嘛的,是用来计算指向用户区域的指针。因为在chunk结构体中prev_size和size都是SIZE_SZ类型的变量所以乘以2,p是一个指向chunk的结构体指针,将p指针强制类型转换成char*类型指针然后加上2个SIZE_SZ字节最后在强制类型转换成void*。此时就指向了用户申请的空间地址的起始位置。也就是我们Malloc函数后接收的返回值。为什么是void*呢最后,我想是因为我们用户在申请malloc函数的时候,设计者并不知道我们想要的指针类型,直接设计成void*反正用户拿到也会进行强转对吧。
#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ))
这个宏刚好相反,是将返回给用户堆地址转换为堆头地址。观察下是不是刚好相反。最后强转成mchunkptr,这是一个chunk结构体指针代码如下:
typedef struct malloc_chunk* mchunkptr;
#define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize))
这个宏的意思是计算一个最小的chunk占多大,offsetof的作用是计算malloc_chunk这个结构体中fd_nextsize的偏移是多少。相当于最小的chunk只有prev_size,size,fd,bk四个成员变量的总大小。很好理解。官方也给了很详细的解释The smallest possible chunk。
#define MINSIZE \(unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))
这个宏挺复杂的作用就是用来对齐,官方解释:The smallest size we can malloc is an aligned minimal chunk。我们可以使用 malloc
分配的最小大小是对齐的最小块。"aligned minimal chunk" 指的是对齐的最小内存块。在内存分配中,为了满足对齐要求,分配的内存块大小通常需要是对齐值的倍数。对齐值是根据系统架构和数据类型而定的,例如 4 字节对齐或 8 字节对齐。
所以,"the smallest size we can malloc is an aligned minimal chunk" 可以理解为我们可以使用 malloc
分配的最小内存大小是对齐的最小块大小。也就是说,malloc
分配的内存大小会被向上调整为满足对齐要求的最小值,以保证所分配的内存是对齐的。对齐的很大作用是用来提升访问效率的。
#define aligned_OK(m) (((unsigned long)(m) & MALLOC_ALIGN_MASK) == 0)
这段代码定义了一个宏 aligned_OK(m)
,用于检查指针 m
是否按照内存块对齐要求对齐。
宏的定义中,(unsigned long)(m)
将指针 m
转换为无符号长整型,以进行位操作。MALLOC_ALIGN_MASK
是一个掩码,用于对齐内存块的大小。
接下来,宏内部使用了位与操作符 &
将指针的值与 MALLOC_ALIGN_MASK
进行按位与运算。如果按位与的结果为0,表示指针 m
符合对齐要求,即按照 MALLOC_ALIGN_MASK
的掩码进行对齐。
所以,这段代码的作用是判断指针 m
是否按照内存块对齐要求对齐。如果按照要求对齐,条件成立,返回真;否则,条件不成立,返回假。
#define misaligned_chunk(p) \((uintptr_t)(MALLOC_ALIGNMENT == 2 * SIZE_SZ ? (p) : chunk2mem (p)) \& MALLOC_ALIGN_MASK)
这段代码定义了一个宏 misaligned_chunk(p)
,用于检查指针 p
是否指向未按照内存块对齐要求对齐的内存块。
宏的定义中,(uintptr_t)
将指针 p
转换为无符号整型,以进行位操作。MALLOC_ALIGNMENT
是一个常量,表示内存块的对齐要求。SIZE_SZ
是一个常量,表示内存块大小信息所占的字节数。chunk2mem(p)
是一个函数或宏,用于将内存块指针 p
转换为内存块的数据指针。
接下来,宏内部使用了条件表达式 (... ? ... : ...)
来选择合适的操作。如果 MALLOC_ALIGNMENT
等于 2 * SIZE_SZ
,则条件成立,执行 (p)
;否则,条件不成立,执行 chunk2mem(p)
。
最后,将选择的结果与 MALLOC_ALIGN_MASK
进行按位与运算。MALLOC_ALIGN_MASK
是一个掩码,用于对齐内存块的大小。
所以,这段代码的作用是判断指针 p
所指向的内存块是否未按照内存块对齐要求对齐。如果内存块未对齐,条件成立,返回真;否则,条件不成立,返回假。
#define REQUEST_OUT_OF_RANGE(req) \((unsigned long) (req) >= \(unsigned long) (INTERNAL_SIZE_T) (-2 * MINSIZE))
这段代码定义了一个宏 REQUEST_OUT_OF_RANGE(req)
,用于检查一个请求是否超出了范围。
首先,宏内部使用了 (unsigned long) (req)
和 (unsigned long) (INTERNAL_SIZE_T) (-2 * MINSIZE)
进行比较。这里使用了类型转换将 req
转换为 unsigned long
类型,以及将 -2 * MINSIZE
转换为 INTERNAL_SIZE_T
类型的无符号长整型。
宏的定义中,(unsigned long) (INTERNAL_SIZE_T) (-2 * MINSIZE)
表示将 -2 * MINSIZE
转换为 INTERNAL_SIZE_T
类型的无符号长整型。这里使用了 -2 * MINSIZE
是为了确保即使在对请求进行填充和对齐时也不会导致回绕到零。
接下来,(unsigned long) (req) >= (unsigned long) (INTERNAL_SIZE_T) (-2 * MINSIZE)
表示比较请求的无符号长整型值与界限值的大小关系。如果请求的值大于或等于界限值,那么表达式的结果为真,即表示请求超出了范围。
#define request2size(req) \(((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \MINSIZE : \((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
这段代码定义了一个宏 request2size(req)
,用于将请求的字节数转换为可用的内存块大小。
首先,宏内部使用了 (req) + SIZE_SZ + MALLOC_ALIGN_MASK
进行计算。这里的 req
表示请求的字节数,SIZE_SZ
是一个常量,表示内存块的大小信息所占的字节数,MALLOC_ALIGN_MASK
是一个掩码,用于对齐内存块的大小。
接下来,宏的定义中使用了条件表达式 (... ? ... : ...)
进行判断。如果 (req) + SIZE_SZ + MALLOC_ALIGN_MASK
的结果小于 MINSIZE
,则条件成立,返回 MINSIZE
,否则执行后续的表达式。
在后续的表达式中,((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK
用于将计算结果进行对齐操作。&
表示按位与操作,~MALLOC_ALIGN_MASK
是对 MALLOC_ALIGN_MASK
取反操作,即得到了一个掩码,用于将计算结果向下对齐到最近的较小的对齐倍数。
总结起来,这段代码的作用是将请求的字节数转换为可用的内存块大小。首先,计算出填充和对齐后的大小,然后判断是否小于最小块大小 MINSIZE
,如果是,则返回 MINSIZE
,否则将结果向下对齐到最近的较小的对齐倍数。
#define checked_request2size(req, sz) \if (REQUEST_OUT_OF_RANGE (req)) { \__set_errno (ENOMEM); \return 0; \} \(sz) = request2size (req);
这段代码定义了一个宏 checked_request2size(req, sz)
,用于检查请求的字节数是否超出范围,并将其转换为可用的内存块大小。
宏的定义包含了以下几个部分:
-
REQUEST_OUT_OF_RANGE
检查:宏内部使用了REQUEST_OUT_OF_RANGE (req)
进行请求范围的检查。如果请求超出范围,即请求的字节数过大而会导致回绕到零,那么条件成立。 -
错误处理和返回:在条件成立时,通过调用
__set_errno(ENOMEM)
来设置错误码为ENOMEM
,表示内存不足的错误,并使用return 0;
提前返回。这样做是为了在请求超出范围时,及时处理错误并终止函数执行。 -
转换为可用的内存块大小:在上述条件不成立时,即请求的字节数在范围内,宏继续执行
(sz) = request2size (req);
这个语句。该语句将请求的字节数转换为可用的内存块大小,并将结果赋值给sz
变量。
总结起来,这段代码的作用是检查请求的字节数是否超出范围。如果超出范围,则设置错误码并返回,否则将请求转换为可用的内存块大小,并将结果赋值给 sz
变量。
更多推荐
关于chunk的一些宏定义glibc
发布评论