使用mmap和munmap实现自己的malloc/free函数

10

我已经使用 mmap 实现了自己的 mallocfree。由于与 free 不同,munmap 还需要长度作为参数,因此我将长度作为映射内存中的附加信息。

下面是我的 mallocfree 代码。我想问一下,这段代码是否好或者我还有遗漏或者错误的地方。

void * malloc ( size_t size )
{
    int *plen;
    int len = size + sizeof( size ); // Add sizeof( size ) for holding length.
    
    plen = mmap( 0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0 );
    
    *plen = len;                     // First 4 bytes contain length.
    return (void*)(&plen[1]);        // Memory that is after length variable.
}

void free ( void * ptr )
{
    int *plen = (int*)ptr;
    int len;
    
    plen--;                          // Reach top of memory
    len = *plen;                     // Read length
    
    munmap( (void*)plen, len );
}

3
我建议制作一个基本的测试程序,使用多组不同的参数运行多次,并通过Valgrind运行它,以查看是否会出现任何问题。 - Dan Fego
适合发布在http://codereview.stackexchange.com吗? - Paul R
@Paul:仍然有困难浏览所有不同的子论坛,从你提到的那个到 Stack Overflow 的 Ubuntu 论坛。请问您能否指导在哪里获取它们的列表? - gnometorule
@gnometorule:点击页面左上方的StackExchange单词,这将弹出一个窗口,然后点击“所有网站”。 - Paul R
5个回答

18

以下是一些观察:

  • 你假定intsize_t有相同的大小。如果你想在分配的开头存储一个size_t值,那么为什么不直接这样做呢?为什么要引入int
  • 这很可能会非常低效,无论是在内存使用还是速度方面都如此。 mmap()存在重大开销,并且通常分配不能小于“页”。大多数实际分配器尝试以各种方式避免在每次malloc()时调用操作系统级功能。
  • 如果mmap()失败,则会返回MAP_FAILED,因此malloc()也应该如此。因此,在取消引用由mmap()返回的指针之前,需要测试是否出现了这种情况。
  • 调用free(NULL)应该是有效的;使用你的实现很可能会导致崩溃,因为你在假设参数有效之前没有检查NULL

3
在许多C程序中,通常使用malloc分配的内存大小要比一页小得多,因此您需要将几个内存块保留在一个(或几个连续的)页面中。例如,当您使用strdup复制某些名称或人类语言单词时,典型的大小为一到几十字节,但典型的页面大小为4K字节!您还需要接受大量的malloc请求并进行特殊处理。 - Basile Starynkevitch
2
@Basile:话说,我的当前PC的内存比我第一台PC允许某些程序使用的内存多4096倍。因此,玩具程序可以使用这个分配器运行,只是不要尝试将Firefox与之链接。但你*真正想要的是每个微不足道的十几字节分配两页,即“后置保护页”;-) - Steve Jessop
是的,但是您电脑上当前的软件使用的内存可能比以前多了五千个。RAM就像磁盘空间一样,它往往会满。 - Basile Starynkevitch
1
@unwind:mmap 失败时会返回 MAP_FAILED 而不是 NULL - rpetrich
@rpetrich 哎呀!谢谢提醒,我太粗心了。已经修复了。 - unwind
显示剩余2条评论

10

至少应该:

  • 检查mmap是否失败
  • 存储size_t的大小,而不是int。它们可能不同。
  • 返回适当对齐的内存,这里的内存似乎是4字节对齐的(因为您将来自mmap的页面对齐数据添加一个int,假定为4字节)。这意味着在返回的内存中存储需要更大对齐的值,例如double会导致性能损失,或在某些体系结构上直接崩溃。
  • 处理传递给free的NULL(它应该是无操作)

对于malloc实现,我还希望它至少具有基本的调试支持,例如尝试检测双重释放,尝试检测释放无效指针,列出未释放的内存等。

请记住,您的malloc实现可能非常浪费。如果您malloc 10个字节,则最终会分配1页(4096字节),所有这些都必须映射到物理内存,并且剩余的4082字节未使用。


3
阅读 维基百科上的C malloc,并仔细研究一些真实的malloc实现,例如Doug Lea's malloc。关于这个主题有很多文献,例如Wolfram Gloger和许多其他人。

@MetallicPriest:你应该更详细地解释你正在做什么(我对你的工作并不非常乐观,但你可能会学到很多!)


在访问Wolfram Gloger的链接时出现了“禁止”错误。如果有的话,您能否链接到一些开放获取的文章? - Pavan Manjunath

3

您不应该在每次调用自定义malloc时都调用mmap。如前所述,这样做会产生巨大的开销。

您应该创建一个大型共享内存(例如4K),您的malloc函数只需要返回在您分配的内存范围内的指针。

然后,如果您的内存不足,可以创建另外4k的共享内存。

您只需计算使用了多少空间并跟踪引用,这样释放内存就更容易了。


2

同时,您应该使用-1而不是0作为mmap的fd参数。

mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)

MAP_ANONYMOUS

该映射不是由任何文件支持的,其内容初始化为零。fd 和 offset 参数将被忽略;但是,一些实现要求如果指定了 MAP_ANONYMOUS(或 MAP_ANON),则 fd 必须为 -1,可移植应用程序应确保此要求。在 Linux 上仅支持使用 MAP_SHARED 与 MAP_ANONYMOUS 结合使用,自内核 2.4 以来。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接