☰
1. 为什么需要 blob:从 conventional resource 的困局说起
2026/9/26 16:29:13 网站建设 项目流程

本文是《穿透虚拟化图形栈:virtio-gpu blob 机制全景解析》专栏的开篇。整个专栏将从 guest 使用 BO 的视角出发,系统梳理现代 virtio-gpu 性能的基石——blob 协议的机制与实现。仅从省去拷贝这一点来看,blob 的重要性无论怎样强调都不为过。

让我们先回答一个根本的问题:
在没有 blob 之前,虚拟化图形栈是怎么利用一块显存的?它为什么不够用了?


1、故事的起点:conventional resource

virtio-gpu 最初的资源模型,我们今天称之为conventional resource(传统资源)。它的命令是VIRTIO_GPU_CMD_RESOURCE_CREATE_2D和_CREATE_3D。

它的世界观非常“图形化”,可以浓缩成三条:

  1. 资源必须是有类型的(typed)。创建一个资源时,你必须告诉设备它的宽、高、像素格式(VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM之类)。资源天生就是一张“纹理”或一个“渲染目标”。

  2. guest 存储与 host 存储是两份,靠命令显式搬运。guest 侧有一份内存(用来 CPU 访问),host 侧 GPU 有另一份。两者之间的数据同步,靠:

    • RESOURCE_ATTACH_BACKING:把 guest 页挂到资源上;
    • TRANSFER_TO_HOST_2D/TRANSFER_FROM_HOST_3D:显式地把字节从一侧拷到另一侧。
  3. 一切以“提交—拷贝—呈现”为节奏。guest 改了内存,就要TRANSFER_TO_HOST;host 渲染完了要给 guest 看,就要TRANSFER_FROM_HOST。

在 OpenGL 固定管线、2D 桌面合成的年代,这套模型工作得很好。它简单、自洽、语义清晰。

我们用一张图看清它的“拷贝节奏”:

Host GPUGuest 内核Guest 应用Host GPUGuest 内核Guest 应用写入纹理数据 (guest 内存)RESOURCE_ATTACH_BACKING (挂 guest 页)TRANSFER_TO_HOST_2D (拷贝!)GPU 渲染TRANSFER_FROM_HOST_3D (拷回!)读取结果

注意那两个拷贝!。它们是这套模型的心跳,也是它的原罪。


2、困局:现代 API 让这套模型处处漏风

时代变了。当我们要在虚拟机里跑Vulkan、OpenCL、compute、乃至 GPU 通用计算(如 ROCm)时,conventional resource 的三条世界观逐条崩塌。

2.1 现代内存根本“没有类型”

Vulkan 的VkDeviceMemory、OpenCL 的 buffer、compute 的 SSBO,本质上都是一段无结构的字节(untyped memory)。它们没有宽高格式,可能这一刻当顶点缓冲,下一刻当 storage buffer。

强行套CREATE_3D的“宽×高×格式”模型,等于要求你给一段裸内存编造一个假的图像格式。这既别扭又会丢失真实的分配语义(比如对齐、内存类型索引)。

规范的回应:需要一种“只有大小、没有类型”的资源。

2.2 拷贝正在谋杀性能

TRANSFER_TO/FROM_HOST的每一次调用,都是一次真实的内存拷贝(外加命令往返)。对一张 4K 纹理是几十 MB,对一块 compute 数据集可能是几个 GB。

现代 GPU 工作负载的特征是高频、大块、低延迟。在每帧、甚至每次 dispatch 都插入一次全量拷贝,是不可接受的。我们真正想要的是:

guest CPU 写进去的地址,就是host GPU 读出来的地址——零拷贝。

2.3 host-visible coherent 内存无处安放

Vulkan 有一类关键内存:HOST_VISIBLE | HOST_COHERENT。应用vkMapMemory拿到一个指针,直接写,GPU 立即可见,不需要任何显式 flush 或 transfer。

