Linux MMAP 内部原理

24

我有几个关于Linux系统中 mmap 实现的问题,这些问题似乎没有太多的文档:

使用 mmap 将文件映射到内存时,如何处理预取文件中的数据?

即,当您从映射的区域读取数据时会发生什么?该数据是否移动到 L1/L2 缓存中?是否直接从磁盘缓存中读取?prefetchnta和类似的 ASM 指令是否可用于 mmap 区域?

mmap 实际调用的开销是多少?它相对于映射数据量而言是恒定的还是与之成比例的?

希望有人对此有深入了解。先谢谢了。

4个回答

31

mmap基本上是对虚拟内存子系统的程序化访问。

当你有一个1G的文件,并且将其mmap,你会得到一个指向整个文件的指针,就好像它在内存中一样。

然而,在此阶段除了实际映射操作为文件在VM中保留页面外,什么也没有发生。(当然,文件越大,映射操作时间越长。)

为了从文件开始读取数据,只需通过mmap调用返回的指针进行访问。

如果希望"预加载"文件的某些部分,只需访问想要预加载的区域。确保访问想要加载的所有页面,因为VM只会加载您访问的页面。例如,假设在您的1G文件中,您有一个10MB的"索引"区域需要映射。最简单的方法是只需"遍历您的索引"或任何数据结构,让VM根据需要分页数据。或者,如果您"知道"它是文件的"前10MB",并且您的VM的页面大小为4K,则只需将mmap指针转换为char指针,然后遍历页面。

void load_mmap(char *mmapPtr) {
    // We'll load 10MB of data from mmap
    int offset = 0;
    for(int offset = 0; offset < 10 * 1024 * 1024; offset += 4 * 1024) {
        char *p = mmapPtr + offset;
        // deref pointer to force mmap load
        char c = *p;
    }
}

关于L1和L2缓存,mmap与此无关,这完全取决于您如何访问数据。

由于您正在使用底层VM系统,因此任何在mmap块内寻址数据的内容都可以工作(甚至是从汇编语言)。

如果您不更改任何mmap的数据,则VM将随着需要新页面而自动清除旧页面。如果您实际上更改了它们,那么VM将为您写回这些页面。


8
char "c = *p" 会被优化掉吗?c 应该声明为 volatile 吗? - Laurynas Biveinis
1
在最近的Linux版本中,您可以在mmap()的标志参数中设置MAP_POPULATE,这样在调用返回之前,页面将被拉入。这将阻塞调用一段时间,该时间是文件大小的某个函数,因此如果有其他可以并行完成的工作,在需要读取文件内容之前在单独的线程中运行示例load_mmap()函数将提高性能,特别是因为大部分工作都在内核端完成。 - BD at Rivenhill
mlock 不是预加载页面吗? - Giovanni Funchal
那么除了手动“触摸”每个页面之外,没有本地方法可以实现mmapped文件块的预加载吗?我正在阅读readahead的手册,但它接受一个文件描述符,不清楚我是否可以使用我用于mmap文件的那个描述符。 - cYrus

4
这与CPU缓存无关;它将其映射到虚拟地址空间中,如果随后访问或使用mlock()加锁,则会将其物理地带入内存。它所在的CPU缓存与否是您无法真正控制的(至少不通过mmap)。通常需要触摸页面才能导致其被映射,但如果您执行mlock或mlockall,则会产生相同的效果(这些通常是特权)。就开销而言,我不太清楚,您需要测量。我的猜测是,一个没有加载页面的mmap()大致上是一个恒定时间操作,但是带入页面将花费更长的时间,并且具有更多的页面。Linux的最新版本还支持MAP_POPULATE标志,该标志指示mmap立即加载页面(可能仅在可能的情况下)。

2

回答Ravi Phulsundar先生的问题:

只要权限设置正确,多个进程可以将同一文件映射为长期使用。查看mmap手册页,只需传递MAP_SHARED标志(如果需要映射一个非常大的文件,请改用mmap2):

mmap

MAP_SHARED

与所有映射此对象的其他进程共享此映射。 将区域存储相当于写入文件。 直到调用msync(2)或munmap(2),文件可能实际上并未更新。


0

你使用MAP_SHARED


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