3

对于 glibc 2.15,我正在查看 malloc.c,特别是 free() 函数,并对 unlink() 宏感到困惑。根据来源,使用中的块如下所示:

   chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  Size of previous chunk, if allocated            
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  Size of chunk, in bytes                       
     mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  User data starts here...                          .
    .                                                               .
    .             (malloc_usable_size() bytes)                      .
    .                                                               
nextchunk->+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

一个 free() 的块看起来像这样:

    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                         Size of previous chunk                    
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 `head:'           Size of chunk, in bytes                          
  mem->     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  Forward pointer to next chunk in list             
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  Back pointer to previous chunk in list            
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  Unused space (may be 0 bytes long)                .
    .                                                               .
    .                                                               
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

当使用的块是 free()'d 时,它将接收到的 mem 指针作为参数并从中减去偏移量以获得块指针。中间有一堆检查,但在没有映射块的情况下,它通常向前或向后将它与另一个空闲块合并。由于 free() 的块已经在一个 bin 中,它只是在那个特定的 bin 中搜索块来合并它,对吗?在前向合并的情况下,unlink()宏被调用并应用于被释放()的块之后的块。我不明白这一点,因为当下一个块(称之为'nextchunk')被取消链接时,会发生以下代码:

    #define unlink(P, BK, FD) {                                            
    FD = P->fd;                                                          
    BK = P->bk;
    .
    .
    .
    FD->bk = BK;                                                       
    BK->fd = FD;
    .
    .
    .
                             }

BK->fd考虑到 BK 指向被 free() 的块并且查看它的结构,它没有前向或后向指针,如何引用它。我一定错过了代码中将 fd 和 bk 字段添加到 free() 的块中的部分,但我不知道在哪里。任何人都可以帮忙吗?谢谢。

4

1 回答 1

1

这一行在被释放的块中创建了一个前向指针:

BK->fd = FD;

BK曾经是一大块用户数据,但现在它是一大块空闲数据,因此malloc可以随意在内存上随意涂鸦。

如果它有帮助,你可以把它想象成一个联合:

union {
    struct {
        chunk *fd;
        chunk *bk;
    } freed;
    unsigned char user_data[N];
};

在工会中,您可以写入任何工会成员,但您只能从最近写入的成员中读取。所以当free被调用时,数据被写入fd并且bk——这没关系,唯一的结果是现在user_data可能有垃圾。相比之下,当块包含用户数据(不是免费的)时,fdandbk指针是垃圾,因为它们是别名user_data

(从技术上讲,你总是可以从user_data任何别名中读取,因为它是一个unsigned char,但这并不真正相关。)

更新:这是低级C 代码。您会期望在实现中使用低级 C 代码malloc。字段存在或不存在的想法在低级代码中没有意义,因为我们在不同类型之间进行转换并允许指针相互别名。

在低级代码中,字段只是内存偏移量。在我的系统上,该fd字段的偏移量可能为 0,而该bk字段的偏移量可能为 8 或 4,具体取决于我编译的体系结构。所以下面的代码:

BK->fd = FD;

这意味着“将值 FD 写入内存位置 BK + 0”。如果您BK->fd仅将其视为内存中的一个位置,它可能会帮助您了解其free工作原理。(它实际上不仅仅是内存中的一个位置,因为在编译时还有类型信息和别名规则。)

理解低级 C: 如果你想理解低级 C 代码,那么理解汇编语言会有很大帮助。这不是必需的,但它有帮助。学习哪种汇编语言并不重要:x86、MIPS、PowerPC、ARM 等。您不需要学习太多汇编,只需一点点。你不需要学习 x86,即使你从不使用 MIPS,你也可以学习 MIPS。(事实上​​,MIPS 可能更容易学习。)

只需学习足够的汇编,您就可以将一小段 C 代码转换为汇编,这样您就可以了解它在幕后所做的事情。上面那一行 C 代码可能翻译成一行汇编代码,因为它是如此简单。

并且在编写 C 时尽量不要过多考虑汇编。当您编写C 时,编译器正在编写汇编,这意味着您不是在编写汇编。

于 2012-05-18T06:46:38.683 回答