iOS 越狱真机实测:文件页回收性能与管理策略多维论证

iPhone 12 Pro (A14 / 6GB / iOS 14.8 / Taurine) · 每条结论均由「真机实验数据」与「XNU 7195 源码行号锚点」双重支撑 —— 数据可复核(audit.py 24 项机械校验全通过),源码可对勘。
EXP = 真机实验可复现 SRC = XNU 源码行号可证

0. 摘要 — 六条核心结论

  1. 回收是三级瀑布,顺序明确:内存压力下内核先用尽 free(含 inactive 补充),再以 的稳定速率回收干净文件页(线性拟合 R²=),文件页接近保护地板时才启动压缩器(t=s 时才首次大量压缩)。EXPSRC
  2. 文件缓存有硬性地板:不可压缩匿名压力下实测地板 ,比源码公式 filecache_min = AVAILABLE×10/70 的理论值 ——多出的部分由「inactive 外部页平衡目标」与「99% 重激活保护」共同造成;压力撤销后地板仍保留。EXPSRC
  3. 重填代价差距悬殊且可量化:UBC 命中 )vs 驱逐后从 NVMe 重读 ),全程 倍差距;单页故障中位数更达 倍(1.6µs → 492µs)。EXP
  4. iOS 的 MADV_DONTNEED 不是 Linux 语义:只读映射与 COW 私有脏副本在 MADV_DONTNEED内容全部保留(32768/32768 页),重触达 0 pagein——它只是「去激活」(清除 refmod + 移入 inactive),源码将其映射到 vm_map_msync(VM_SYNC_DEACTIVATE)。跨平台移植代码不可依赖 Linux 丢弃语义。EXPSRC
  5. 真实应用启动 I/O 几乎全是文件页:微信/淘宝/京东/美团冷启动从 NVMe 读入 文件页;UBC 命中时热启动 I/O 降至 (微信 倍差)。驱逐实验显示 filecache_min 地板保护了应用页——重压之后启动依然便宜(0.4MB)。EXP
  6. 脏文件页回写昂贵且异步:msync(MS_SYNC) 仅 (脏化速率的 ),干净页回收则「免费」(全程 pageouts 仅 )——这解释了策略为何强烈偏爱回收干净文件页。EXPSRC

1. 测试环境与仪器

测量仪器为自研 filecache(交叉编译 + 设备端 ldid 签名,仅链接 libSystem),通过 host_statistics64 读取 external_page_count 等计数器、task_info 采样进程事件、mincore 验证驻留;压力由 fork 出的步进提交子进程产生(每步 mmap-touch 16–128MB)。关键口径:external_page_count = vm_page_pageable_external_count + local_q_external(host.c L868)——与内核回收策略的决策变量是同一个计数器,实验观测即内核视角。

2. 维度一:策略排序(谁先被回收)

方法:先把 1GB 测试文件读入 UBC(文件缓存 3.4GB),随后以 5×16MB/0.5s 的速率持续提交匿名内存,250ms 采样全设备 VM 计数器,直到稳定态。对照组将匿名数据换成不可压缩的随机数据。

2.1 可压缩匿名压力下的完整交叉序列

论证:三阶段清晰可辨——①free 消耗期(ext 纹丝不动);②线性回收期:free 钉在 ~30MB,ext 以 MB/s 匀速下降(R²=,n=),此阶段压缩器零参与;③压缩器接管期:ext 降到 附近后下降骤缓,compressions 计数跳升( 次压缩)。源码对应 vps_choose_victim_page:干净文件页直接 free(L3818),匿名页走压缩器(L3864),当 pageable_external < filecache_min 时 99% 的文件页受害者被重激活保护(L2687-2716,每 100 次只偷 1 页)——实验测得的「下降骤缓」正是该保护生效的直接证据。

2.2 不可压缩压力:实测 filecache_min 地板

论证:随机数据不可压缩,内核无法借道压缩器,只能持续牺牲文件页——直到 ext 触及 完全稳定(末 60s 标准差 < 30MB,Δ=<15MB)。该地板高于 filecache_min 公式值()约 390MB:多出部分由 VM_PAGE_INACTIVE_TARGET(pageable_external) = pageable_external/2(L226-228,inactive 外部页平衡目标)与重激活保护共同构成。稳态物理账目闭合:external + internal + 压缩器 (存 未压缩数据,比率 :1)+ free + spec 134MB + wired ≈999MB = 6144MB。
两次异常事件本身也是数据:为什么实验有两次设备重启

早期把 jetsam 防线设为 free<12MB 时,设备两次在持续极低压下 panic 重启(userspace 重置,越狱会话重建)。这本身就是「文件页地板 + 压缩器满载仍不够时系统宁可崩溃也不愿无底线牺牲文件缓存」的极端证据。后续实验把防线提高到 free+inactive≥180MB,再无失稳。

