我研究了一些库的代码,注意到对calloc
的调用后面跟着calloc
分配的块的memset
。我发现这个问题对calloc
和malloc
+memset
之间的差异有着相当全面的回答,并且在分配存储之前调用memset
:
为什么malloc+memset比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-12.12-1.149.el6.x86_64到glibc-12.2-1.163.el6.x86_64时,它开始奇怪地挂起并崩溃。发现这个补丁
glibc-h1066724.补丁
介绍了问题。
您向_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,而不像上游那样,它检查是否设置了扰动字节。这需要更改为
if (p != NULL && && __builtin_expect(perturb_byte, 0))
alloc_perturb (p, bytes);
return p;
我所附的补丁为我修复了这个问题。
竞技场上的任何类型的锁争用都会导致我们回到mmap()的新区块,这一事实加剧了这个问题。这是因为我们检查我们检查的未受控制的竞技场是否腐败,如果是,我们循环通过,如果我们循环到一开始,我们知道我们什么都没发现。除非我们最初的竞技场实际上没有损坏,否则我们仍然会返回NULL,所以我们更频繁地使用mmap(),这确实会使事情变得不稳定。
请尽快解决这个问题,我甚至认为这可能是一个安全问题。
调用memset()
可确保操作系统实际执行虚拟内存映射。正如您链接的问题的答案中所指出的,calloc()
可以进行优化,以便推迟实际的内存映射。
应用程序可能有理由不推迟虚拟内存映射的实际创建,例如使用缓冲区从非常高速的设备读取,尽管在使用memset()
将内存清零的情况下,使用calloc()
而不是malloc()
似乎是多余的。
没有人是完美的,仅此而已。是的,memset
在calloc
之后归零是奢侈的。如果你想要一个明确的memset
来保证你拥有你所要求的内存,那么它应该跟在malloc
后面。
新代码每1000行大约有20个错误,概率定律意味着并非所有错误都被清除了。此外,这并不是一个真正的错误,因为没有不良行为可以观察到。
我会称之为bug,因为它正在做毫无意义的工作,calloc()
被指定返回已经清除的内存,为什么要再次清除它呢?当有人从malloc()
切换时,可能是一次失败的重构。
如果代码是开源的和/或在您可以访问的存储库中,我会检查这些行的提交历史记录,看看发生了什么。幸运的是,有一条提交消息告诉动机。。。