是否有可能"打孔"通过mmap'ed匿名内存? [英] Is it possible to "punch holes" through mmap'ed anonymous memory?

查看:110
本文介绍了是否有可能"打孔"通过mmap'ed匿名内存?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑它使用了大量的大致页大小的存储区域的(例如64kB的左右),其每一个是相当短暂的一个程序。 (在我的具体情况,这些都为绿色线程堆栈备用)

Consider a program which uses a large number of roughly page-sized memory regions (say 64 kB or so), each of which is rather short-lived. (In my particular case, these are alternate stacks for green threads.)

如何总会有做最好的分配这些地区,使得它们的页面可以一次该地区不再使用返回到内核?天真的解决方案显然是简单地 MMAP 每个区域单独和则munmap 再次,一旦他们为我'米,他们所做的。我觉得这是一个坏主意,不过,因为有这么多的人。我怀疑VMM可以开始一段时间后,缩放严重;但即使没有,我仍然有兴趣在理论情况下。

How would one best do to allocate these regions, such that their pages can be returned to the kernel once the region isn't in use anymore? The naïve solution would clearly be to simply mmap each of the regions individually, and munmap them again as soon as I'm done with them. I feel this is a bad idea, though, since there are so many of them. I suspect that the VMM may start scaling badly after a while; but even if it doesn't, I'm still interested in the theoretical case.

如果我不是只 MMAP 我是一个巨大的匿名映射从中我按需分配的地区,是有办法打孔通过映射区域我正在与做了什么?有点像的madvise(MAD​​V_DONTNEED),但不同的是,网页应该考虑删除,这样内核实际上并不需要保留其内容任何地方,但可以只重用时,他们会再次出现故障归零页面。

If I instead just mmap myself a huge anonymous mapping from which I allocate the regions on demand, is there a way to "punch holes" through that mapping for a region that I'm done with? Kind of like madvise(MADV_DONTNEED), but with the difference that the pages should be considered deleted, so that the kernel doesn't actually need to keep their contents anywhere but can just reuse zeroed pages whenever they are faulted again.

我使用的是Linux操作系统,在这种情况下,我不使用特定的Linux电话打扰。

I'm using Linux, and in this case I'm not bothered by using Linux-specific calls.

推荐答案

我不明白为什么做大量的电话,以 MMAP / 则munmap 应该是不好的。在内核中查找性能映射应该是O(log n)的。

I don't see why doing lots of calls to mmap/munmap should be that bad. The lookup performance in the kernel for mappings should be O(log n).

您唯一的选择,因为它似乎在Linux现在实施是冲在映射孔做你想要的是则mprotect(PROT_NONE),并且仍然是分段映射在内核所以它主要是等同于 MMAP / 则munmap 除了别的东西将无法窃取你的VM范围。你可能会想的madvise(MAD​​V_REMOVE)工作或作为它被称为在BSD - 的madvise(MAD​​V_FREE)。这是被设计为做的正是你想要的 - 回收不分裂的映射页面最便宜的方式。但至少根据手册页在我的Linux两种版本它不能完全适用于各种映射来实现的。

Your only options as it seems to be implemented in Linux right now is to punch holes in the mappings to do what you want is mprotect(PROT_NONE) and that is still fragmenting the mappings in the kernel so it's mostly equivalent to mmap/munmap except that something else won't be able to steal that VM range from you. You'd probably want madvise(MADV_REMOVE) work or as it's called in BSD - madvise(MADV_FREE). That is explicitly designed to do exactly what you want - the cheapest way to reclaim pages without fragmenting the mappings. But at least according to the man page on my two flavors of Linux it's not fully implemented for all kinds of mappings.

免责声明:我主要是熟悉BSD VM系统的内部,但是这应该是在Linux上颇为相似

Disclaimer: I'm mostly familiar with the internals of BSD VM systems, but this should be quite similar on Linux.

由于在下面的评论,令人惊讶的是讨论 MADV_DONTNEED 似乎这样的伎俩:

As in the discussion in comments below, surprisingly enough MADV_DONTNEED seems to do the trick:

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/resource.h>

#include <stdio.h>
#include <unistd.h>

#include <err.h>

int
main(int argc, char **argv)
{
        int ps = getpagesize();
        struct rusage ru = {0};
        char *map;
        int n = 15;
        int i;

        if ((map = mmap(NULL, ps * n, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)) == MAP_FAILED)
                err(1, "mmap");

        for (i = 0; i < n; i++) {
                map[ps * i] = i + 10;
        }

        printf("unnecessary printf to fault stuff in: %d %ld\n", map[0], ru.ru_minflt);

        /* Unnecessary call to madvise to fault in that part of libc. */
        if (madvise(&map[ps], ps, MADV_NORMAL) == -1)
                err(1, "madvise");

        if (getrusage(RUSAGE_SELF, &ru) == -1)
                err(1, "getrusage");
        printf("after MADV_NORMAL, before touching pages: %d %ld\n", map[0], ru.ru_minflt);

        for (i = 0; i < n; i++) {
                map[ps * i] = i + 10;
        }

        if (getrusage(RUSAGE_SELF, &ru) == -1)
                err(1, "getrusage");
        printf("after MADV_NORMAL, after touching pages: %d %ld\n", map[0], ru.ru_minflt);

        if (madvise(map, ps * n, MADV_DONTNEED) == -1)
                err(1, "madvise");

        if (getrusage(RUSAGE_SELF, &ru) == -1)
                err(1, "getrusage");
        printf("after MADV_DONTNEED, before touching pages: %d %ld\n", map[0], ru.ru_minflt);

        for (i = 0; i < n; i++) {
                map[ps * i] = i + 10;
        }

        if (getrusage(RUSAGE_SELF, &ru) == -1)
                err(1, "getrusage");
        printf("after MADV_DONTNEED, after touching pages: %d %ld\n", map[0], ru.ru_minflt);

        return 0;
}

我测量 ru_minflt 作为代理,看看我们有多少页需要来分配(这是不完全正确的,但下一句使得它更容易) 。我们可以看到,我们取得的第三printf的新页面,因为映射的内容[0] 为0。

I'm measuring ru_minflt as a proxy to see how many pages we needed to allocate (this isn't exactly true, but the next sentence makes it more likely). We can see that we get new pages in the third printf because the contents of map[0] are 0.

这篇关于是否有可能&QUOT;打孔&QUOT;通过mmap'ed匿名内存?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