c-如何实现calloc



我正在尝试重写malloc和calloc,我的问题是关于calloc的实现,而不是如何使用它。

应该始终使用calloc()而不是malloc()+memset(),因为它可以利用写时复制(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的页面为零填充。它们可能是出于安全原因,我们不希望其他进程的数据泄露,但我找不到任何关于这方面的标准参考。

代替使用MAP_ANON,我们可以从/dev/zero:中获得mmap

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()的正确方法是什么?

Pure POSIX不支持匿名内存映射,并且没有比calloc更低级别的接口来分配零内存。

现有的POSIX实现支持匿名私有内存映射作为扩展,通过MAP_ANONMAP_ANONYMOUS标志(或历史上通过从/dev/zero映射)。内核确保应用程序只看到零内存。(也有较旧的接口,如brksbrk,但它们很难使用非线程安全的接口。)

malloc函数族的实现通常使用mmap来分配较大的块,并且为每个块保留水印指针,该水印指针指示哪个部分已经被分配给应用程序至少一次(经由malloc/realloc/calloc,这无关紧要)。calloc在返回分配之前会检查水印指针,如果应用程序以前使用过内存,它会将其清除。否则,它会直接返回,因为它是新的,因此已被内核清除。

也可以使用mmap直接分配大的块。但是内核最终也必须清除内存(在使用它来支持触发写时复制错误的映射之前),所以只有当分配比实际需要大得多,并且大多数部分从未写入时,这才是一个明显的胜利

相关内容

  • 没有找到相关文章