glibc的free()的内部工作原理

时间:2012-05-18 01:45:45

标签: c heap free glibc

对于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()'d块看起来像这样:

    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-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

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

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

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

1 个答案:

答案 0 :(得分:1)

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

BK->fd = FD;

BK 使用作为一大块用户数据,但现在它是一大块免费数据,因此允许malloc在内存上涂鸦认为合适。

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

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

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

(从技术上讲,你总是可以从user_data读取,无论它是什么别名,因为它是unsigned char,但这并不是真的相关。)

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

在低级代码中,字段只是内存偏移量。在我的系统上,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时,编译器的编写汇编,这意味着你不是写作大会。

相关问题