为什么memset在calloc之后调用?

时间:2019-02-07 13:52:20

标签: c malloc calloc memset

我研究了某个库的代码,并注意到对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将内存清零。这里是链接: https://bugzilla.redhat.com/show_bug.cgi?id=1293976

当链接被移动时,实际的错误报告文本:

glibc:calloc()返回非零的内存

问题描述:

在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无条件地将内存集的前字节设置为0xf,这与上游情况不同,它会检查perturb_byte是否已设置。需要更改为

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

我随附的补丁可以为我解决问题。

以下事实加剧了这个问题:竞技场上的任何类型的锁争用都会导致我们退回到mmap()的新块上。这是因为我们检查看看我们检查的无人竞技场是否已损坏,如果是循环的,则循环进行,如果循环到开始,则知道没有找到任何东西。除非我们最初的竞技场没有真正损坏,否则我们仍然会返回NULL,因此我们会更频繁地使用mmap()这个东西,这确实使事情变得不稳定。

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

3 个答案:

答案 0 :(得分:7)

调用memset()可确保操作系统实际上在进行虚拟内存映射。如您所链接问题的答案中所述,可以calloc()进行优化,以便推迟实际的内存映射。

该应用程序可能有理由推迟实际创建虚拟内存映射-例如使用缓冲区从超高速设备读取数据,尽管在使用{{ 1}}将内存清零,使用memset()代替calloc()似乎是多余的。

答案 1 :(得分:2)

没有人是完美的,仅此而已。是的,memset之后的calloc为零是奢侈的。如果要使用显式memset来确保拥有所需的内存,则应改用malloc

新代码每1000行大约有20个错误,概率法则表明并非全部被淘汰。另外,这并不是真正的错误,因为没有不良行为要观察。

答案 2 :(得分:1)

我将其称为错误,因为它正在进行毫无意义的工作,所以指定calloc()来返回已经清除的内存,为什么还要再次清除它呢?当有人从malloc()切换时,重构可能失败。

如果代码是开源的和/或在您有权访问的存储库中,我将检查这些行的提交历史记录,并查看发生了什么。幸运的是,有一条提交消息可以告诉您动机……