为什么 memset 在 calloc 之后调用?
Why memset called after calloc?
我研究了一些库的代码,注意到对 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 清零内存。这是 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 个错误,概率定律表明并非所有错误都被清除。另外,这并不是真正的错误,因为没有观察到不良行为。
我研究了一些库的代码,注意到对 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 清零内存。这是 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 个错误,概率定律表明并非所有错误都被清除。另外,这并不是真正的错误,因为没有观察到不良行为。