为什么 memset 在 calloc 之后调用?

Why memset called after calloc?

我研究了一些库的代码,注意到对 calloc 的调用后跟 memset 用于 calloc 分配的块。 我发现这个问题对 callocmalloc + memset 之间的差异有相当全面的回答,并在分配存储之前调用 memset

Why malloc+memset is slower than calloc?

我仍然不明白为什么有人要这样做。 这个操作有什么好处?

上述库中的代码示例:

light_pcapng_file_info *light_create_default_file_info()
{
    light_pcapng_file_info *default_file_info = calloc(1, sizeof(light_pcapng_file_info));
    memset(default_file_info, 0, sizeof(light_pcapng_file_info));
    default_file_info->major_version = 1;
    return default_file_info;
}

分配结构体代码(每个数组32个元素):

typedef struct _light_pcapng_file_info {
    uint16_t major_version;
    uint16_t minor_version;
    char *file_comment;
    size_t file_comment_size;
    char *hardware_desc;
    size_t hardware_desc_size;
    char *os_desc;
    size_t os_desc_size;
    char *user_app_desc;
    size_t user_app_desc_size;
    size_t interface_block_count;
    uint16_t link_types[MAX_SUPPORTED_INTERFACE_BLOCKS];
    double timestamp_resolution[MAX_SUPPORTED_INTERFACE_BLOCKS];

} light_pcapng_file_info;

编辑:

除了已接受的答案之外,我还想提供一些我的同事指出的信息。 glibc 中有一个错误,有时会阻止 calloc 清零内存。这是 link: https://bugzilla.redhat.com/show_bug.cgi?id=1293976

在 link 被移动的情况下的实际错误报告文本:

glibc: calloc() returns 非零内存

问题描述:

在 Facebook,我们有一个应用程序在从 glibc-2.12-1.149.el6.x86_64 到 glibc-2.12-1.163.el6.x86_64 时开始奇怪地挂起和崩溃。原来这个补丁

glibc-rh1066724.patch

介绍问题。

您将以下位添加到 _int_malloc()

  /* There are no usable arenas.  Fall back to sysmalloc to get a chunk from
     mmap.  */
  if (__glibc_unlikely (av == NULL))
    {
      void *p = sYSMALLOc (nb, av);
      if (p != NULL)
       alloc_perturb (p, bytes);
      return p;
    }

但这不行,alloc_perturb 无条件地 memset 的前字节为 0xf,不像上游它检查是否设置了 perturb_byte。这个需要改成

if (p != NULL && && __builtin_expect(perturb_byte, 0))
   alloc_perturb (p, bytes);
return p;

我附加的补丁解决了我的问题。

竞技场上的任何类型的锁争用都会导致我们退回到 mmap()'ing 新块这一事实加剧了这个问题。这是因为我们检查我们检查的无竞争竞技场是否损坏,如果是,我们循环遍历,如果我们循环到开头,我们知道我们没有找到任何东西。除非我们的初始竞技场实际上没有损坏,否则我们仍然 return NULL,所以我们更频繁地求助于这个 mmap() 事情,这确实让事情变得不稳定。

请尽快修复此问题,我什至将其称为可能的安全问题。

我会称之为一个错误,因为它在做毫无意义的工作,calloc() 被指定为 return 已经清除的内存所以为什么要再次清除它?也许重构失败,当有人从 malloc().

切换时

如果代码在您有权访问的存储库中是开源的 and/or,我会检查这些行的提交历史记录,看看发生了什么。幸运的是,有一条提交消息说明了动机 ...

调用memset()确保OS实际进行虚拟内存映射。正如您链接的问题的答案中所述,可以优化 calloc() 以便延迟实际的内存映射。

应用程序可能有理由推迟虚拟内存映射的实际创建——例如使用缓冲区从非常高速的设备读取,尽管在使用 memset() 将内存归零的情况,使用 calloc() 而不是 malloc() 似乎是多余的。

没有人是完美的,仅此而已。是的,在 calloc 之后 memset 归零是奢侈的。如果你想要一个显式的 memset 来保证你拥有你所要求的内存,那么它应该遵循 malloc 而不是。

新代码每 1,000 行大约有 20 个错误,概率定律表明并非所有错误都被清除。另外,这并不是真正的错误,因为没有观察到不良行为。