关于chunk的一些宏定义glibc-malloc函数(1)

上一篇博客我介绍了在堆管理中一个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),用于检查请求的字节数是否超出范围,并将其转换为可用的内存块大小。

宏的定义包含了以下几个部分:

  1. REQUEST_OUT_OF_RANGE 检查:宏内部使用了 REQUEST_OUT_OF_RANGE (req) 进行请求范围的检查。如果请求超出范围,即请求的字节数过大而会导致回绕到零,那么条件成立。

  2. 错误处理和返回:在条件成立时,通过调用 __set_errno(ENOMEM) 来设置错误码为 ENOMEM,表示内存不足的错误,并使用 return 0; 提前返回。这样做是为了在请求超出范围时,及时处理错误并终止函数执行。

  3. 转换为可用的内存块大小:在上述条件不成立时,即请求的字节数在范围内,宏继续执行 (sz) = request2size (req); 这个语句。该语句将请求的字节数转换为可用的内存块大小,并将结果赋值给 sz 变量。

总结起来,这段代码的作用是检查请求的字节数是否超出范围。如果超出范围,则设置错误码并返回,否则将请求转换为可用的内存块大小,并将结果赋值给 sz 变量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值