【发布时间】:2019-02-07 13:52:20
【问题描述】:
我研究了一些库的代码,并注意到对calloc 的调用后跟memset 用于calloc 分配的块。
我发现这个问题对calloc 和malloc + 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()'ing 一个新块这一事实而加剧。这是因为我们检查我们检查的无人竞争的竞技场是否损坏,如果是,我们循环通过,如果我们循环到开头,我们知道我们没有找到任何东西。除非我们最初的 arena 实际上没有损坏,否则我们仍然会返回 NULL,所以我们更频繁地使用这个 mmap() 东西,这确实让事情变得不稳定。
请尽快解决这个问题,我什至认为这是一个可能的安全问题。
【问题讨论】:
-
calloc已经 0 初始化内存,因此使用memset再次执行它是没有意义的(除非您正在处理有缺陷的calloc实现)。 -
可能库作者一开始没有使用
calloc,而是malloc?也许图书馆的作者并不真正知道calloc应该做什么?也许还有无数其他原因?能回答的只有原作者。我们能做的就是猜测。 -
代码是某个菜鸟写的。就是这样……没什么好考虑的。
-
阅读其他代码并从中学习很重要,但它确实有一个丑陋的缺点:那里有很多写得非常糟糕的代码,这只是一个小例子。在 calloc 之后调用 memset 绝对没有意义。