3. 维度二:重填代价(回收的代价有多大)

方法:512MB 测试文件;warm pass 在 UBC 命中状态下测;随后用压力把文件页真正驱逐出 UBC,立即做 cold pass(全程 d_pageins == 全部页数 校验真实从盘读)。

论证:①单档差距 倍(ns/fault 口径),吞吐从 跌至 ——回收 512MB 文件页的代价是下次访问时多花 ~4.9s 的 I/O 等待(512MB / 822MB/s);②随机访问冷读更糟( vs 顺序 ),顺序预取在 mmap 冷路径几乎无效(MADV_SEQUENTIAL 前后 19050 vs 18997 ns/fault,差 <0.3%)——iOS 的 mmap 文件预取不遵循 POSIX 提示语义,或预取粒度不足以掩盖 16KB 随机读延迟;③单页故障直方图:UBC 1.6µs / 驱逐后 492µs = 倍,且 p99 683µs——应用若在滚动关键路径上触碰被驱逐页,将产生肉眼可感的卡顿(>1 帧预算 16.7ms 的 4%)。

4. 维度三:提示与语义(开发者能控制什么)

论证:①posix_fadvise 在 iOS 14.8 用户态不存在(dlsym 失败)——Linux 的 POSIX_FADV_DONTNEED 式缓存丢弃在此平台不可用;②MADV_DONTNEED 语义是「去激活」而非「丢弃」:只读映射与 COW 脏副本内容 100% 保留(32768/32768 页字节不变),重触达 0 pagein、延迟与 warm 持平——源码链:madvise(MADV_DONTNEED) → vm_map_msync(VM_SYNC_DEACTIVATE) → vm_object_deactivate_pages(kill_page=0),仅清 refmod 位并入 inactive 队列;③MADV_WILLNEED 确实触发预取(57ms 内 4096 页全驻留),对后续访问吞吐提升 ~13%;④F_NOCACHE 对 read() 吞吐几乎无影响(6.4 vs 6.6 GB/s)——该路径本就不经 UBC 计数。实践含义:iOS 上想主动释放文件缓存,唯一可靠途径是 munmap(对象引用清零后随回收压力自然释放);想要立即释放匿名页,用 MADV_FREE_REUSABLE 体系(不在本报告范围)。

5. 维度四:脏文件页回写

论证:MAP_SHARED 脏化 vs msync(MS_SYNC))——回写是 NVMe 写带宽瓶颈(含 FTL 损耗均衡),这也是内核在回收路径上极力避免脏页的原因:vm_pageout_scan 对干净页直接 free(零 I/O),脏外部页须先 vm_pageout_cluster 走 pager 回写再进 cleaned 队列(L3857-3869)。数据闭环:全程干净页回收 次 pageouts 即完成 的回收量。

6. 维度五:真实应用视角(微信/淘宝/京东/美团)

方法:kill -9 后 uiopen 冷启动,appsamp(task_for_pid + TASK_EVENTS_INFO)在进程创建后尽早附加;「附加时刻的生涯 pageins」即启动窗口内从 NVMe 读入的文件页总量。

论证:①冷启动 I/O 与应用体积正相关(微信 219MB ≈ 其安装包解包后可执行+资源热区);②UBC 命中的热启动把 I/O 压到 (微信 12.5MB, 倍差)——文件缓存是应用启动性能的核心资产;③地板保护的实战意义:先用匿名压力把文件缓存压到地板,再冷启动微信,启动 I/O 仅 0.4MB——filecache_min 保护了应用二进制/数据页不被牺牲,代价由我们的测试文件页承担。这直接回答「6GB 设备上大应用生态为何稳定」:应用页被策略性地视为最值得保留的文件页。

7. 源码锚点总表(与实验的对应关系)

验证方法:xnu-7195.141.2 源码存档于本仓库 xnu-verify/deep-read/(与设备内核 xnu-7195.140.44~1 同属 7195 家族);每个锚点均可按「文件 + 行号」复核。策略公式数值验证:filecache_min = (active+inactive+free+spec)×10/70 代入稳态实测值得 676MB,实测地板 1066MB,差值 390MB 由 inactive 平衡目标解释(详见 §2.2)。

8. 方法论与可复核性

生成于 2026-08-24 · 数据: iPhone 12 Pro (iOS 14.8, xnu-7195.140.44~1, Taurine 越狱) · 源码: xnu-7195.141.2 (apple-oss-distributions) · 工具链: filecache/appsamp (clang iphoneos + 设备端 ldid) · 部署: Cloudflare Pages