Ben*_*rns 15
操作系统没有强制执行直接或显式的虚拟内存限制,因此您的应用程序理论上可以分配其整个潜在的逻辑地址空间.对于32位处理器,此大小为4千兆字节,对于64位处理器,此大小为18艾字节.
实际上,可用虚拟内存大小存在一些限制,这在很大程度上取决于应用程序如何使用其内存空间.这些限制源于操作系统管理虚拟内存的方式效率低下,因为操作系统针对低功耗而非大量文件I/O进行了积极调整.
如果您不关心这些实际限制的细节,请随时停止阅读.否则,接下来是我在研究iOS上的内存问题,以及虚拟内存和内存映射文件的入门,以及关于iOS与其他操作系统的不同之处的讨论.
在现代操作系统(包括iOS)中,您的进程可以访问的所有内存都是技术上的虚拟内存.它被称为"虚拟"内存,因为暴露给进程的地址空间(称为进程的逻辑地址空间)不一定与机器的物理地址空间,甚至是其他进程的虚拟地址空间对齐.虚拟内存允许内核在不同的上下文中提供不同的逻辑地址空间.
在iOS上,此逻辑地址空间的大小理论上仅受指针大小的限制,指针大小由处理器体系结构定义.这意味着进程在32位处理器上具有大约4千兆字节的逻辑内存空间,在64位处理器上具有18艾字节.
要回答这个问题,首先你必须意识到,当你的进程调用类似malloc内存的东西时,内核会设置进程逻辑地址空间的一部分并标记它的分配.在这种情况下,这个分配的内存也占用了物理内存空间.存储在物理存储器中的数据称为驻留,因为它驻留在物理存储器中,而驻留的数据被称为驻留集的一部分.内核通常通过跟踪进程的驻留集大小来计算物理内存使用量与物理内存限制.
如果这是您的进程分配内存的唯一方式,那么对于大多数现代操作系统,您的进程将受到未分配的物理内存量以及计算机页面文件,交换分区或其他可用空间量的限制.非易失性后备存储.但是,iOS没有后备存储,并且它对进程的最大驻留集大小强制执行相当严格的约束,因此通常应用程序的驻留集大小将受可用物理内存和每进程驻留集限制中较小者的限制.
这有用的一个例子是内存映射文件.
如果您的应用程序需要快速访问文件,并且您希望依赖内核来确定要读入的文件的哪些位以及何时读取,则可以对内存映射文件(请参阅文档mmap).在这种情况下,内核将文件(或您请求的文件部分)作为进程逻辑地址空间的连续区域呈现给进程.
操作系统将捕获对为映射文件保留的逻辑内存空间部分的访问,并在访问文件时透明地将文件块复制到物理内存中.这些"块"被称为页面,允许内核执行此操作的陷阱称为a page fault,而使数据驻留的行为称为分页,并且从内存中删除页面称为分页.这一切都使得你的过程看起来就像你已经在内存中一直映射的文件区域(或整个文件,如果这是你映射的那样).当然,为了使其正常工作,操作系统必须检测暂时未访问的页面,并将其分页.
在上面的场景中,与大多数桌面类操作系统相比,iOS有点受限.具体来说,从我在文档中看到的情况来看,似乎只有在使用PROT_READ标志映射时才能从物理内存中清除iOS页面.这意味着,如果您使用该PROT_WRITE标志映射文件,则此文件中的页面将保持驻留状态,直到您调用munmap(我无法单独从文档中判断这是否意味着所有页面将保持驻留,或者它是否只是修改过的页面).
此行为与其他操作系统不同,因为在PROT_WRITE使用时MAP_SHARED,可以随时清除未修改的页面,并且一旦修改的页面与磁盘同步(也就是说,一旦它们不再"脏"),就可以清除它们.此外,对于其他操作系统,在使用时PROT_WRITE和MAP_PRIVATE未修改页面的情况保持不变,但只要操作系统使用某些"后备存储"(例如页面文件),仍可以从物理内存中清除已修改的页面.
在后一种情况下,iOS无法将这些修改分页,因为它没有后备存储.我发现先前的情况令人困惑,因为理论上没有逻辑限制来允许同步页面被分页.我可以推断出最多的是,在PROT_WRITE和MAP_SHARED情况下,这些页面保持居民,以避免在SMP环境中的I/O调度和寻呼机之间的紧密同步的需要.文档中的弱建议(下面的链接)支持此推断,在OSX上,修改后的页面将被写入后备存储,无论它们是否与磁盘同步.不过,我打算在Mach内核上做更多的阅读,以更全面地理解这些局限性.
因此mmap,PROT_READ似乎是我们使用所有流程的可用VM空间而不会达到常驻设置大小限制的路径,对吧?我的意思是,使用这种方法,理论上你可以多次创建一些大型文件的只读映射,每个映射占用进程可用逻辑内存空间的一个单独部分.在32位操作系统上,即使在32位iOS上,您也可以很容易地通过虚拟地址空间饱和来实现您经过深思熟虑的世界统治计划.ENOMEM为了所有人!
实际上,虽然在许多操作系统上,你可以只是mmap一个文件并且在操作系统找出细节时很容易休息,但在iOS上并非如此.即使只读映射文件占据了进程的逻辑地址空间,仍然可能会因为超出常驻设置内存限制而杀死您的应用程序.这是因为内核决定哪些页面可以从物理内存中删除.
在阅读下一部分时,请记住iOS已经非常积极地调整功率效率.它不是文件服务器.
内核只释放已标记为非活动状态的页面,我将从下面链接的文档中推断出,如果页面在某个阈值时间段内未被触摸,则只能将其标记为非活动状态.
我还将从文档和我自己的经验推断,负责标记页面不活动的内核部分与负责检查驻留集大小限制的内核部分不能很好地协调.
当您的常驻设置大小接近限制时,操作系统会尝试清除非活动页面,并触发内存警告,应用程序应通过减少其驻留设置大小来响应.一旦您的常驻设置大小超过某个阈值,jetsam就会杀死您的应用.
因此,鉴于上述情况,当您的应用程序在大量页面上进行大量随机访问时,如果负责将页面标记为非活动状态的内核部分未运行一段时间会发生什么?
即使您的常驻集的大部分被只读映射文件页面占用,您的应用也会被终止.
我相信这是苹果建议尽可能使用更小,更瞬态映射的主要原因之一,而不是映射整个文件(尽管在该使用模式中,内存空间碎片开始成为一个问题).
如果您想了解更多信息,请参阅Apple关于iOS和OSX虚拟内存的文档,以及文件系统高级编程主题文档的内存映射文件部分以及mmapiOS的手册页.
另外,还有其他一些值得阅读的相关主题,这使得这一切变得更加复杂和有用,例如页面锁定,页面同步,I/O缓存,预取,以及对于如此深刻的好奇,如何操作系统实现对共享映射的高效多处理访问.