【问题标题】:Puzzling behavior, malloc and free(), with libuv令人费解的行为,malloc 和 free(),与 libuv
【发布时间】:2013-03-29 21:27:47
【问题描述】:

使用示例代码了解 libuv 我遇到了一个我不确定的副作用。该代码使用 malloc() 获取内存以存储来自网络上的客户端的数据,然后将相同的数据发送回,只是回显。然后它使用 free 释放内存。这通过回调循环一遍又一遍地重复。获取内存的代码行是:

uv_write_t *req = (uv_write_t *) malloc(sizeof(uv_write_t));

释放内存的行是:

free((char*) req->data);
free(req);

但是,如果您输入一个长字符串,例如“Whats the word on the street?”要回显,然后放入较短的字符串,例如旧字符串的“Hi”片段,在较短的字符串回显后将重新出现。例如输出可以是这样的:

街上的词是什么? 你好 你好 你好 你好 他在街上的话?

由于内存被释放,我不确定为什么旧片段会重新出现。我对这个主题的想法是,要么我对 malloc 和 free() 有一些我不了解的地方,要么在库中存在一个错误,它决定了传入数据所需的大小以及在使用更长的字符串后我得到了垃圾作为太大的内存块的一部分。如果是这样的话,那么它是我之前输入的一个片段的事实只是偶然的。这是可能的原因,还是我错过了什么?有没有其他信息。我应该包括澄清吗?

【问题讨论】:

  • 您是否正确设置了req->data?我看到你释放它但从不分配它。
  • Nitpick:不要在 C 中强制转换 malloc()。测试用例怎么样?我们无法看到当前真正发生的事情。
  • @RandyHoward 实际上,在 void * 在 C 中合法之前​​,您必须强制转换以避免警告,所以这可能是更精通旧 C 的人。
  • 已经很久了。现在是 2013 年。这不仅是个坏主意,还会隐藏错误。
  • 我们需要看看你是如何设置req->data的,目前我们没有足够的代码来回答你的问题。

标签: c malloc free libuv


【解决方案1】:

malloc() 的实现会有所不同,但可以安全地假设对 malloc() 的调用可以返回指向先前 free() 的内存块的指针,并且返回的内存不会被清零。换句话说,malloc() 给我们一个指向包含先前初始化数据的数据的指针是完全正常的。

也就是说,我怀疑这里的根本问题将是一个未终止的字符串,这可能是您序列化字符串方式的产物。例如,如果您只是从客户端写入 strlen(str) 字节,则不会写入 NULL。因此,当服务器接收到消息时,它将有一个未终止的字符串。如果这是您计划传递字符串的方式,并且您计划将其视为普通的以 null 结尾的字符串,则服务器需要将数据复制到足够大的缓冲区以容纳字符串和额外的 NULL 字符。

那么,为什么您会看到过去消息的片段?大概是运气不好。如果这是一个非常简单的应用程序,那么 malloc() 很有可能返回一块与上一个请求重叠的内存。

那么为什么我得到如此干净的输出,我不应该看到大量乱码数据,或者我的字符串操作的段错误走向无穷大吗?再次,愚蠢的运气。请记住,当内核第一次为您的应用程序提供一个内存页面时,它将首先将页面清零(出于安全原因这样做)。因此,即使您可能没有终止字符串,您的字符串所在的堆内存页面可能处于相对原始的清零状态。

【讨论】:

    【解决方案2】:

    uv_write_t *req 不是要发送或接收的数据。它就像一个写请求的句柄。

    req->data 也不是。那是指向您的任意私有数据的指针。例如,如果您想传递与连接相关的一些数据,则可能会使用它。

    实际有效负载数据通过写入缓冲区 (uv_buf_t) 发送并接收到在处理读取请求时分配的缓冲区。这就是 read 函数需要一个 alloc 参数的原因。稍后将该缓冲区传递给读取回调。

    req->data 的释放假定 'data' 指向一些私有数据,通常是一个结构,它是 malloc 的(由你)。

    根据经验,套接字由 uv_xxx_t 表示,而读写使用“请求”结构。 编写服务器(典型的 uv 用例)并不知道会有多少连接,因此一切都是动态分配的。

    为了让您的生活更轻松,您可能会考虑成对(打开/关闭或开始/完成)。因此,在接受新连接时,您会开始一个循环并分配客户端。关闭该连接时,您将其释放。写入时,您分配请求以及有效负载数据缓冲区。写完后,你释放它们。读取时分配读取请求,并且负载数据缓冲区在后台分配(通过分配回调),当完成读取(并复制负载数据)时,您将两者都释放。

    有很多方法可以在没有所有这些 malloc/free 对的情况下完成工作(这在性能方面并不出色),但对于新手来说,我会同意 uv 文档;您绝对应该从 malloc/free 路线开始。 给你一个想法:我为大约一万或十万个连接预先分配了所有东西,但这带来了一些管理和诡计,例如伪造 alloc 回调,仅分配一个预先分配的缓冲区。

    如果被要求猜测,我建议避免 malloc/free 只值得在任何时间点超过 5k 到 10k 连接之外的麻烦。

    【讨论】:

      猜你喜欢
      • 2018-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-30
      • 1970-01-01
      • 2018-07-02
      • 2020-06-19
      相关资源
      最近更新 更多