2010-09-24 121 views
5

我有一个在多线程中描述的有关内存映射和Linux下不断增长的内存消耗的问题。Linux内存映射文件保留大量物理内存

当我打开下Linux或MacOS X的1GB的文件,并使用

me.data_begin = mmap(NULL, capacity(me), prot, MAP_SHARED, me.file.handle, 0); 

和顺序读取映射的内存,我的程序使用越来越多的物理内存,虽然我用posix_madvise它映射到内存中(甚至称它在读取过程中多次):

posix_madvise(me.data_begin, capacity(me), MMAP_SEQUENTIAL); 

没有成功。 :-(

我想:

  • 不同的标志MMAP_RANDOM,MMAP_DONTNEED,MMAP_NORMAL没有成功
  • posix_fadvise(me.file.handle,0,容量(我),POSIX_FADV_DONTNEED)和前MMAP调用后 - >没有成功

在Mac OS X !!!当我结合

posix_madvise(.. MMAP_SEQUENTIAL) 
工作

msync(me.data_begin, capacity(me), MS_INVALIDATE). 

驻留存储器低于16M(I周期性地调用的msync 16mio步骤之后)。

但是根据Linux没有任何工作。有没有人对我在Linux下的问题有一个想法或成功的故事?

干杯, 大卫

+0

它可能也可能不相关,但应该知道:您使用的是32位还是64位系统?你知道你不应该在32位系统中映射1GB? (即使您使用的是64位系统,您也可能会担心可移植性)。 – Juliano 2010-09-24 18:13:21

+0

所有系统都是64位(带有64位文件指针和偏移量),我可以成功映射40GB文件。为了可重复性,我只是将问题归结为1GB。 – Dave 2010-09-26 10:17:30

+0

@Sven。有时使用内存映射是不可避免的,例如,当库调用需要内存区域而不是文件时。所以你的建议是无益的,并没有回答这个问题。 至于答案,显然在Linux上MMAP_SEQUENTIAL是非常*破碎*。预读部分工作,页面回收部分不工作。唯一的办法是向Linux提出,实际上这些页面是很好的候选者是通过取消映射区域(并重新映射)。 – 2015-03-06 00:51:15

回答

8

Linux内存管理是从其他系统不同。关键原则是没有使用的内存会浪费内存。在很多方面,Linux都会尝试最大限度地利用内存,从而导致(大部分时间)具有更好的性能。

这并不是说“没有任何工作”在Linux中,但它的行为有点不同于你的期望。

当从mmapped文件中提取内存页时,操作系统必须决定它将释放(或换出)哪些物理内存页以便使用。它会查找更容易换出的页面(不需要立即写入磁盘),并且不太可能再次使用。

madvice()POSIX调用用来告诉系统应用程序如何使用页面。但正如名称所示,这是一个建议,以便操作系统更好地进行分页和交换决策。这既不是政策也不是秩序。

为了演示madvice()在Linux上的效果,我修改了一个给予我的学生的练习。请参阅complete source code here。我的系统是64位,有2 GB的RAM,现在大约有50%正在使用。使用该程序来映射2 GB文件,按顺序读取并丢弃所有内容。它每读取200 MB报告RSS使用情况。结果无madvice()

<[email protected]> ~% ./madvtest file.dat n 
    0 :  3 MB 
    200 : 202 MB 
    400 : 402 MB 
    600 : 602 MB 
    800 : 802 MB 
    1000 : 1002 MB 
    1200 : 1066 MB 
    1400 : 1068 MB 
    1600 : 1078 MB 
    1800 : 1113 MB 
    2000 : 1113 MB 

Linux的不断推出来的东西内存,直到1 GB左右被读取。之后,它开始对流程本身施加压力(因为另外50%的内存是由其他进程激活的)并且一直保持到文件结束。

现在,与madvice()

<[email protected]> ~% ./madvtest file.dat y 
    0 :  3 MB 
    200 : 202 MB 
    400 : 402 MB 
    600 : 494 MB 
    800 : 501 MB 
    1000 : 518 MB 
    1200 : 530 MB 
    1400 : 530 MB 
    1600 : 530 MB 
    1800 : 595 MB 
    2000 : 788 MB 

需要注意的是Linux的决定页面分配给过程只,直到它达到大约500 MB,更快比没有madvice()。这是因为在此之后,目前在内存中的页面似乎比通过此过程被标记为顺序访问的页面更有价值。 VMM中有一个阈值,用于定义何时从该进程中删除旧页面。

您可能会问,为什么Linux一直在分配大约500 MB的页面,并且因为它们被标记为顺序访问,所以不会很快停止。无论如何,系统要么拥有足够的可用内存页面,要么其他常驻页面太旧而无法保持。在保留内存中似乎不再有用的古代页面之间,以及为运行现在的的程序提供更多页面之间,Linux会选择第二个选项。

即使他们被标记为顺序访问,它只是一个建议。应用程序可能仍然希望返回到这些页面并再次阅读它们。或系统中的另一个应用程序。 madvice()调用只说明应用程序本身在做什么,Linux则考虑更大的图景。

+0

谢谢Juliano,50%的行为很有趣。我只是想知道为什么没有办法强制Linux释放我再也不会读取的页面。相反,它会牺牲文件系统的缓冲区和缓存。 在MacOS X上牺牲这些缓冲区会阻塞系统,直到它完全无法使用。但幸运的是,我们可以通过 * msync(... MS_INVALIDATE)* 来防止在Linux上这似乎是您观察到的使用madvice防止系统停滞的行为。 – Dave 2010-09-27 22:50:17

+1

@Dave:认为没有必要过早地释放这些页面。 Linux不会牺牲缓存和缓冲区,相反,它正是这样做的。当你从磁盘读取更多数据时,Linux必须将这些数据带到内存中。它对从磁盘读取的内容进行排序,但不是将其视为“缓存”,而是将其作为映射该文件的进程的RSS部分的一部分进行计算。当Linux需要再次缓存时,它将释放映射到该应用程序的那些页面。你不需要担心这一点! – Juliano 2010-09-28 04:09:54

+0

@Juliano:认为MADV_SEQUENTIAL专门告诉系统页面将通过顺序读取访问一次。这些页面是回收的理想选择。相反,我看到在我的盒子里,直到达到50%的内存(在这种情况下是32GB),文件缓存才被回收。我发现其他进程的性能正在下降,现在我发现了一种迫使linux不这样做的嘲讽方式。通过重新映射和映射文件,每1Gb左右。 * DOES *解决了问题,之后我没有看到其他进程的性能下降。 – 2015-03-06 00:14:28