为什么使用“munmap()”后页面回收仍然存在?

Why pages reclaims still present after using `munmap()`?

提问人:lucocozz 提问时间:3/27/2023 最后编辑:lucocozz 更新时间:5/20/2023 访问量:126

问:

对于一个研究项目,我必须编写 和 使用 和 的重新实现。malloc()free()mmap()munmap()

我在最后一个 Ubuntu 上运行。在我的测试中,我使用命令 (from),它向我展示了有关我的程序的大量信息,包括内存。以下是一些示例:time -v/usr/bin/time

因此,我们可以看到哪个对应于回收页数根据我们的使用情况而变化,但特别是如果我们在回收页数返回到其初始数量后使用,则我的重新实现并非如此:Minor page faultsfree()malloc()

以下是我的代码片段,用于可视化我的工作。

在我的 :malloc()

static t_page *__alloc_page(size_t size)
{
    struct rlimit limit;
    t_page *page;

    getrlimit(RLIMIT_AS, &limit);
    if (size > limit.rlim_max)
        return (NULL);
    page = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (page == MAP_FAILED)
        return (NULL);
    ft_bzero(page, sizeof(t_page));
    page->size = size;
    page->used_size = sizeof(t_page);
    return (page);
}

在我的 :free()

static void __free_page(t_page *page)
{
    t_binding *binder = __get_binder(page);

    binder->count--;
    if (binder->pages == page)
        binder->pages = page->next;
    
    if (page->prev != NULL)
        page->prev->next = page->next;
    if (page->next != NULL)
        page->next->prev = page->prev;

    if (munmap(page, page->size) == -1)
        ft_putstr("free(): munmap error\n");
}

作为信息,我的大小始终是 () 的倍数。getpagesize()N * getpagesize()

以下是我进行测试的方式

首先,我将我的文件等编译成一个动态库()。
然后我用下面的 main 构建了两个二进制文件。一个是用我的 malloc 编译的,另一个是用 libc 编译的。
malloc.cfree.clibmalloc.so

clang main.c -o libc_malloc
clang main.c -D LIBMALLOC libmalloc.so -o my_malloc
#ifdef LIBMALLOC
# include "../includes/malloc.h"
#else
# include <stdlib.h>
#endif

int main(void)
{
    int i;
    char *addr;

    i = 0;
    while (i < 1024) 
    {
        addr = (char*)malloc(1024);
        addr[0] = 42;
        free(addr);
        i++; 
    }
    return (0);
}

我还有一个脚本,允许我使用名为run.sh

#!/bin/sh
export LD_LIBRARY_PATH="."
export LD_PRELOAD="`pwd`/libmalloc.so"
$@

最后,我像这样运行我的两个二进制文件:time -v

/usr/bin/time -v ./libc_malloc
./run.sh /usr/bin/time -v ./my_malloc

如何以极简主义的方式复制

#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

int main(void)
{
    int i;
    char *addr;

    i = 0;
    #ifdef _MMAP_
        printf("mmap\n");
    #else
        printf("malloc\n");
    #endif
    while (i < 1024) 
    {
        #ifdef _MMAP_
            addr = mmap(NULL, 4 * getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
        #else
            addr = malloc(4 * getpagesize());
        #endif
        addr[0] = 42;
        #ifdef _MMAP_
            munmap(addr, 4 * getpagesize());
        #else
            free(addr);
        #endif
        i++; 
    }
    return (0);
}

将上面的这个主要内容复制到一个文件 () 中。
创建两个二进制文件,如下所示:
main.c

clang main.c -o using_malloc
clang -D _MMAP_ main.c -o using_mmap

然后使用以下命令运行它们:time -v

/usr/bin/time -v ./using_malloc
/usr/bin/time -v ./using_mmap

我试过什么

在互联网上搜索时,我遇到了这篇文章,它的问题与我的完全相同:使用 munmap
时页面回收率更高,
但建议的解决方案不起作用(我不能使用它)。
我不被允许使用类似或两者兼而有之的功能......
无论如何,我都尝试了它们,看看它们是否能解决我的问题,但没有成功。
我还运行了别人的项目。他工作得很好,而我们似乎在做同样的事情。
我错过了什么吗?
posix_madvise()msync()

c malloc 动态内存分配 免费 mmap

评论

0赞 Craig Estey 3/27/2023
看看我的回答:Malloc 正在使用它所说的必要内存量的 10 倍(常驻集大小)。请注意,映射/取消映射与 RSS 并不完全相同,因此没有映射,您将无法对进程的 RSS 进行太多控制。RSSposix_madvise/madvise/msync
0赞 Craig Estey 3/27/2023
另外,有关其他信息,请参阅我的另一个答案:mmap 如何提高文件读取速度?在该答案中,有指向我的答案的链接:哪些段受写入时复制的影响? 并以最有效的方式逐行阅读特定于平台,提供有关这些问题的更多详细信息。
0赞 Nate Eldredge 3/27/2023
首先,您是否确定并调用您的版本而不是标准库中的版本?实现这一点可能需要付出一些努力。mallocfree
0赞 Nate Eldredge 3/27/2023
您能否在单个代码块中组合一个最小的可重现示例,以及构建和运行它的确切命令、您得到的输出以及您认为它是错误的原因?很难遵循零碎的代码,而且您的示例函数也无济于事,因为它们似乎没有实际测量任何东西。main
0赞 lucocozz 3/28/2023
是的,对不起,我更新了@NateEldredge是的,我确信我的 malloc 和 free 都叫得很好

答:

1赞 lucocozz 5/19/2023 #1

我找到了我的问题。在我的主力中,我不断地一个接一个地执行 malloc() 和 free()。当系统执行时,它会尝试通过不立即删除分配的页面来优化,以便以后重用它。但是,这仍会导致在后续调用 时创建新页面。munmap()mmap()mmap()

要解决此问题,需要保留一个剩余的页面,以便不会在循环中调用该页面。free()munmap()

我的解释不是很清楚,请随时提供更多细节。