为什么mmap在iOS上失败?

时间:2012-11-16 22:45:44

标签: ios memory-management mmap

我正在尝试使用mmap在iOS上读取和播放音频文件。它适用于高达约400MB的文件。但是当我尝试500MB文件时,我收到ENOMEM错误。

char *path = [[[NSBundle mainBundle] pathForResource: @"test500MB" ofType: @"wav"] cStringUsingEncoding: [NSString defaultCStringEncoding]];
FILE *f = fopen( path, "rb" );
fseek( f, 0, SEEK_END );
int len = (int)ftell( f );
fseek( f, 0, SEEK_SET );

void *raw = mmap( 0, len, PROT_READ, MAP_SHARED, fileno( f ), 0 );

if ( raw == MAP_FAILED ) {
    printf( "MAP_FAILED. errno=%d", errno ); // Here it says 12, which is ENOMEM.
}

为什么吗

我很高兴得到一个答案,例如“700MB是虚拟内存限制,但有时地址空间是碎片化的,所以你要获得700MB但是更小的块”。 (这只是推测,我仍然需要答案)

关于虚拟内存的Apple文档页面说:

  

虽然OS X支持后备存储,但iOS不支持。在iPhone中   应用程序,磁盘上已有的只读数据(例如代码)   页面)只是从内存中删除,并根据需要从磁盘重新加载。

这似乎证实了mmap应该适用于大于物理内存的块,但仍然无法解释为什么我会达到如此低的限制。

更新

  • This answer很有意思,但500MB远远低于它提到的700MB限制。
  • This discussion提及连续记忆。那么内存碎片可能是一个真正的问题吗?
  • 我正在使用具有256MB物理内存的iPod Touch第4代。
  • 我的研究的目的是看看在从文件加载只读数据时是否有更好的方法来管理内存,而不是“保持分配直到你收到内存警告”mmap似乎是解决这个问题的好方法......

更新2

我希望mmap能够与新的64位版本的iOS完美配合。一旦我拿到64位设备就会测试。

4 个答案:

答案 0 :(得分:3)

经过进一步调查并阅读 excellent blog post by John Carmack ,以下是我的结论:

  • 700MB是iOS上的虚拟内存限制(截至2012年,32位iOS)
  • 单个区块可能有也可能没有;这取决于设备状态和应用行为

因此,为了可靠地mmap 700MB的文件数据,有必要将其分成更小的块。

答案 1 :(得分:2)

我没有给你答案,但是我在运行6.0.1的iPhone 5上测试了你的代码,mmap在700MB ISO文件上成功了。所以我会从其他因素开始,并假设mmap正常工作。也许你回来的错误并不是真的是由于内存,或者设备本身的内存在某种程度上已经耗尽到了mmap失败的地方;尝试重启设备。根据你的iOS版本,我想知道你在文件末尾的搜索是否可能导致mmap尝试映射整个文件;您可以尝试清理它并使用stat来确定文件大小,或者关闭然后在映射之前重新打开文件描述符。这些都只是想法;如果我可以重现你的错误,我很乐意帮助解决它。

答案 2 :(得分:0)

使用NSData,不要直接在这里触摸mmap。

要获得错误读取的优点,请使用NSDataReadingMapped。 NSData还可以在低内存情况下释放字节

答案 3 :(得分:0)

通常,可用的物理内存量与您是否能够mmap文件无关。这是我们所讨论的所有VIRTUAL记忆之后。 iOS上的问题是,至少根据iOS应用程序编程指南,虚拟内存管理器仅交换只读部分......理论上这意味着您不仅受可用地址空间量的限制,但是,如果要使用PROT_READ以外的任何内容进行映射,则还会受到可用RAM量的限制。 见http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/TheiOSEnvironment/TheiOSEnvironment.html

然而,您可能遇到的问题可能是缺少足够大的连续内存,无法在虚拟地址空间中进行映射。据我所知,Apple没有公布用户模式进程的内存上限。通常,地址空间的上部区域是为内核保留的。在用户模式下,您可能只有16位内存。

如果没有在调试器中转储内存映射,你看不到的是,有很多(我在Apple的简单示例应用程序中计算了100多个)共享库(dylibs)加载到进程地址空间。其中每个都是mmap,并且每个都将分割可用的地址空间。

在gdb中,您应该能够使用'info proc mappings'转储内存映射。不幸的是,在lldb中,我能找到的唯一等价物是“图像列表”,它只显示共享库映射,而不是数据mmap映射。

以这种方式使用调试器,您应该能够确定地址空间是否具有足够大的连续块以用于您尝试映射的数据,尽管它需要花些时间来发现上限(Apple应该发布这个!)