Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/98.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么mmap在iOS上失败?_Ios_Memory Management_Mmap - Fatal编程技术网

为什么mmap在iOS上失败?

为什么mmap在iOS上失败?,ios,memory-management,mmap,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

我正在尝试使用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文档页面上说:

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

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

更新

  • 很有趣,但是500MB远低于它提到的700MB限制
  • 提及连续内存。所以内存碎片可能是一个真正的问题
  • 我正在使用第四代iPodtouch,它有256MB的物理内存
  • 我研究的重点是,在从文件中加载只读数据时,是否有比“在收到内存警告之前保持分配”更好的内存管理方法
    mmap
    似乎是解决这个问题的好方法
更新2


我希望mmap能与新的64位版本的iOS完美配合。一旦我接触到64位设备,将进行测试。

使用NSData,不要直接在此处触摸mmap

要获得错误读取的优势,请使用NSDataReadingMapped。
NSData在内存不足的情况下也会释放字节

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

在进一步调查和阅读此后,以下是我的结论:

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

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

通常可用的物理内存量与您是否能够mmap文件无关。这毕竟是我们谈论的虚拟内存。iOS上的问题是,至少根据iOS应用程序编程指南,虚拟内存管理器只交换只读部分。。。从理论上讲,这意味着您不仅受到可用地址空间数量的限制,而且还受到可用RAM数量的限制,如果您使用的是PROT_READ以外的任何东西进行映射的话。 看

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

如果不在调试器中转储内存映射,您将无法看到有许多共享库(我在Apple的一个简单示例应用程序中计算了100多个)加载到进程地址空间中。每一个都是mmap'd in,每个都将分割可用的地址空间

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


以这种方式使用调试器,您应该能够确定地址空间是否有足够大的连续块,以容纳您试图映射的数据,尽管这需要一些工作来发现上限(苹果应该发布这个!)

嗯,一个500MB的文件几乎是iPhone 4上实际可用的可用RAM的两倍大。。。你期待什么
mmap()
不是魔术。@H2CO3我希望它能根据需要将文件数据分页到RAM中,因为mmaap区域是可以访问的。这不是mmap所做的吗?这是一个实现细节a。你不应该依赖的,b。这是你不应该期望的,c。可以通过阅读libSystem源代码的相关部分来查找。@H2CO3在256MB的iPod Touch上有一个400MB的文件,它不会失败。“对mmap()没有期望”听起来不对。存在mmap()的全部原因(与read()相反)是,文件可以按需分页,并在VM系统认为合理时从物理内存中删除。即使只有一个物理页的RAM空闲,也应该可以将mmap()映射到一个大文件。但是,如果mmap()的范围大于最大的连续虚拟内存块,则会失败--您可能应该一次映射文件的较小部分以解决此问题。感谢您实际尝试并报告您的结果。我认为重新启动设备可能会有所帮助,但显然这也是我不能要求用户做的事情。至于查找可能导致mmap失败,我不认为是这种情况-我尝试了硬编码文件大小,但没有使用ident进行查找