- 虚拟化
- JIT编译
- 硬件仿真
【免费下载链接】v86
x86 PC emulator and x86-to-wasm JIT, running in the browser
本指南围绕仓库 tools/docker/debian/Readme.md 及其配套 Dockerfile,完整讲解如何在 v86(浏览器中的 x86 PC 模拟器 + x86-to-WASM JIT)里运行一个带 XFCE 图形桌面的 Debian 系统。读完本文,你将掌握两条可落地的磁盘方案(分片裸磁盘 Raw Disk 与 9p 文件系统)的构建、状态快照生成、前端页面参数配置与网站托管流程,并能在浏览器中通过网络访问完整的 Debian 桌面环境。
方案总览:一条 Dockerfile 产出两种启动介质
tools/docker/debian目录的核心思路是:先用 Docker 构建一个带 XFCE 桌面、systemd、串口自动登录与 9p 启动支持的 Debian 32 位系统,再把该系统导出为两类前端可用的介质:
| 方案 | 构建镜像 | 构建状态 | 示例前端 | 磁盘后端 |
|---|---|---|---|---|
| Split Raw Disk | bash build-raw-disk.sh | ./build-state-raw-disk.js | examples/debian-raw-disk.html | 分片 zstd 压缩的裸磁盘镜像,按需异步加载 |
| 9p 文件系统 | bash build-9p.sh | ./build-state-9p.js | examples/debian-9p.html | 通过 virtio-9p 暴露给 guest 的宿主文件目录 |
随后统一进入“网站搭建”阶段:把构建产物v86.wasm与libv86.js放入build目录,在仓库根目录启动静态服务器,即可在浏览器中打开上述两个示例页面。
需要说明的是:Readme 中提到的build-raw-disk.sh、build-9p.sh、build-state-raw-disk.js、build-state-9p.js属于该镜像构建工作流的外部脚本(源自上游 sandbox-bio 分支),当前仓库中未收录这些脚本文件,仅保留了 Dockerfile 与配套工具。因此下文对这几步脚本职责的描述,是结合 Readme 步骤说明与仓库内现有工具(如 split-image.py、fs2json.py、copy-to-sha256.py)推断得出的。
Dockerfile 深度解读:一个为 v86 量身定制的 Debian XFCE 系统
tools/docker/debian/Dockerfile 是整套方案的基石,其设计几乎每一层都在服务于 v86 的模拟环境特性(32 位 x86、串口终端、虚拟显卡、virtio 磁盘)。
基础镜像、内核与 systemd
FROM i386/debian:bookworm-slim WORKDIR /root/build ENV DEBIAN_FRONTEND=noninteractive- 基础镜像为
i386/debian:bookworm-slim:32 位 x86 架构,与 v86 模拟的 x86 处理器完全对齐,避免在浏览器中运行 64 位内核带来的性能与兼容性负担; DEBIAN_FRONTEND=noninteractive保证 apt 安装全程无人值守;- 随后安装
linux-image-686(32 位内核)、systemd-sysv(使 systemd 成为 PID 1)、locales与libterm-readline-perl-perl。
locale 配置被显式固定为en_US.UTF-8,并把 root 的 shell 切换为 bash:
RUN echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen && \ locale-gen && \ echo 'LANG="en_US.UTF-8"' > /etc/default/locale && \ chsh -s /bin/bash root桌面环境与应用集
第二个 apt 批次组装了完整桌面栈:
- 显示服务:
xserver-xorg-core、xserver-xorg-video-fbdev(v86 的虚拟显卡走 fbdev 驱动)、xinit; - 桌面:
xfce4、xfce4-terminal、desktop-base、thunar(文件管理器)及其归档插件; - 输入:
xserver-xorg-input-mouse、xserver-xorg-input-kbd、xserver-xorg-input-evdev; - 图形依赖库与字体:
libxext6、libxrender1、libxtst6、libxi6、libx11-xcb1、libfontconfig1、fonts-dejavu-core、fonts-wqy-microhei、fonts-symbola、fonts-liberation; - 网络/系统工具:
isc-dhcp-client、iproute2、iputils-ping、sudo、dbus-x11、xdg-utils、shared-mime-info、gvfs与gvfs-backends; - 开发与常用软件:
cmake、git、tmux、mc、htop、vim、nano、p7zip-full、curl、build-essential,以及firefox-esr、libreoffice、thunderbird、gimp、ristretto(图片查看)、atril(PDF 阅读)、engrampa(归档管理器)、geany、mousepad、pavucontrol、xfce4-screenshooter、xfce4-notifyd、xfce4-taskmanager等。
用户与免密体系
RUN passwd -d root && \ sed -i 's/nullok_secure/nullok/' /etc/pam.d/common-auth && \ useradd -m -s /bin/bash username && \ passwd -d username && \ usermod -aG sudo,video,tty,input,dialout username && \ echo "username ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers- root 与标准用户
username的密码均被清空(passwd -d),配合 PAM 的nullok允许空密码登录; username被加入sudo、video、tty、input、dialout组,并配置NOPASSWD 免密 sudo——这是为了让 guest 内sudo dhclient、sudo hostname等命令无需交互即可执行。
串口自动登录:v86 文本终端的入口
v86 在图形界面之外通常会把串口输出渲染到页面上的文本终端(如示例页面screen_container中white-space: pre的文本层)。因此 Dockerfile 为 tty1 与 ttyS0 都配置了 systemd getty 覆盖:
RUN mkdir -p /etc/systemd/system/getty@tty1.service.d /etc/systemd/system/serial-getty@ttyS0.service.d && \ printf '%s\n' \ '[Service]' \ 'TTYVTDisallocate=no' \ > /etc/systemd/system/getty@tty1.service.d/getty-noclear.conf && \ printf '%s\n' \ '[Service]' \ 'ExecStart=' \ 'ExecStart=-/sbin/agetty --autologin username --noissue --noclear %I 38400 $TERM' \ > /etc/systemd/system/getty@tty1.service.d/getty-override.conf && \ printf '%s\n' \ '[Service]' \ 'ExecStart=' \ 'ExecStart=-/sbin/agetty --autologin username --noissue --noclear -s %I 115200,38400,9600 vt102' \ > /etc/systemd/system/serial-getty@ttyS0.service.d/getty-autologin-serial.conf- tty1 使用 38400 波特率自动登录
username,并关闭清屏(--noclear)以便文本终端内容在 v86 页面上持续可见; - 串口 ttyS0 的自动登录是 v86 场景的核心:
-s让 agetty 自动探测波特率(依次尝试 115200、38400、9600),终端类型固定为vt102。这样只要通过 v86 的串口连接到 guest,即可直接获得一个登录后的 shell,无需输入用户名密码。
同时被禁用的还有会干扰镜像稳定的后台服务:
RUN systemctl disable systemd-timesyncd.service && \ systemctl disable apt-daily.timer && \ systemctl disable apt-daily-upgrade.timerX11 自动启动与 FBDEV 虚拟显卡
guest 内对普通用户username配置了自动进入桌面的逻辑:
RUN echo '(sleep 5 && sudo bash -c "printf \"\\n\\nGUI_READY\\n\" > /dev/ttyS0") & \n\ exec startxfce4' > /home/username/.xinitrc.xinitrc启动 XFCE,并在启动 5 秒后向串口 ttyS0 打印GUI_READY标记——这是给上层自动化脚本(如状态快照构建脚本)用来探测“桌面已就绪”的串口握手信号。
RUN { \ echo 'if [ -z "$DISPLAY" ]; then'; \ echo ' sudo bash -c "echo 127.0.0.1 localhost > /etc/hosts"'; \ echo ' sudo bash -c "echo localhost > /etc/hostname"'; \ echo ' sudo hostname localhost'; \ echo ' startx -- -ac'; \ echo 'fi'; \ |} >> /home/username/.bashrc登录 shell 若检测不到DISPLAY环境变量,会自动修正 hosts/hostname 并执行startx -- -ac(-ac关闭 X 访问控制,适配模拟环境)。最后强制 Xorg 使用 fbdev 驱动:
RUN mkdir -p /etc/X11/xorg.conf.d && \ echo 'Section "Device"\n\ Identifier "Card0"\n\ Driver "fbdev"\n\ EndSection' > /etc/X11/xorg.conf.d/10-fbdev.confv86 的虚拟 VGA 在 X11 层面由 fbdev 驱动支撑(这也是 Dockerfile 安装xserver-xorg-video-fbdev的原因),该配置确保 Xorg 不会尝试探测不存在的硬件加速设备。
方案一:Split Raw Disk 分片裸磁盘
Readme 给出的三步流程为:
- 运行
bash build-raw-disk.sh构建镜像; - 运行
./build-state-raw-disk.js构建状态快照; - 使用示例前端 examples/debian-raw-disk.html。
为什么要“分片”
裸磁盘镜像(典型 5 GiB)无法作为单个文件在浏览器中快速加载,因此流程会把磁盘切成固定大小的小块,配合 v86 的async按需加载机制使用。仓库中的 tools/split-image.py 就是这类切分工具:它把输入文件按指定大小切块,并支持--zstd(zstd -19 压缩)或--gzip(gzip -9 压缩)对每个 chunk 单独压缩,输出文件名形如chunk.%d-%d.zst。示例页面中hda的url指向的正是../images/debian-chunks/chunk.zst这类分片。
前端参数逐项解读
examples/debian-raw-disk.html 是分片磁盘方案的参考实现:
<script src="../build/libv86.js"></script>页面通过new V86({...})创建模拟器,关键参数如下:
wasm_path: "../build/v86.wasm":WASM JIT 引擎文件,必须与libv86.js同置于build目录;memory_size: 1636 * 1024 * 1024:guest 物理内存约 1.6 GiB,足以承载 XFCE 桌面与 Firefox/LibreOffice;vga_memory_size: 8 * 1024 * 1024:显存 8 MiB;screen_container:指向页面中的文本终端容器,负责渲染串口输出与接收键盘输入(tabindex="0"使其可聚焦,点击触发screen.focus());initial_state: { url: "../images/debian-state-base.bin.zst" }:状态快照。v86 支持把 CPU/内存/设备状态序列化为单个文件(zstd 压缩),启动时直接从快照恢复,跳过内核引导,这就是第 2 步build-state-raw-disk.js的产物;hda磁盘配置:
hda: { url: "../images/debian-chunks/chunk.zst", async: true, use_parts: true, fixed_chunk_size: 128 * 1024, size: 5 * 1024 * 1024 * 1024, },| 参数 | 值 | 含义 |
|---|---|---|
url | chunk.zst | 分片文件的基础 URL,v86 会按规则请求各 chunk |
async | true | 启用异步按需读取,只在 guest 访问相应扇区时才拉取对应分片 |
use_parts | true | 开启分片模式,磁盘由多个小文件组成而非单一镜像 |
fixed_chunk_size | 128 * 1024 | 每个分片的固定字节数(128 KiB),必须与切分工具的分片大小一致 |
size | 5 GiB | 磁盘总容量,用于虚拟磁盘寻址 |
network_relay_url:WebSocket 网络中继地址(示例配置为公共 relay 服务),guest 内执行sudo dhclient后即可获得网络;autostart: true:页面加载后自动启动模拟器。
方案二:9p 文件系统
9p 方案的流程为:
- 运行
bash build-9p.sh构建镜像; - 运行
./build-state-9p.js构建状态快照; - 使用示例前端 examples/debian-9p.html。
Dockerfile 中的 9p 启动链
与 Raw Disk 方案最大的不同在于:9p 方案的 guest 根文件系统不再来自块设备,而是通过 virtio-9p 直接挂载宿主目录。Dockerfile 为此做了完整的 initramfs 定制:
RUN printf '%s\n' 9p 9pnet 9pnet_virtio virtio virtio_ring virtio_pci | tee -a /etc/initramfs-tools/modules把 9p 与 virtio 内核模块静态纳入 initramfs。随后写入一个自定义启动脚本 [boot-9p 逻辑]:
RUN printf '%s\n' \ '#!/bin/sh' \ 'case $1 in prereqs) exit 0;; esac' \ '. /scripts/functions' \ 'if [ "${ROOT}" = "host9p" ]; then' \ ' mkdir -p ${rootmnt}' \ ' mount -n -t 9p -o trans=virtio,version=9p2000.L,cache=loose,rw host9p ${rootmnt}' \ 'else' \ ' . /scripts/local' \ 'fi' \ > /etc/initramfs-tools/scripts/boot-9p && \ chmod +x /etc/initramfs-tools/scripts/boot-9p RUN echo 'BOOT=boot-9p' | tee -a /etc/initramfs-tools/initramfs.conf RUN update-initramfs -u这段脚本实现了“一镜像双启动”的分流逻辑:
- 若内核命令行中的
root=host9p,initramfs 就会以trans=virtio,version=9p2000.L,cache=loose,rw挂载 9p 根文件系统; - 否则回退到标准本地磁盘启动(
. /scripts/local)。
挂载参数与仓库 docs/filesystem.md 中描述的 guest 挂载方式完全一致:
mount -t 9p -o trans=virtio,version=9p2000.L host9p /mnt/9p/以及对应的内核启动参数:
rw root=host9p rootfstype=9p rootflags=trans=virtio,version=9p2000.L9p2000.L是带锁语义的 9p 协议版本,cache=loose允许 guest 侧宽松缓存以换取性能,rw表示可写根文件系统。如需从 9p 根文件系统启动的内核配置要求,可参考 docs/linux-9p-image.md(CONFIG_NET_9P、CONFIG_9P_FS、CONFIG_VIRTIO_PCI等内核选项),以及mount_handler方式的 initcpio 钩子示例。
宿主侧文件系统产物的生成
v86 的 9p 后端由 src/virtio_9p.js 与 lib/9p.js 实现。JSON/HTTP 模式(即 Readme 9p 方案使用的模式)通过两个仓库工具生成产物:
- tools/fs2json.py:把目录(或 tar 包)递归扫描成
fs.json(fsroot目录树 +version+size),每个文件按 SHA-256 前 10 位哈希重命名,支持--exclude排除路径与--zstd压缩,用法示例:
./fs2json.py --exclude /boot/ --out fs.json /mnt/- tools/copy-to-sha256.py:把源文件按内容哈希复制到目标目录(文件名即短哈希,与 fs2json 中的
HASH_LENGTH = 10保持一致),并支持 zstd 压缩,形成baseurl指向的 flat 文件目录。
前端 examples/debian-9p.html 中的filesystem配置即对应上述两种产物:
filesystem: { baseurl: "../images/debian-9p-rootfs-flat/" },(Readme 的build-9p.sh阶段预期会把导出的 rootfs 交给这两个工具处理,生成fs.json与 flat 目录;build-state-9p.js则在 guest 启动并完成基础初始化后生成debian-state-base.bin.zst状态快照,与磁盘方案共用同一份initial_state配置。)
网站搭建与本地运行
Readme 给出的上线步骤同样适用于两种方案:
- 准备 v86 运行时:构建 v86 项目(或直接下载官方 release),将
v86.wasm与libv86.js放置到仓库根目录下的build目录——示例页面的<script src="../build/libv86.js">与wasm_path: "../build/v86.wasm"都指向该目录; - 启动静态服务器:在仓库根目录执行:
python3 -m http.server 8000- 在浏览器打开示例页面:
- Raw Disk 方案:
http://localhost:8000/examples/debian-raw-disk.html - 9p 文件系统方案:
http://localhost:8000/examples/debian-9p.html
务必通过 HTTP 服务器访问而非直接双击 HTML 文件,否则fetch加载 wasm/镜像/状态文件会因file://协议被浏览器拦截。两个页面的screen_container均包含文本终端层与 canvas 层:点击 canvas 会请求 Pointer Lock(用于鼠标捕获),点击文本区域则聚焦以便接收键盘输入。
网络配置:guest 内启用 DHCP
镜像默认没有为模拟网卡分配地址,需要在 XFCE 终端(或串口终端)中执行:
sudo dhclient enp0s5enp0s5是 v86 模拟的 virtio-net 网卡在 systemd 命名规则下的接口名(对应 Dockerfile 中安装的isc-dhcp-client与iproute2);- 由于镜像已配置
username的 NOPASSWD sudo,上述命令无需交互输入密码; - v86 侧的联网依赖前端页面传入的
network_relay_url(WebSocket 中继),因此请确保示例页面中的中继地址可达。
串口验证与自动化信号
整套镜像设计为可脚本化验证:
- 通过 v86 的串口终端可获得自动登录后的 shell(ttyS0 agetty autologin,vt102);
- 桌面启动 5 秒后,guest 会向 ttyS0 打印
GUI_READY,上层脚本(如状态快照构建)可据此判断 XFCE 已就绪后再落盘状态; startx -- -ac关闭 X 访问控制,配合 fbdev 驱动,使 v86 的虚拟 VGA 能以最小依赖呈现完整桌面。
这一“串口握手 + 状态快照”的组合,正是build-state-raw-disk.js/build-state-9p.js能够产出可直接恢复的debian-state-base.bin.zst的关键:先让系统在模拟器中完整启动到桌面,再把当时的内存与设备状态序列化,之后每次打开页面都能秒级进入可用桌面,无需重新引导内核。
- 虚拟化
- JIT编译
- 硬件仿真
【免费下载链接】v86
x86 PC emulator and x86-to-wasm JIT, running in the browser
相关推荐
技术深度解析:OpenArk在Windows系统安全监控中的架构实践
技术深度解析:OpenArk在Windows系统安全监控中的架构实践 OpenArk是一款面向Windows平台的开源Anti Rootkit(ARK)工具集,
虚拟化JIT编译硬件仿真抖音批量下载神器:免费开源工具轻松管理海量内容
抖音批量下载神器:免费开源工具轻松管理海量内容 你是否曾经遇到过这样的烦恼:看到抖音上精彩的视频、好听的音乐,却苦于无法批量保存?手动一个个下载不仅耗时费力,还
虚拟化JIT编译硬件仿真如何快速完成学术论文排版:厦门大学LaTeX模板完整指南
如何快速完成学术论文排版:厦门大学LaTeX模板完整指南 你是否曾因论文格式问题熬夜修改?是否在目录页码调整中消耗了大量时间?厦门大学LaTeX论文模板正是为解
虚拟化JIT编译硬件仿真
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考