我正在尝试重写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
个ed页面为零填充。它们可能是出于安全原因,我们不希望其他进程的数据泄露,但我找不到任何关于此的标准参考。
我们可以MAP_ANON
mmap
来代替/dev/zero
使用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()
,从而破坏我的实施。
有效重写# ignore all files except for template config files
!/
/*
!exception_dir/
!.gitignore
!init.sh
!blah.template.json
的正确方法是什么,尊重写时复制?
答案 0 :(得分:2)
Pure POSIX不支持匿名内存映射,并且没有比calloc
更低级别的接口来分配零内存。
现有的POSIX实现通过MAP_ANON
或MAP_ANONYMOUS
标志(或历史上,通过/dev/zero
的映射)支持匿名私有内存映射作为扩展。内核确保应用程序只看到归零内存。 (也有较旧的接口,例如brk
和sbrk
,但它们很难使用非线程安全的。)
malloc
函数族的实现通常使用mmap
分配更大的块,并为每个块保留一个水印指针,指示哪个部分已经分配给应用程序至少一次(通过{ {1}} / malloc
/ realloc
,无关紧要)。 calloc
在返回分配之前检查水印指针,如果之前应用程序使用了内存,则会清除它。否则,它会被直接返回,因为已知它是新鲜的,因此已被内核清除。
也可以使用calloc
直接分配大块。但是内核最终也必须清除内存(在使用它来支持触发写时复制错误的映射之前),所以如果分配比实际需要大得多,那么这只是一个明显的胜利,并且大多数部分都是从未写过。