free(含 inactive 补充),再以 的稳定速率回收干净文件页(线性拟合 R²=),文件页接近保护地板时才启动压缩器(t=s 时才首次大量压缩)。EXPSRCfilecache_min = AVAILABLE×10/70 的理论值 高 ——多出的部分由「inactive 外部页平衡目标」与「99% 重激活保护」共同造成;压力撤销后地板仍保留。EXPSRCMADV_DONTNEED 后内容全部保留(32768/32768 页),重触达 0 pagein——它只是「去激活」(清除 refmod + 移入 inactive),源码将其映射到 vm_map_msync(VM_SYNC_DEACTIVATE)。跨平台移植代码不可依赖 Linux 丢弃语义。EXPSRCfilecache(交叉编译 + 设备端 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)——与内核回收策略的决策变量是同一个计数器,实验观测即内核视角。方法:先把 1GB 测试文件读入 UBC(文件缓存 3.4GB),随后以 5×16MB/0.5s 的速率持续提交匿名内存,250ms 采样全设备 VM 计数器,直到稳定态。对照组将匿名数据换成不可压缩的随机数据。
vps_choose_victim_page:干净文件页直接 free(L3818),匿名页走压缩器(L3864),当 pageable_external < filecache_min 时 99% 的文件页受害者被重激活保护(L2687-2716,每 100 次只偷 1 页)——实验测得的「下降骤缓」正是该保护生效的直接证据。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,再无失稳。
方法:512MB 测试文件;warm pass 在 UBC 命中状态下测;随后用压力把文件页真正驱逐出 UBC,立即做 cold pass(全程 d_pageins == 全部页数 校验真实从盘读)。
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 体系(不在本报告范围)。msync(MS_SYNC) 仅 ()——回写是 NVMe 写带宽瓶颈(含 FTL 损耗均衡),这也是内核在回收路径上极力避免脏页的原因:vm_pageout_scan 对干净页直接 free(零 I/O),脏外部页须先 vm_pageout_cluster 走 pager 回写再进 cleaned 队列(L3857-3869)。数据闭环:全程干净页回收 次 pageouts 即完成 的回收量。方法:kill -9 后 uiopen 冷启动,appsamp(task_for_pid + TASK_EVENTS_INFO)在进程创建后尽早附加;「附加时刻的生涯 pageins」即启动窗口内从 NVMe 读入的文件页总量。
filecache_min 保护了应用二进制/数据页不被牺牲,代价由我们的测试文件页承担。这直接回答「6GB 设备上大应用生态为何稳定」:应用页被策略性地视为最值得保留的文件页。xnu-verify/deep-read/(与设备内核 xnu-7195.140.44~1 同属 7195 家族);每个锚点均可按「文件 + 行号」复核。策略公式数值验证:filecache_min = (active+inactive+free+spec)×10/70 代入稳态实测值得 676MB,实测地板 1066MB,差值 390MB 由 inactive 平衡目标解释(详见 §2.2)。scripts/audit.py 对本页每个数值断言从原始 JSON 重算(线性回归、比率、守恒校验), 项全部通过;原则是「冻结验算式与口径,不冻结数值」——累计计数器漂移属正常,公式与数据源固定。scripts/results-final/*.json(31 个文件,60KB 汇总 + 全序列)由设备直接产出,未做人工修饰;网站数字由 build_data.py 单点生成。d_pageins 全量校验,杜绝把 UBC 命中误当磁盘读。