如何实现calloc
How to implement calloc
我正在尝试重写malloc和calloc,我的问题是关于calloc的实现,而不是如何使用它。
应该始终使用 calloc()
而不是 malloc()+memset()
,因为它可以利用 copy-on-write(COW)。
有些calloc
是这样实现的:
void * calloc(size_t nelem, size_t elsize)
{
void *p;
p = malloc (nelem * elsize);
if (p == 0)
return (p);
bzero (p, nelem * elsize);
return (p);
}
但他们根本不使用 COW(并且他们不检查溢出)。
如果这些实现不调用 bzero()
,它们必须假定它们收到的 mmap
页面是零填充的。他们可能是出于安全原因,我们不希望其他进程的数据泄漏,但我找不到任何关于此的标准参考。
我们可以 mmap
来自 /dev/zero
:
而不是使用 MAP_ANON
fd = open("/dev/zero", O_RDWR);
a = mmap (0, 4096e4, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FILE, fd, 0);
但是 /dev/zero
没有被 POSIX 强制要求,并且可以很容易地做到 sudo mv /dev/zero /dev/foo
,破坏了我的实现。
关于写时复制,有效重写 calloc()
的正确方法是什么?
纯 POSIX 不支持匿名内存映射,并且没有比 calloc
更低级别的接口来分配零内存。
现有 POSIX 实现支持匿名私有内存映射作为扩展,通过 MAP_ANON
或 MAP_ANONYMOUS
标志(或历史上,通过从 /dev/zero
映射)。内核确保应用程序只看到归零的内存。 (也有一些较旧的接口,例如 brk
和 sbrk
,但它们很难使用并且不是线程安全的。)
malloc
函数族的实现通常使用 mmap
分配更大的块,并为每个块保留一个水印指针,指示哪个部分已经至少分配给应用程序一次(通过 malloc
/realloc
/calloc
,无关紧要)。 calloc
在返回分配之前检查水印指针,如果之前应用程序使用过内存,则将其清除。否则直接返回,因为知道是新鲜的,已经被内核清除了。
大块也可以直接使用mmap
分配。但是内核最终也必须清除内存(在将其用于触发写时复制错误的反向映射之前),所以如果分配比实际需要的大得多,那么这只是一个明显的胜利,而且大部分都是从未写过。
我正在尝试重写malloc和calloc,我的问题是关于calloc的实现,而不是如何使用它。
应该始终使用 calloc()
而不是 malloc()+memset()
,因为它可以利用 copy-on-write(COW)。
有些calloc
是这样实现的:
void * calloc(size_t nelem, size_t elsize)
{
void *p;
p = malloc (nelem * elsize);
if (p == 0)
return (p);
bzero (p, nelem * elsize);
return (p);
}
但他们根本不使用 COW(并且他们不检查溢出)。
如果这些实现不调用 bzero()
,它们必须假定它们收到的 mmap
页面是零填充的。他们可能是出于安全原因,我们不希望其他进程的数据泄漏,但我找不到任何关于此的标准参考。
我们可以 mmap
来自 /dev/zero
:
MAP_ANON
fd = open("/dev/zero", O_RDWR);
a = mmap (0, 4096e4, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FILE, fd, 0);
但是 /dev/zero
没有被 POSIX 强制要求,并且可以很容易地做到 sudo mv /dev/zero /dev/foo
,破坏了我的实现。
关于写时复制,有效重写 calloc()
的正确方法是什么?
纯 POSIX 不支持匿名内存映射,并且没有比 calloc
更低级别的接口来分配零内存。
现有 POSIX 实现支持匿名私有内存映射作为扩展,通过 MAP_ANON
或 MAP_ANONYMOUS
标志(或历史上,通过从 /dev/zero
映射)。内核确保应用程序只看到归零的内存。 (也有一些较旧的接口,例如 brk
和 sbrk
,但它们很难使用并且不是线程安全的。)
malloc
函数族的实现通常使用 mmap
分配更大的块,并为每个块保留一个水印指针,指示哪个部分已经至少分配给应用程序一次(通过 malloc
/realloc
/calloc
,无关紧要)。 calloc
在返回分配之前检查水印指针,如果之前应用程序使用过内存,则将其清除。否则直接返回,因为知道是新鲜的,已经被内核清除了。
大块也可以直接使用mmap
分配。但是内核最终也必须清除内存(在将其用于触发写时复制错误的反向映射之前),所以如果分配比实际需要的大得多,那么这只是一个明显的胜利,而且大部分都是从未写过。