iOS 的 WebKit 是 Apple 自研的多进程 Web 引擎(WebKit2 架构):应用进程只持有 WKWebView 这个"遥控器",真正解析 HTML、执行 JS、排版绘制的是独立的 WebContent 进程,网络走 Networking 进程,整套进程由 XNU 内核的 per-task 内存账本(ledger)、jetsam 优先级带、freezer 冻结机制精细治理——内核源码里甚至有对 com.apple.WebKit.WebContent 的按名字特判。
鸿蒙的 ArkWeb 渲染进程则基于 Chromium + CEF 构建(官方明示),是"五进程模型"中的一环:Web 渲染进程负责 HTML 解析/排版/绘制 + JS/WASM 执行,GPU 光栅化与合成送显被拆到独立的 Web GPU 进程并与系统 RenderService 对接;移动端默认多个 Web 实例共享一个渲染进程以省内存,2in1 设备才默认独立进程。鸿蒙侧官方文档明确写着「Web 内核对内存大小的申请无限制约束」,内存治理策略与 iOS 完全不同。
本质区别一句话:两者都把"网页代码"隔离进无特权的独立渲染进程(防沙箱逃逸),但 iOS 走的是自研引擎 + 内核级强内存配额 + 按站点拆进程的路线;鸿蒙走的是 Chromium 血统 + 共享渲染进程省内存 + 独立 GPU 进程对接自研合成器的路线。
WebKit 是 Apple 主导的开源 Web 浏览器引擎(C++ 为主,部分 C/汇编在 JavaScriptCore,Cocoa 集成为 Objective-C)。它是 iOS/macOS 上的系统框架,Safari、邮件、备忘录、图书、新闻、App Store 都用它,第三方 App 内嵌网页也必须通过 WKWebView。
| 组件 | 功能 |
|---|---|
| bmalloc | WebKit 自己的 malloc:bump pointer allocator + IsoHeap(每种类型隔离到独立页面),核心价值是防御 use-after-free 引发的类型混淆攻击。 |
| WTF | Web Template Framework:模板/容器库(Vector、HashMap、HashSet)与智能指针(Ref/RefPtr/WeakPtr),全代码库的基础设施。 |
| JavaScriptCore (JSC) | JS 引擎,四层执行体系:解释器 → Baseline JIT → DFG JIT → FTL JIT(B3 后端),逐层用编译时间换执行速度;JSC 在 iOS 上的 JIT 依赖 XNU 的 mman.h L127 MAP_JIT 机制。 |
| WebCore | WebKit 最大的组件:HTML/XML/CSS 解析器、DOM 树 / Render Tree、HTML/SVG/MathML 元素实现、独有的 CSS JIT(现存唯一的 CSS 即时编译器)。 |
| WebCore/PAL | 平台抽象层,让 WebCore 在 macOS/iOS/Windows/Linux 保持平台无关。 |
| WebKitLegacy (WebKit1) | 单进程时代的旧架构,实现 WebView/UIWebView(iOS 8 起废弃)。 |
| WebKit (WebKit2) | 多进程架构,实现 WKWebView:UI 进程、WebContent 进程、Networking 进程、WebInspector/WebDriver。 |
| 进程 | 职责 | iOS 特点 |
|---|---|---|
| UI 进程 (即宿主 App) |
持有 WKWebView、处理用户输入、页面生命周期、权限弹窗、原生 UI(菜单/弹窗)。 | 与 App 同进程;WebKit 以系统 framework 形态嵌入。 |
| WebContent 进程 com.apple.WebKit.WebContent |
跑网站的代码:HTML 解析、DOM/RenderTree、JS 执行(JSC JIT)、排版、绘制、Media 处理。Safari 每个标签页通常各一个。 | 沙箱最严:无直接网络 socket(出网必须经 Networking 进程)、无任意文件写、IPC 白名单;受 MACF 策略管控(见 §6.6)。 |
| Networking 进程 | 所有网络请求、TLS、HTTP 缓存、Cookie;存储管理(默认/隐私会话隔离)。 | 同一会话的所有 WebContent 共享一个 Networking 进程的网络会话。 |
| WebInspector / WebDriver | 开发者工具与自动化。 | Inspect WebContent 进程。 |
WebKit 文档定义 Site = scheme + eTLD+1(可注册域)。开启站点隔离后,跨站 iframe 不再和主框架同进程:
为什么要拆进程?渲染引擎解析不可信网页,是整个系统最大的攻击面。任何 Blink/WebCore/JSC 的内存破坏漏洞,最坏也只能拿到 WebContent 的无特权上下文——这正是 iOS WebKit 与鸿蒙 ArkWeb 共同的设计出发点。
iOS 上 WebContent 进程把绘制结果编码成 RemoteLayer 树(IOSurface 后端的 backing store),提交给系统的 RenderServer(即 backboardd 的合成职责)做最终合成送显。WebContent 自身不直接与 GPU 驱动做全量光栅化提交——这层经由平台图形栈与 ANGLE(Metal) 完成。
| 进程 | 唯一性 | 职责(华为官方文档原文语义) |
|---|---|---|
| 应用进程 | 应用唯一 | 主进程,含 UI 主线程与 Web 相关线程:网络线程、Video 线程、Audio 线程、IO 线程;负责 Web 组件对外接口与回调、需与其他系统服务交互的功能(网络、媒体服务)。 |
| Foundation 进程 | 系统唯一 | 接收应用进程的孵化请求,管理应用进程与 Web 渲染进程的绑定关系。 |
| Web 孵化进程 | 系统唯一 | 接收 Foundation 请求,孵化 Web 渲染进程与 Web GPU 进程;孵化后做安全沙箱降权、预加载动态库提速。 |
| Web 渲染进程 | 应用可指定多实例共享或独立 | 运行 Web 渲染进程引擎(HTML 解析、排版、绘制、渲染)+ ArkWeb 执行引擎(JavaScript、WebAssembly)。 |
| Web GPU 进程 | 应用唯一 | 光栅化、合成送显,与 GPU、RenderService 交互,提升应用进程稳定性与安全性。 |
| 模式 | 机制 | 限制与适用 |
|---|---|---|
| 异步渲染 (默认) |
Web 组件作为图形 surface 节点,独立送显(自己管合成节拍)。 | 高度 ≤ 7680 物理像素,超出白屏;不支持动态切换。适合纯 Web 为主体的页面,性能功耗更优。 |
| 同步渲染 | Web 组件作为图形 canvas 节点,跟随系统组件一起送显。 | 不支持 DSS(显示子系统)合成;高度最大 500,000 px。适合 Web 与 ArkUI 组件同滚动的超长混合页面。 |
另有同层渲染(same-layer rendering)支撑小程序类宿主场景,让 Web 内容与原生组件处于同一渲染层。
OpenHarmony web_webview 仓库 README 原文:"nweb is the native engine of the OpenHarmony webview component and is built based on Chromium and the Chromium Embedded Framework (CEF)"。华为官方版本对应表:
| HarmonyOS 版本 | ArkWeb 内核(Chromium) |
|---|---|
| 4.0 及之前 | M99 |
| 4.1 – 5.1 | M114 |
| 6.0 | M132(默认)/ M114(可选) |
| 6.1 | M132 |
| 7.0 | M144(默认)/ M132(可选) |
因此 ArkWeb 的"Web 渲染进程"在血统上是 Chromium RenderProcess:Blink 排版引擎 + V8 JS 引擎(ARM64 上同时配 Les Wasm 解释器),经 CEF/nweb 适配层接入鸿蒙图形栈(RenderService)与系统服务。
| 维度 | iOSWebKit WebContent | 鸿蒙ArkWeb Web 渲染进程 |
|---|---|---|
| 引擎血统 | Apple 自研 WebKit:WebCore + JavaScriptCore(四层 JIT) | Chromium/Blink + V8(经 CEF 适配),版本 M99→M144 |
| 进程数策略 | 每标签页通常一个 WebContent;站点隔离下跨站 iframe 再拆进程 | 移动端默认多实例共享一个渲染进程;2in1 默认独立;token 定向复用 |
| 进程内职责 | HTML 解析、DOM/RenderTree、JS(JSC JIT)、排版绘制、媒体 | HTML 解析、排版、绘制、渲染 + JS/WASM 执行引擎(职责声明几乎对等) |
| 网络 | 独立 Networking 进程;WebContent 无 socket 权限 | 网络线程在应用进程(与 Video/Audio/IO 线程并列) |
| GPU/合成 | 图形栈经 ANGLE(Metal);结果以 RemoteLayer/IOSurface 交 RenderServer 合成 | 独立 Web GPU 进程做光栅化与合成送显,对接 RenderService;异步/同步两种送显模式 |
| 进程创建方 | UI 进程请求系统(launchd 系)拉起 WebContent | 应用 → Foundation 进程 → Web 孵化进程(孵化 + 沙箱降权 + 预加载动态库) |
| 内存治理 | XNU per-task phys_footprint ledger + jetsam band + freezer 冻结;内核对 WebContent 有专门统计与冻/解冻特判(§7) | 官方文档:「Web 内核对内存大小的申请无限制约束」;治理交由上层(OOM 时 onRenderExited 回调通知应用) |
| 崩溃/无响应感知 | 进程退出由 WebKit 内部 + 系统服务处理,页面级 reload | 显式 API:onRenderExited(OOM/crash/正常)、onRenderProcessNotResponding/Responding |
| JS JIT 与 W^X | JSC JIT 用 MAP_JIT;XNU 强制 JIT 区 RWX 全权限、W^X 互斥检查(§6.5) | V8 JIT;鸿蒙内核为 Linux 血统,沿用标准 W^X/双映射策略 |
| 可丢弃内存 | MADV_FREE_REUSABLE/REUSE(XNU 定义)支撑 WebKit 内存缓存丢弃 | Chromium 的 discardable memory(ashmem/memfd 系) |
| 宿主组件 | WKWebView(iOS 8+,唯一现代入口;UIWebView 已废弃) | ArkUI Web 组件 + WebviewController(@kit.ArkWeb) |
以下全部锚点基于 xnu-11215.1.10(iOS 18),文件归档于本报告仓库 xnu-render-analysis/xnu-11215.1.10/,行号可直接复核。跨版本数据来自 iOS 12/13/14/16/17/18 六版本同口径对比。
task.c L1178-1196 的注释块给出了权威定义——WebContent 进程的内存上限按这个口径计量:
/* phys_footprint: This is the sum of: * + (internal - alternate_accounting) * + (internal_compressed - alternate_accounting_compressed) * + iokit_mapped * + purgeable_nonvolatile + purgeable_nonvolatile_compressed * + page_table */ 1241 task_ledgers.internal = ledger_entry_add(t, "internal", ...); 1243 task_ledgers.iokit_mapped = ledger_entry_add_with_flags(t, "iokit_mapped", ...); 1249 task_ledgers.page_table = ledger_entry_add_with_flags(t, "page_table", ...); 1251 task_ledgers.phys_footprint = ledger_entry_add(t, "phys_footprint", "physmem", "bytes"); 1253 task_ledgers.internal_compressed = ledger_entry_add(t, ...); 1290 task_ledgers.frozen_to_swap = ledger_entry_add(t, "frozen_to_swap", ...); // CONFIG_FREEZE
要点:匿名脏页 + 压缩器里的份额 + IOKit 映射 + 非易失 purgeable + 页表都算进足迹——WebContent 里庞大的解码图像、IOSurface、JIT 页无一漏网。iOS 18 还加了 media/graphics/neural 分类记账(task.c L1259-1295),为分类限费铺路。
超限回调直接挂在账本上:task.c L1460
1460 ledger_set_callback(t, task_ledgers.phys_footprint, task_footprint_exceeded, NULL, NULL);
7081 PROC_CROSSED_HIGH_WATERMARK__SEND_EXC_RESOURCE_AND_SUSPEND(...) // 先挂起进程(SUSPEND),再发 EXC_RESOURCE 例外; // hwm_user_cores 时还会先落一份 coredump(L7104-7112) 7207 task_footprint_exceeded(int warning, ...) 7240 // CONFIG_DEFERRED_RECLAIM:先同步回收再复核余额, // 余额已回落则直接 return(给进程自救机会) 7284 task_process_crossed_limit_no_diag(...) 7297 PROC_CROSSED_HIGH_WATERMARK__SEND_EXC_RESOURCE_AND_SUSPEND(...); // 真超限 7319 PROC_CROSSED_HIGH_WATERMARK__SEND_EXC_RESOURCE_AND_SUSPEND(...); // 诊断阈值
memlimit 分 active/inactive 两档(App 前后台切换时换限额),fatal 与否由 memorystatus 决定。WebContent 进程超限的最终结局通常是 jetsam 的 per-process-limit 原因被杀。
Jetsam 原因码全表 kern_memorystatus.c L103-117:
jettisoned | highwater | vnode-limit | vm-pageshortage | proc-thrashing
fc-thrashing | per-process-limit | disk-space-shortage | idle-exit
zone-map-exhaustion | vm-compressor-thrashing | vm-compressor-space-shortage
low-swap | sustained-memory-pressure | vm-pageout-starvation
优先级带 kern_memorystatus.c L126-143:FOREGROUND / AUDIO_AND_ACCESSORY / CONDUCTOR / DRIVER_APPLE / HOME / EXECUTIVE / IMPORTANT / CRITICAL(数值定义在 sys/proc.h 的 JETSAM_PRIORITY_*)。kill 主循环 kern_memorystatus.c L3884-3990(memorystatus_thread_internal) 的次序:先清超 highwater 的进程 → 再 swap 全部 app → 然后逐 band 从最不重要往重要杀,每杀一个都回头重查 highwater。
关键限额参数:
// kern_memorystatus_freeze.c (xnu-11215.1.10) 104 unsigned int memorystatus_frozen_count_webcontent = 0; 224 static void memorystatus_demote_frozen_processes(bool urgent_mode); 312 calculate_thaw_percentage(uint64_t frozen_count, uint64_t thaw_count); 366 get_thaw_percentage_webcontent(); // WebContent 专属解冻率
Freezer 把后台进程的用户态匿名页整进程收进压缩器/swap(iOS 18 桌面级 iPad 与 Mac 上 swap 路径完备:vm_compressor_backing_file.c L47/L88/L108/L146/L182,含 FIOPINSWAP ioctl、VSWAP 标记与 ENCRYPTED_SWAP)。冻结名额有限(memorystatus_frozen_processes_max),解冻率决定下一轮还愿不愿意冻它——这正是 §7 里 WebContent 特判的意义。
冻/解冻会计账进 ledger 的 frozen_to_swap 条目 task.c L1290 / L5099-5121。
// bsd/sys/mman.h 127 #define MAP_JIT 0x0800 // Allocate a region that will be used for JIT purposes 212 #define MADV_FREE_REUSABLE 7 // pages can be reused (by anyone) 213 #define MADV_FREE_REUSE 8 // caller wants to reuse those pages
// osfmk/vm/vm_map.c — JIT 区域的三重约束 626/2882 printf("CODE SIGNING: ... curprot cannot be write+execute..."); // W^X 互斥 2895 if (entry_for_jit && cur_protection != VM_PROT_ALL) { // 非 macOS 平台:MAP_JIT 区必须是全 RWX,否则拒绝 2930 printf("CODE SIGNING: ... JIT requires RWX: failing."); return KERN_PROTECTION_FAILURE; } 2945 if (map->map_disallow_new_exec == TRUE) { ... } // 可执行锁死:封掉新增可执行映射
解读:iOS 上 W 与 X 永久互斥(L626/L2882);带 com.apple.security.cs.allow-jit 身份的进程才能用 MAP_JIT,且该区域权限必须是完整的 RWX(L2895-2930,"JIT requires RWX");进程还能自愿锁死后续可执行映射(L2945)。WebContent 里 JSC 的 Baseline/DFG/FTL JIT 代码页就活在这个 MAP_JIT 区里,受 pg_tlb/code signing 监控。对照:鸿蒙的 V8 JIT 走 Linux 血统内核的标准 W^X 双映射,无 MAP_JIT 概念。
MADV_FREE_REUSABLE/REUSE 则是 WebKit 内存缓存"可丢弃"语义的内核支撑——内存吃紧时这些页可被任何人复用,不计入进程压力。
XNU 的强制访问控制框架(TrustedBSD MAC)在内核埋了几百个策略点,sandbox/ContainerManager 等策略模块(闭源)挂上来:
// security/mac_process.c (xnu-11215.1.10) 209 mac_cred_label_associate_fork(kauth_cred_t cred, proc_t proc); 323 mac_proc_check_debug(...); // 谁能调试谁 // 加上 mac_cred_label_associate_kernel/user、mac_proc_check_visible 等全套钩子
WebContent 进程"无网络 socket、无任意文件写、IPC 白名单"的沙箱画像,就是用户态沙箱 profile 编译成 MACF 规则后在全部内核策略点上过滤的结果。
// bsd/kern/kern_memorystatus_notify.c (xnu-11215.1.10) 150 vm_pressure_level_t memorystatus_vm_pressure_level = kVMPressureNormal; 129 static struct knote *vm_pressure_select_optimal_candidate_to_notify(...); 228 if (level == kVMPressureWarning || kVMPressureUrgent) { notify... } 232 else if (level == kVMPressureCritical) { ... } 443 uint64_t memorystatus_kill_on_sustained_pressure_window_s = 60*10; // 10 分钟窗口 453 static void sustained_pressure_handler(void*, void*); // iOS16+ 引入
内核按 Normal→Warning→Urgent→Critical 分级,经 knote 通知到"最值得通知的进程"(前台优先)。WebContent 收到 pressure 事件后清 JS GC、丢内存缓存、释放解码位图——这是它超限前的自救窗口。持续高压 10 分钟仍不缓解,iOS 16+ 会直接触发 sustained-memory-pressure 击杀(六版本实测:iOS 12/13/14 无此逻辑,16 起出现)。
iOS 16(xnu-8792.61.2)起,freezer 代码里出现了对渲染进程的按进程名硬编码统计——内核为 WebContent 单独建了一本"冻结台账":
// kern_memorystatus_freeze.c (xnu-11215.1.10) // 冻结时: 628 if (strcmp(p->p_name, "com.apple.WebKit.WebContent") == 0) { 629 memorystatus_frozen_count_webcontent++; 630 os_atomic_inc(&memorystatus_freezer_stats.mfs_processes_frozen_webcontent, ...); } // 同样逻辑在 memorystatus_freeze_process() 内再出现一次:L1903-1905 // kern_memorystatus.c (xnu-11215.1.10) // 进程移除时核销: 2715 if (strcmp(p->p_name, "com.apple.WebKit.WebContent") == 0) { 2716 assert(memorystatus_frozen_count_webcontent > 0); 2717 memorystatus_frozen_count_webcontent--; } // memorystatus_on_resume() 里解冻记账: 3217 if (strcmp(p->p_name, "com.apple.WebKit.WebContent") == 0) { 3218 os_atomic_inc(&memorystatus_freezer_stats.mfs_processes_thawed_webcontent, ...); }
并用专属 sysctl 暴露 WebContent 解冻率:kern_memorystatus_freeze.c L366-380(get_thaw_percentage_webcontent / kern.memorystatus_freezer_thaw_percentage_webcontent)。
为什么值得单列?这是"内核反过来给某个用户态引擎开小灶"的直接证据:iOS 设备上后台标签页的 WebContent 进程既多又大,Freezer 需要单独评估"冻 WebContent 划不划算"(解冻率太高说明反复冻/解冻浪费带宽),进而影响冻结名额分配策略。鸿蒙侧没有任何等价的内核特判——ArkWeb 的内存治理完全在框架层(且官方明示内核不设申请上限),这是两平台最深刻的设计哲学差异之一。
| iOS | xnu tag | 与 Web 渲染进程相关的变化 |
|---|---|---|
| 12 | xnu-4903.270.47 | freezer 代码还内联在 kern_memorystatus.c(freeze 文件不存在);对 WebContent 无任何特判(仅 L2942 注释提到 WebContent 用 inactive limit) |
| 13 | xnu-6153.141.1 | freeze 拆分成独立文件 |
| 14.8 | xnu-7195.141.2 | 仍然无 WebContent 特判(L2626 注释同 iOS12) |
| 16 | xnu-8792.61.2 | 转折点:① freezer 出现 com.apple.WebKit.WebContent 硬编码统计(冻结台账);② 新增 sustained-memory-pressure 击杀(10 分钟持续高压);③ swap 相关引用大增(48→55→101,对应桌面级 swap 能力) |
| 17 | xnu-10063.141.1 | 延续 iOS16 机制(WebContent 特判 20 处、sustained kill 3 处) |
| 18 | xnu-11215.1.10 | 延续并微调;ledger 新增 media/graphics/neural 分类记账与 frozen_to_swap 条目;CONFIG_DEFERRED_RECLAIM 给超限进程同步回收自救的机会 |
鸿蒙侧并行演化:HarmonyOS 4.0(Chromium M99)→ 4.1-5.1(M114)→ 6.0(M132 默认)→ 7.0(M144 默认)。同一时期把渲染进程模式 API 化(setRenderProcessMode / sharedRenderProcessToken / onRenderProcessNotResponding 均为 API 12 引入),并新增 M114→M132 差异适配指南。
给开发者的实用结论:在 iOS 上做混合开发,WKWebView 的崩溃与内存问题要盯 WebContent 进程的 phys_footprint(可在 Xcode 的 Memory Report 里看分进程曲线),系统会在超限时杀进程救全局;在鸿蒙上则要主动用 setRenderProcessMode(MULTIPLE) 换隔离性、用 onRenderExited/onRenderProcessNotResponding 兜住共享进程的连坐风险——两个平台对"渲染进程"的治理哲学完全不同,移植方案不能照抄。
| 来源 | 用途 |
|---|---|
| apple-oss-distributions/xnu @ xnu-11215.1.10(GitHub) | osfmk/kern/task.c、osfmk/vm/vm_map.c、bsd/kern/kern_memorystatus(_freeze/_notify).c、bsd/vm/vm_compressor_backing_file.c、security/mac_process.c、bsd/sys/mman.h 全文抓取归档 |
| xnu 六版本存档(iOS 12/13/14/16/17/18) | 跨版本对比:WebContent 特判出现于 iOS16;sustained-pressure kill 出现于 iOS16;swap 引用 48→50→55→101→102→102 |
| WebKit 官方文档(docs.webkit.ac.cn 中文镜像:WebKit 简介 / Site Isolation) | 组件构成、JSC 四层 JIT、WebKit2 多进程、站点隔离机制(RemoteFrame/BrowsingContextGroup/FrameProcess/WebProcessCache) |
| Apple Developer Documentation — WKWebView / WKContentView | WKWebView 定位、UIWebView 废弃关系、导航与 UI 代理 |
| 华为开发者文档 — ArkWeb进程 / Web组件渲染模式 / ArkWeb简介 / 使用离线Web组件 / ArkWeb术语 | ArkWeb 五进程模型、渲染进程模式 API、渲染布局两模式、Chromium 版本对应表、离线组件内存数据 |
| OpenHarmony web_webview 仓库 README | "nweb 基于 Chromium + CEF 构建"的官方表述 |
本页所有 XNU 行号引用格式为 文件 L行号,对应归档文件 xnu-render-analysis/xnu-11215.1.10/(task.c = osfmk/kern/task.c 10396 行、vm_map.c = osfmk/vm/vm_map.c 24141 行 等),可逐条 grep 复核。文档类论断以各官方文档原文为准。