【问题标题】:The inner workings of glibc's free()glibc 的 free() 的内部工作原理
【发布时间】:2012-05-25 14:53:10
【问题描述】:

对于 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 指针作为参数并从中减去一个偏移量以获得一个块指针。中间有一堆检查,但在没有映射块的情况下,它通常向前或向后将它与另一个空闲块合并。由于空闲()的块已经在一个 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()'d 并且查看它的结构它没有前向或后向指针。我一定错过了代码中将 fd 和 bk 字段添加到 free() 的块中的部分,但我不知道在哪里。任何人都可以帮忙吗?谢谢。

【问题讨论】:

    标签: c heap-memory free glibc


    【解决方案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 时,编译器正在编写汇编,这意味着 你不是编写汇编。

    【讨论】:

    • 所以最初是 malloc() 的块已经有 fd 和 bk 字段正确吗?它们被标记为“用户数据”。但这不是意味着不能将任何数据写入用户数据的前 x 个字节,这样 fd 和 bk 就不会被覆盖?
    • 当块被传递给free时,free函数写入fdbk字段。这些字段与用户数据重叠,但这没关系,因为用户数据未用于释放的块。
    • 但是由于 BK=nextchunk->bk BK 是一个指向被 free()'d 块顶部的指针,并且当 BK->fd=FD 被调用时,这不只是“引用”字段 fd 还是它实际上在 BK 中创建 fd 并将其设置为等于 FD?
    • Mabye 我应该看看在调用 malloc 以创建现在空闲的内存时创建的初始结构。我确实知道 x86 汇编并理解代码中“存在”的概念,但是当引用没有意义的偏移量时,这将不起作用。因此,当 free()'d 的块是 malloc()'d 时,它的结构必须在某个地方定义它......我将再次检查源代码。
    • 1+ 表示“当您编写 C 时,编译器正在编写程序集,这意味着您不是在编写程序集。”
    猜你喜欢
    • 2011-08-29
    • 2015-08-21
    • 2014-01-22
    • 1970-01-01
    • 1970-01-01
    • 2018-07-28
    • 2019-06-29
    • 2022-05-21
    • 1970-01-01
    相关资源
    最近更新 更多