浏览器标签中的类Linux内核 - 深入解析BrowserPod架构

Lobsters Hottest 产品

摘要

深入解析BrowserPod架构,这是一个基于WebAssembly内核的浏览器内沙箱,完全在客户端运行兼容Linux的应用程序。本文涵盖内核设计、磁盘和网络子系统,以及其在浏览器中运行诸如Claude Code等工具的能力。

<p><a href="https://lobste.rs/s/s3wqhi/linux_like_kernel_browser_tab_deep_dive">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/05/18 16:32

# 在浏览器标签页中运行类 Linux 内核 —— BrowserPod 架构深度解析 来源:https://labs.leaningtech.com/blog/browserpod-deep-dive 几个月前我们发布了 [BrowserPod](https://browserpod.io/):一个基于 WebAssembly 的浏览器内沙箱,完全在客户端运行。BrowserPod 底层提供了一个**原生的 WebAssembly 内核**,从头设计以在浏览器中运行,同时兼容许多原本为 Linux 设计的应用程序。这个 WebAssembly 内核是 BrowserPod 能够在浏览器中完整运行真实全栈应用的关键特性。本文将深入探讨我们解决方案的**架构**、**能力**和**局限性**:首先介绍现代操作系统的核心功能及其在 BrowserPod 设计中的体现,然后深入讲解磁盘和网络子系统的实现,最后讨论当前的局限性和未来计划。 BrowserCode / Claude Code [BrowserCode](https://browsercode.io/) 在 BrowserPod 之上于浏览器中运行 Claude Code 随着 [BrowserPod 2.0](https://labs.leaningtech.com/blog/browserpod-20) 的发布,该工具现已足够稳定,可以在浏览器中运行 *Claude Code* 及其他代理型 CLI。欢迎查看 [BrowserCode](http://browsercode.io/) 快速了解今天能用 BrowserPod 构建什么。 ## 内核到底做什么? BrowserPod 深受传统原生硬件内核的启发,我们发现 WebAssembly 执行模型与操作系统教科书中常见的原则之间存在许多直接对应关系。低级编程概念常常被误解,尤其是那些只接触过抽象编码形式的开发人员。因此,在深入细节之前,我们有必要大致勾勒一下操作系统内核的主要功能: - **访问和抽象硬件**:内核包含专用代码,通常称为*设备驱动程序*,能够与设备的各种硬件组件进行低级协议通信。内核还向应用程序暴露硬件的抽象接口,使得多个竞争厂商和硬件设计成为可能,而无需每个程序都包含设备相关的逻辑。 - **协调硬件访问**:内核决定哪个应用程序在何时可以访问硬件,如果允许并发访问,则解决冲突。例如,考虑磁盘驱动程序:硬件一次只能并发读取/写入一个(或几个)数据块。任何其他请求必须放入队列并逐步处理。内核决定请求的处理顺序,确保没有单个应用程序可以独占所有资源。内核还可以优化访问模式,例如利用*局部性*。 - **应用隔离**:现代操作系统的一个基本能力是保持应用程序的隔离。这通常通过 CPU 的低级功能“虚拟内存”来实现。其思想是每个应用程序(更准确地说,每个*进程*)只能访问物理内存的一个子集,并且完全无法访问其他*进程*中的地址。用操作系统术语来说,每个进程拥有自己独立的*地址空间*。这个特性的一个有用副作用是,一个进程的崩溃不会影响系统中的其他进程(至少在理想情况下)。 - **并发执行**:如果硬件提供多个 CPU,内核也可以同时运行多个进程。实际上,这意味着每个 CPU 会被配置为访问不同的地址空间,多个指令流将独立并行执行。一个有趣的特殊情况是*多线程*,其中多个 CPU 被配置为访问*相同*的地址空间,但保持独立的执行流。 ## 一个真正的内核,但在浏览器标签页中 BrowserPod 架构图 BrowserPod 架构示意图,展示了主要组件。 我们对 BrowserPod 内核的目标是超越当前 WebAssembly 作为编译目标的现有水平。使用现有工具链编译一个单独的(可能多线程的)程序并在浏览器中运行相对容易。我们认为,缺失的是运行完整*软件系统*的解决方案:一组程序协同工作并交互,以提供完整的用户体验。这是一个实际例子:一个简单的 `npm install && npm run dev` 命令实际上代表了多个进程的复杂编排,包括 shell、node.js 实例、安装脚本等。所有这些步骤需要在一个连贯且持久的文件和其他系统资源视图上工作,协同而不互相干扰。 当我们开始规划一个可行的架构来解决这个问题时,我们注意到必须有效地构建一个*内核*,并发现了我们的设计与原生内核之间的许多相似之处。每个*程序*可以由一个单独的 WebAssembly 二进制文件表示,概念上我们可以谈论 `bash.wasm` 或 `blender.wasm`,但为了与 Linux 惯例一致,我们不对可执行文件应用任何文件扩展名。*并发性*由 Workers 提供。系统中每个进程或线程都会生成一个 Worker。在单线程应用程序的情况下,程序的 WebAssembly 模块将在一个独立的 Worker 中运行,拥有自己的 WebAssembly 内存。多线程进程将使用多个 Worker,所有 Worker 共享同一内存。 所有应用程序通过 BrowserPod WebAssembly 内核连接在一起,内核提供标准命名的系统调用,例如 `__syscall_readv`。WebAssembly 应用程序可以通过将这些系统调用*导入*到 WebAssembly 实例中来使用它们。BrowserPod 内核本身是一个 WebAssembly 模块,在每个 Worker 中可用。所有应用程序并发访问内核,提供统一且连贯的单一系统视图。 - **硬件访问和协调**:这里的“硬件”当然是虚拟的,但工作方式与原生情况类似。应用程序未经修改,期望使用系统资源的抽象接口:文件、套接字或诸如 `/dev/tty` 的设备。BrowserPod 内核提供与 Linux API 兼容的系统调用接口,并确保所有应用程序有序地访问共享状态,例如文件系统和底层虚拟块设备。 - **应用隔离**:此功能由 WebAssembly 执行模型*自动*提供。WebAssembly 模块只能访问它创建或导入的内存对象。在 BrowserPod 架构中,所有 WebAssembly 进程使用导入的内存,这些内存在启动时提供。同一应用程序(例如 bash)的每个实例都会收到一个全新的内存对象,并且只能通过 BrowserPod 内核与其他进程交互。当启动新*线程*时,它将使用现有的内存对象,从而允许对同一地址空间的并发访问。 - **并发性**:如上所述通过 Workers 提供。需要注意的是,这里的模型假设有无限数量的虚拟 CPU。适当的调度委托给用户设备的底层操作系统。未来,还可以基于 WebAssembly 的 *Promise Integration* 扩展,通过在单个 Worker 之上复用多个 WebAssembly 线程来实现不同的模型。 BrowserPod 的磁盘和网络后端对于兼容广泛的应用特别重要,它们的架构值得更深入地讨论。 ## 适用于任何设备的大型磁盘 BrowserPod 磁盘架构 磁盘和文件系统架构。 BrowserPod 旨在成为一个通用的、基于 WebAssembly 的浏览器内沙箱。因此,它不仅仅是运行单个程序,还必须允许用户根据其工作负载执行多样化的应用程序。例如,我们在默认磁盘映像中包含 git 及其所有工具,这对许多用户有用,但我们不希望*每个*用户都无条件下载所有这些额外的代码。现实世界中的代码在正常操作中也可能访问*数千*个小型文件。基于 npm 的应用程序是这方面最严重的违规者之一。将文件存储为普通的 HTTP 资源是可能的,但在此规模下由于每个请求的开销变得不切实际。 [BrowserPod 文件系统](https://browserpod.io/docs/understanding-browserpod/filesystem) 的实现经过精心设计,以平衡这些方面。以下是关键思想: - **文件系统**:与 POSIX 兼容,实际上是 Ext2 实现。 - **基于块的流式传输**:磁盘映像不会在启动时下载,而是逐步按需下载。 - **多后端**:磁盘映像可以存储在任何 HTTP 服务器上,使用标准 HTTP 字节范围功能访问块。为了获得最大性能,我们的根映像使用一个 WebSocket 服务(托管在 Cloudflare 上)。 - **仅本地存储**:下载的块将使用 *Origin Private File System (OPFS)* API 或 *IndexedDB* 在本地缓存。对块的任何更改将仅保存在本地,为 BrowserPod 沙箱提供保护隐私的持久性。 Ext2 可能被视为传统选择,但实现简单且在大规模下表现良好。我们计划随着时间的推移添加功能,以达到 Ext3 并最终达到 Ext4(即现代)的功能集。 ## 再次解决网络问题 BrowserPod 网络架构 入站和出站流量的网络架构。 虽然浏览器在直觉上可能是一个原生网络平台,但事实并非如此。浏览器中没有创建原始 TCP/UDP 套接字的浏览器 API,[Direct Socket API](https://developer.chrome.com/docs/iwa/direct-sockets) 仅在*隔离 Web 应用*中可用。BrowserPod 在许多方面是 [WebVM](https://webvm.io/) 的直接后裔——我们的 x86 Debian 虚拟机在浏览器中运行。在 WebVM 的背景下,我们的解决方案是与 [Tailscale 集成](https://labs.leaningtech.com/blog/webvm-virtual-machine-with-networking-via-tailscale)。这可行,但不是一个开箱即用的解决方案,需要用户在其设备上安装 Tailscale 客户端。对于 BrowserPod,我们希望更进一步,让套接字开箱即用。不仅如此,我们还希望让互联网上的任何地方都可以零设置访问沙箱内的服务器。 为了解释,我们从后者特性开始。 ### 门户 BrowserPod 引入了 [Portals](https://browserpod.io/docs/understanding-browserpod/portals),即自动生成的随机域名,用于将 HTTP 流量路由到沙箱内的服务。门户 URL 如下所示: `https://rbmhk1uf4vrqwns4mizrouzchdbljdohkssfp0mktzrijk4algta-4000.browserportal.io/path/index.html` 典型门户 URL 示例,突出显示**门户 ID**、**内部端口**和**请求路径**。 我们采用 Cloudflare Workers 来支持入站流量,因此很自然地也使用它们作为出站流量的代理,实现相对直接。这个解决方案效果很好,但引入了一些棘手的问题来防止用户滥用我们的平台:向整个互联网开放代理从来都不是一个好主意。为了让 BrowserPod “即开即用”并防止滥用,我们达成了以下折衷: - **免费层的有限白名单域名**:任何人都可以通过几次点击使用 BrowserPod,并享受慷慨的免费层,但免费用户只能访问少数选定的白名单域名。列表预计会随时间变化,但核心内容包括对 npm 包注册表和主要 git 托管平台(如 GitHub)的访问。 - **付费用户的定制白名单域名**:在免费层之上,用户可以将其自定义域名添加到白名单,也可以使用“无限”配置。我们认为这是在浏览器平台限制下目前能负责任地提供的最佳方案。当然,我们会继续关注可能出现的替代方案或 Direct Sockets API 的进一步标准化。该领域的网络问题尚未完全解决,但我们认为每次迭代都越来越接近正确的解决方案。 ## Wasm 二进制文件,但适用于 Linux? 我们已经解释了 BrowserPod 二进制文件是 WebAssembly 模块,并使用与 Linux 兼容的系统调用,但尚未描述它们是如何生成的。为此,我们扩展了 [Cheerp](https://cheerp.io/):我们现有的 C/C++ 到 WebAssembly 和 JavaScript 编译器。我们引入了一个新的目标:`wasm32-browserpod-linux`,指示编译器遵循正确的约定。其思想是,这个新的 Cheerp 目标可以用作构建包时原生编译器的直接替代品。目标命名遵循交叉编译器的既定模式,大多数构建系统无需自定义补丁即可识别。 包使用 Nix 构建,并且我们在不断扩展 BrowserPod 版本中默认包含的内容。如何使用 Nix 本身就是一个有趣的话题,我们未来可能会发表一篇详细的博客文章。如果你听说过 [WASI](https://wasi.dev/),你可能会好奇为什么我们没有采用它。虽然名义上是标准,但 WASI 在设计上既过于复杂,在范围上又过于有限,无法作为 Linux API 的替代品。已经有尝试通过 [WASIX](https://wasix.org/) 扩展功能集,但我们认为直接采用 Linux 系统调用更简单、更有效。 Cheerp 是开源软件,对 `wasm32-browserpod-linux` 目标的支持可在公开的[夜间构建版本](https://launchpad.net/~leaningtech-dev/+archive/ubuntu/cheerp-nightly-ppa)中找到。如果你好奇其实际效果,请继续阅读下面的逐步教程。 ## 那么,我可以用这个做什么? BrowserPod 的第一个版本于 2026 年 2 月发布,但该平台已经非常强大。截至目前,你可以在沙箱中运行 node、python、bash、git 以及许多其他 Linux 命令行工具。我们准备了一些演示,展示 BrowserPod 的能力如何应用于几个具体场景,但潜在用例远比我们在这里展示的广泛得多。 ### 在浏览器中学习 Python 借助 BrowserPod,可以创建在浏览器中学习编程语言的平台,无需任何云端计算资源。作为这个想法的一个示例,以下演示将对用户输入执行一些简单操作。当用户停止循环时,Python 可执行文件将作为 REPL 重新启动,以证明 BrowserPod 可以运行同一应用程序的多个实例。 ```python import sys import subprocess while True: name = input("\nWhat's your name? ") words = name.split() print("\nHello,", name) print("Uppercase:", name.upper()) print("Reversed:", name[::-1]) print("Word count:", len(words)) initials = "".join(word[0].upper() for word in words) print("Initials:", initials) again = input("\nRun again? (y/n) ").lower().strip() if again != "y": print("\nLaunching python REPL...") break subprocess.run([sys.executable]) ``` 现在,还有其他选项可以在浏览器中运行 Python,但 BrowserPod 可以做得更多。 ### 从 Web 应用探索 git 仓库 BrowserPod 自带 git,并且可以从沙箱访问 GitHub 等主要托管平台。以下演示展示了如何使用 BrowserPod 在浏览器中运行 git 工作流。一个 shell 脚本...

相似文章

使用 Bubblewrap 在 Linux 上轻松实现沙箱

Lobsters Hottest

一篇博客文章,介绍了一种在 Linux 上使用 Bubblewrap 的轻量级沙箱方法,其中名为 'box' 的脚本以只读方式共享主机文件系统,并以可读写方式共享当前目录,从而无需单独发行版即可在隔离环境中运行主机二进制文件。

我将 Kubernetes 移植到了浏览器中

Hacker News Top

来自 ngrok 的 Sam Rose 演示了如何通过 WebAssembly 将 Kubernetes 移植到浏览器中完全运行,让开发者能够模拟本地集群,而无需远程服务器或复杂设置。

完全在浏览器中的容器构建

Lobsters Hottest

一个完全在浏览器中使用客户端代码构建容器的Web应用程序,展示了自定义容器工具的强大功能。用户可以选择基础镜像、运行Shell脚本,并将生成的镜像导出为tar文件。

用 x86_64 汇编写成的 Linux 桌面

Lobsters Hottest

一位开发者借助 Claude Code,用纯 x86_64 汇编重建了完整的 Linux 桌面栈——从 shell、终端、窗口管理器到各种工具,实现微秒级启动,并延长数小时续航。