这在物理机上靠的是 CPU 和 GPU 共享同一段物理内存 + 缓存一致性协议。可在虚拟机里,guest 应用 map 到的是 guest 地址,host GPU 用的是 host 地址——conventional 模型下这两者被拷贝隔开,“coherent”这个语义根本无法实现。

我们需要让 host 分配的内存,直接出现在 guest 的地址空间里。而这,正是后面要讲的 QEMUMemoryRegion+ KVM 二级页表要解决的事。

2.4 跨设备 / 跨进程共享无从谈起

现代合成器要把 GPU 渲染的 buffer 交给 display 控制器、视频编码器,甚至另一个进程/容器。物理世界靠dma-buf这个内核对象在设备与进程间共享同一块内存。

conventional resource 是 virtio-gpu 设备的私有概念,没有一个可以拿出去、被别的子系统认识的“内核句柄”。跨设备(cross-device)、跨进程共享这条路根本不存在。


3、blob:一次“把资源和类型解耦”的范式转移

面对这四条困局,virtio-gpu 规范引入了blob resource(命令:VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB)。

它的核心思想只有一句话,但分量极重:

资源不再是“一张有类型的图像”,而是“一段字节存储”。存储从哪来、能不能映射、能不能共享,由几个正交的维度独立描述。

对照四条困局,blob 逐一作答:

conventional 的困局blob 的回应
内存必须有类型blob 只有size,untyped,天然匹配 Vulkan/compute
拷贝谋杀性能存储可以是 host 分配后直接映射进 guest,零拷贝
host-visible coherent 无处安放MAPPABLE+ host-visible 内存区,让 host 内存出现在 guest BAR
跨设备/进程共享CROSS_DEVICE+ 导出为dma-buf内核对象

而“存储从哪来、可不可映射、可不可共享”这几个正交维度,正是 blob 三个关键输入参数——blob_mem、blob_flags、blob_id——所要表达的。它们构成了理解整个 blob 机制的语义骨架。

我们先给一个“剧透版”的对照,让你对后面的旅程有个预期:

blob 的三维输入 · 规范层

blob_mem
存储在哪:
GUEST/HOST3D/both

blob_flags
可见性:
MAPPABLE/SHAREABLE/CROSS_DEVICE

blob_id
引用哪块
已有 host 内存

后端据此选择存储形态

dma-buf fd

opaque fd + UUID

GEM handle

虚拟地址 / SVM

看到右边那四种“存储形态”了吗?它们最终会被后端填进一个叫virgl_context_blob的结构体里——那是本专栏的主角之一。但现在,请先把它放一放。


4、本篇小结与下一站

这一篇,我们回答了“为什么”:

  • conventional resource 是一套“有类型 + 显式拷贝”的模型,在 2D/固定管线时代够用;
  • 现代 API(Vulkan、compute、ROCm)带来了untyped 内存、零拷贝、host-visible coherent、跨设备共享四条硬需求,逐条击穿了旧模型;
  • blob resource 的范式转移是把“资源”和“类型”解耦,用blob_mem × blob_flags × blob_id三个正交维度重新描述存储;
  • 这套解耦的“账单”,最终要由QEMU 的地址空间注入和KVM 的二级页表来结算——这是旧模型完全不需要触碰的深水区。

作为引子,先把这张贯穿整个专栏的地图放在这里。你不需要现在就看懂它——后面每一篇,我都会告诉你“你现在在这里”:

Guest 虚拟机

virtqueue 命令

virgl_renderer API

get_blob

KVM_SET_USER_MEMORY_REGION

应用 / Mesa 驱动
virgl · venus

Guest 内核
drivers/gpu/drm/virtio

VMM · QEMU / crosvm

virglrenderer 核心

后端
vrend · venus · drm · rocm

Hypervisor · KVM
EPT / NPT / stage-2

blob 机制的“全部意义”,就是让最上面那块 guest 里的内存诉求,能够以零拷贝、可映射、可共享的方式,一路打通到最下面 hypervisor 的页表。而在 blob 出现之前,这条路是断的——中间必须靠“拷贝”来接力。


导航

  • 下一篇:第 2 篇 整栈全景图:一次 blob 分配要穿过多少层

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询