---
url: /courses/virtualization-tech/03-virtualization-principles/index.md
---
# 第 3 课 虚拟化原理：虚拟机是怎么跑起来的

::: tip 项目目标
前两课你已经能用 virt-manager 创建并安装一台 Ubuntu 服务器。本课回答一个更底层的问题：虚拟机里的系统，凭什么能安全地共享宿主机的 CPU、内存和磁盘？学完本课，你能解释 CPU 虚拟化的保护环与 VM Exit/Entry、内存虚拟化的两层地址翻译、I/O 虚拟化的几种实现路径，并说清它们各自的代价与适用场景。本课会用第 2 课创建的 `svr01` 做三次只读观察，把原理落到命令证据上。
:::

先花两分钟确认你的起点。下面的自测结果会帮课程助教判断该从哪里开始讲。

{{assessment:lesson-03-precheck}}

## 3.1 从“能创建”到“懂原理”

前两课你跟着向导创建了 `svr01`：分配了 2GB 内存、25GB 磁盘、选了 NAT 网络，然后它就能跑起来。这背后藏着一个看似矛盾的问题：

::: warning 核心矛盾
`svr01` 里跑着一个完整的 Ubuntu 内核，它以为自己独占了一台电脑的 CPU、内存和磁盘。但物理机上只有一个 CPU、一块内存、一块磁盘，而且还要同时喂给宿主机和其他虚拟机。虚拟化是怎么做到“让每个系统都以为自己独占，实际却共享同一份硬件”的？
:::

这个问题的答案分成三块，正好对应计算机的三大件：

| 硬件 | 虚拟化的核心问题 | 关键技术 |
| --- | --- | --- |
| CPU | 两个内核都要最高特权，怎么共存 | 保护环、VM Exit/Entry、VT-x/AMD-V |
| 内存 | 每个系统都从地址 0 开始管理内存，怎么隔离 | 两层地址翻译、EPT |
| I/O 设备 | 多台虚拟机怎么共享一块网卡/磁盘 | 软件模拟、VirtIO、直通、SR-IOV |

下面三节逐一展开。原理讲的是业界通用机制，KVM、VMware、Hyper-V 等产品都建立在这些机制之上。

### 实训准备：先确认对象与执行位置

本课的三组观察都以 **Ubuntu 宿主机终端**为入口，并显式连接系统级 libvirt：`qemu:///system`。先在同一个宿主终端设置本课参数；这些只是当前 Shell 会话中的变量，关闭终端后需要重新设置。

::: tabs#lesson-03-vm-parameters
@tab:active 使用课程默认名称

```bash
VM_NAME='svr01'
GUEST_USER='cloudstudent'
```

@tab 使用自定义名称

把下面两个示例值换成创建虚拟机时实际使用的名称和来宾账号：

```bash
VM_NAME='ubuntu-server-01'
GUEST_USER='ubuntu-admin'
```

:::

列出系统连接中的全部虚拟机，再核对参数指向的虚拟机是否正在运行：

```bash
virsh --connect qemu:///system list --all
virsh --connect qemu:///system domstate "$VM_NAME"
virsh --connect qemu:///system domifaddr "$VM_NAME" --source lease
```

从 `domifaddr` 的输出中找到来宾的 IPv4 地址，将下面的示例地址替换为实际值：

```bash
GUEST_IP='192.168.122.100'
```

`domstate` 应显示 `running`。如果 `domifaddr` 暂时没有地址，先回到第 2 课确认来宾网络和 DHCP 租约，不要继续套用示例地址。

I/O 观察会使用 `qemu-img`。在 Ubuntu 中该命令由 `qemu-utils` 软件包提供（版本差异以实际环境为准），先检查命令是否存在：

```bash
command -v qemu-img
```

如果没有输出，在进入三组只读观察前完成一次软件准备：

```bash
sudo apt update
sudo apt install -y qemu-utils
```

::: warning 宿主与来宾不要混用
本课所有 `virsh`、`cat /sys/module/...`、`ls`、`du` 和 `qemu-img` 命令都在宿主机执行。来宾侧的 `lscpu`、`free` 会写在 SSH 命令的引号中，由 SSH 在来宾内执行，命令结束后仍停留在宿主终端。
:::

## 3.2 CPU 虚拟化：让来宾内核安全地“以为自己是老大”

### 保护环：CPU 的等级制度

CPU 把运行权限分成几个等级，称为**保护环（Protection Rings）**。x86 架构有 Ring 0 到 Ring 3 四级：

| 等级 | 名称 | 谁在用 | 能做什么 |
| --- | --- | --- | --- |
| Ring 0 | 内核态 | 操作系统内核 | 直接访问硬件、管理内存、执行所有指令 |
| Ring 1/2 | （很少用） | 部分驱动 | 历史遗留，现代系统基本不用 |
| Ring 3 | 用户态 | 普通应用程序 | 只能做日常计算，碰硬件要请内核代办 |

::: tip 城堡与通行证
把 CPU 想成一座城堡：Ring 0 是国王寝宫，只有最高通行证能进；Ring 3 是平民区。普通程序（浏览器、游戏）在 Ring 3 运行，想保存文件、访问网络，必须通过**系统调用**请求内核帮忙——CPU 会切换到 Ring 0 执行内核代码，完成后再降回 Ring 3。
:::

### 特权冲突：两个内核都要住进 Ring 0

问题来了：宿主机内核要运行在 Ring 0，来宾内核（`svr01` 里的 Ubuntu）也需要 Ring 0 才能管理自己的硬件。但物理 CPU 只有一个 Ring 0——**特权冲突**。

虚拟化的核心，就是让来宾内核能在自己的 Ring 0 中运行，同时仍受虚拟化层控制。CPU 根据执行控制拦截需要由宿主处理的操作，其他允许直接执行的指令不必退出。

### 先分清两个分类维度

“全虚拟化、半虚拟化、二进制转译、硬件辅助”经常一起出现，但它们回答的不是同一个问题：

| 分类维度 | 路线 | 回答的问题 | KVM/QEMU 中的例子 |
| --- | --- | --- | --- |
| 来宾是否需要配合 | 全虚拟化 | 来宾能否把虚拟硬件当成普通硬件，无需修改内核 | Ubuntu 不修改内核也能在 KVM 上启动 |
| 来宾是否需要配合 | 半虚拟化 | 来宾是否使用专为虚拟化设计的接口主动协作 | VirtIO 网卡、磁盘与气球驱动 |
| CPU 指令怎样执行 | 二进制转译或软件模拟 | 没有硬件虚拟化能力时，怎样翻译或模拟来宾指令 | QEMU 的 TCG 加速器 |
| CPU 指令怎样执行 | 硬件辅助虚拟化 | 怎样借助 CPU 的虚拟化扩展直接运行大部分来宾指令 | KVM 使用 VT-x 或 AMD-V |

::: tip 本课程用的是哪条路
KVM 走的是**硬件辅助虚拟化**主线：CPU 提供 VT-x（Intel）或 AMD-V（AMD）扩展，大部分来宾指令可以直接在真实处理器上运行，需要拦截的操作再退出给 KVM 处理。这正是第一章检查 `vmx`/`svm` 标志的原因。没有硬件辅助时，`type='kvm'` 的来宾不能启动；如果确实要做纯软件模拟，需要改用 QEMU 的 TCG 加速器，而不是由 KVM 自动降级。
:::

### 硬件辅助：宿主模式与来宾模式

以 Intel VT-x 为例，CPU 增加了与 Ring 0–3 正交的两种 VMX 运行模式：

* **VMX Root 模式**：宿主侧使用；KVM 内核代码运行在 Root 模式的 Ring 0。
* **VMX Non-Root 模式**：给虚拟机用，里面也有一整套 Ring 0–3，来宾内核可以在 Non-Root 的 Ring 0 上原生运行。

QEMU 仍然是宿主机的用户态进程，并不运行在 Root 模式的 Ring 0。根据虚拟机执行控制配置，当来宾执行需要拦截的操作时，CPU 触发 **VM Exit** 并先进入 KVM 内核；KVM 能处理的退出直接在内核完成，需要用户态设备模型的退出才返回 QEMU。处理完成后，再通过 **VM Entry** 返回来宾。

Intel 用 **VMCS** 保存虚拟机执行控制和状态；AMD-V/SVM 使用对应的宿主/来宾模式与 **VMCB**。下面沿用“VM Exit/Entry”作为通用教学称呼，但要记住具体结构和指令名称因厂商而异。

下面的动画演示一次 VM Exit/Entry 的完整旅程：

观察要点：

1. 只有被执行控制设置为需要拦截的操作，才会触发 VM Exit；不是每条特权指令都必然退出。
2. VM Exit 先进入 KVM 内核；只有需要用户态设备模型时，KVM 才把控制返回 QEMU。
3. 从来宾到内核、必要时再到用户态的切换都有成本，所以虚拟化设计会尽量减少不必要的退出。

### 动手观察：CPU 虚拟化

验证“vCPU 是调度出来的虚拟执行上下文，不是独占的物理核”。先在宿主机查看当前与最大 vCPU 数量，再从 XML 中核对 `<vcpu>` 定义：

```bash
virsh --connect qemu:///system vcpucount "$VM_NAME"
virsh --connect qemu:///system dumpxml "$VM_NAME" | grep -E '^[[:space:]]*<vcpu([[:space:]>])'
```

然后从宿主终端发起一次 SSH 远程命令，让 `lscpu` 在来宾内运行：

```bash
ssh "${GUEST_USER}@${GUEST_IP}" 'lscpu | grep -E "^CPU\(s\)|Model name"'
```

::: tip 预期
`vcpucount` 会区分配置值、运行值、当前值与最大值；XML 的 `<vcpu>` 行记录持久配置。来宾 `lscpu` 显示的在线 CPU 数应与当前运行配置对应，不要求每个人都固定为 2。即使数量一致，这些 vCPU 仍由宿主调度器安排到物理 CPU 上运行，不代表来宾独占同等数量的物理核。
:::

## 3.3 内存虚拟化：两层地址翻译

### 回顾：操作系统怎么管理内存

程序使用的地址是**虚拟地址**，真实内存条上的地址是**物理地址**。CPU 里的 **MMU（内存管理单元）** 负责翻译：它查询操作系统维护的**页表**，把虚拟地址映射到物理地址。每个进程都有一份独立的页表，所以进程 A 访问不到进程 B 的内存。

### 双重翻译：虚拟化带来的难题

虚拟化环境里，地址翻译变成了两层：

1. **第一层（来宾内部）**：来宾程序用虚拟地址（GVA），来宾内核把它翻译成“来宾以为的物理地址”（GPA）。
2. **第二层（Hypervisor）**：GPA 其实是假的，QEMU/KVM 必须再把它翻译成真实的机器物理地址（MPA）。

```mermaid
flowchart LR
  A["GVA 来宾虚拟地址"] -->|"第一层：来宾页表"| B["GPA 来宾物理地址（假的）"]
  B -->|"第二层：Hypervisor 翻译"| C["MPA 真实物理地址"]
```

### 影子页表：早期的软件方案

早期虚拟化常用**影子页表**：KVM 根据来宾页表维护一套可由硬件直接使用的 GVA→MPA 映射。映射已经存在时，普通内存访问可以直接命中影子页表；来宾修改页表，或者访问缺失、受保护的映射时，KVM 才可能通过 VM Exit 捕获事件并同步影子页表。维护两套页表及其一致性，是这条路线的主要开销。

### EPT/NPT：让硬件支持二维页表遍历

现代 CPU 提供 **EPT**（Intel）和 **RVI/NPT**（AMD）硬件特性，让 MMU 原生支持两级翻译：

* 来宾内核自由维护自己的页表（GVA→GPA）。
* KVM 维护第二级页表（GPA→MPA），把基址告诉 CPU。
* 普通访问在两级映射都有效时，由硬件完成 GVA→GPA→MPA 的二维页表遍历，不需要为了每次地址翻译都退出。
* 如果第二级映射缺失、权限不允许，或地址对应 MMIO，仍可能发生 EPT violation/NPT fault 并触发 VM Exit。

::: tip 为什么 EPT 是性能关键
影子页表需要软件持续维护合成映射；EPT/NPT 把正常访问的两级遍历交给硬件，显著减少同步类 VM Exit。页表项命中 TLB 时开销很低；TLB 未命中时仍要进行多级页表遍历，并不是固定“一个周期”。
:::

下面的动画把影子页表与 EPT/NPT 画成两条替代路径，并分别展示正常命中和异常退出：

观察要点：

1. 影子页表让硬件直接使用 KVM 合成的 GVA→MPA 映射，正常命中时不必每次退出。
2. 来宾页表变化或影子映射缺失时，KVM 需要介入维护一致性。
3. EPT/NPT 让硬件执行二维页表遍历；正常映射无需退出，但 EPT violation/NPT fault 仍需要处理。

### 高级内存管理

| 技术 | 作用 | 通俗理解 |
| --- | --- | --- |
| 内存超售 | 分配给所有虚拟机的内存总和可以超过物理内存 | 不是所有虚拟机同时用满，赌一个“错峰” |
| 透明页共享 | 内容相同的页面只存一份，多台虚拟机共享 | 多台 Windows 都加载同一个 dll，只留一份 |
| 内存气球 | 由宿主请求来宾归还一部分可用内存 | 气球驱动在来宾中占用页面，宿主回收对应页；来宾压力升高时可能丢弃缓存或使用交换空间 |

### 动手观察：内存虚拟化

先在宿主机确认 CPU 厂商：

```bash
grep -m1 '^vendor_id' /proc/cpuinfo
```

输出包含 `GenuineIntel` 时查看 EPT，包含 `AuthenticAMD` 时查看 NPT，只执行与你的 CPU 对应的一项：

::: tabs#lesson-03-cpu-vendor
@tab:active Intel（EPT）

```bash
cat /sys/module/kvm_intel/parameters/ept
```

@tab AMD（NPT）

```bash
cat /sys/module/kvm_amd/parameters/npt
```

:::

::: tip 预期
输出 `Y` 表示 EPT/NPT 已启用，对应本节的“硬件二维页表遍历”；输出 `N` 表示被关闭，页表遍历退回软件路径，性能明显下降。
:::

再在宿主机查看内存上限、内存气球设备与运行时统计：

```bash
virsh --connect qemu:///system dominfo "$VM_NAME"
virsh --connect qemu:///system dumpxml "$VM_NAME" | grep -E '<memballoon([[:space:]>])'
virsh --connect qemu:///system dommemstat "$VM_NAME"
```

::: tip 预期
`dominfo` 的 `Max memory` 是配置允许的内存上限；`memballoon` 表示来宾配置了内存气球设备。`dommemstat` 中的 `actual` 是当前气球值，`rss` 是宿主上该虚拟机进程的驻留集大小，两者都以 KiB 为单位。`rss` 包含的是宿主进程视角的数据，可能小于或大于 `actual`，不能把它直接当作来宾“已用内存”。
:::

最后从宿主终端发起 SSH 远程命令，在来宾内查看来宾操作系统自己的内存统计：

```bash
ssh "${GUEST_USER}@${GUEST_IP}" 'free -h'
```

::: tip 预期
`free -h` 是来宾操作系统视角，`total` 通常接近当前提供给来宾的内存，但会受固件、内核保留区域和气球状态影响。比较 `Max memory`、`actual`、`rss` 与 `free -h` 时，重点说明每个数来自哪一层，不要求它们相等，也不能仅凭大小关系推断来宾负载。
:::

## 3.4 I/O 虚拟化：让虚拟机又快又稳地存取

多台虚拟机共享一块物理网卡、一块物理磁盘，Hypervisor 得像交通警察一样保证：每个数据包都投递给正确的虚拟机，互不混淆，还不能太慢。

### 四种实现路径

| 路径 | 做法 | 性能 | 共享 | 典型场景 |
| --- | --- | --- | --- | --- |
| 软件模拟 | 模拟一块经典网卡/磁盘，来宾用自带驱动 | 低 | 高 | 兼容性优先 |
| 半虚拟化（VirtIO） | 来宾装前端驱动，与 QEMU、vhost 或其他宿主后端通过 virtqueue 协作 | 高 | 高 | 现代虚拟化默认 |
| 设备直通 | 整块物理设备直接给一台虚拟机 | 极高 | 无（1:1） | 追求极致性能 |
| SR-IOV | 物理设备硬件上切成多个虚拟功能，分别给不同虚拟机 | 高 | 高 | 数据中心高性能场景 |

### VirtIO：半虚拟化的代表

软件模拟慢，是因为每笔 I/O 都要模拟传统设备行为。**VirtIO** 换了个思路：来宾里装一个轻量**前端驱动**，宿主侧由 QEMU、vhost 内核模块或其他后端实现虚拟设备；双方通过共享内存中的 **virtqueue** 交换描述符和数据。来宾要发数据，前端驱动把请求放进队列并通知后端，减少传统设备模拟开销。

::: tip KVM 里的 VirtIO
你在创建 `svr01` 时，磁盘控制器和网卡都选了 VirtIO 半虚拟化设备；QEMU 在宿主机侧提供后端驱动，来宾里由 Linux 内核自带 virtio 前端驱动，I/O 性能明显好于纯软件模拟。第一章 1.6 节讲过的“半虚拟化”就在这里落地。
:::

### 设备直通与 SR-IOV

* **设备直通**：把一张物理网卡整个交给一台虚拟机，虚拟机装原生驱动，性能接近物理机。代价是设备不能再被其他来宾共享，而且通常会限制带内存快照、挂起和热迁移；少数支持状态迁移的设备与软件栈可以例外。需要 **IOMMU**（Intel VT-d / AMD-Vi）限制设备可访问的内存范围。
* **SR-IOV**：物理设备在硬件层面把自己切成多个“虚拟功能”（VF），每个 VF 像一个小网卡，分别直通给不同虚拟机。兼顾高性能和共享。

### 动手观察：I/O 虚拟化

在宿主机分别观察虚拟机类型、网卡清单、磁盘清单，以及 XML 中的磁盘和网卡定义：

```bash
virsh --connect qemu:///system dumpxml "$VM_NAME" | grep -m1 '<domain type='
virsh --connect qemu:///system domiflist "$VM_NAME"
virsh --connect qemu:///system domblklist "$VM_NAME" --details
virsh --connect qemu:///system dumpxml "$VM_NAME" | sed -n -e '/<disk /,/<\/disk>/p' -e '/<interface /,/<\/interface>/p'
```

如果第二课沿用了推荐配置，通常会看到 `domain type='kvm'`，`domiflist` 的 `Model` 列为 `virtio`，磁盘 XML 的 `<target>` 也会显示 `bus='virtio'`。这些结果只能证明来宾暴露了哪类虚拟设备，不能单凭 XML 判断实际吞吐量或延迟。

从 `domblklist` 的 `Source` 列找到要观察的磁盘文件，把实际路径写入 `DISK_PATH`。下面只展示变量写法，文件名不要求与虚拟机名称相同：

```bash
DISK_PATH='/var/lib/libvirt/images/svr01.qcow2'
sudo ls -lh "$DISK_PATH"
sudo du -h "$DISK_PATH"
sudo qemu-img info --force-share "$DISK_PATH"
```

::: tip 预期
`ls -lh` 显示文件的逻辑长度，`du -h` 估算宿主文件系统已经分配的空间；`qemu-img info` 会自动识别镜像格式，并分别给出 `virtual size` 与 `disk size`。动态分配的 qcow2 镜像通常表现为虚拟容量大于已分配空间，但三个工具的统计口径不同，数值不必完全一致。
:::

::: warning 正在运行的镜像只做观察
`qemu-img info --force-share` 以共享只读方式查询正在被 QEMU 使用的镜像，不会修改磁盘；如果来宾同时写入，镜像元数据也可能在变化，因此输出只是观察时刻的近似结果。不要把 `info` 换成 `check -r`、`resize`、`convert` 等会检查修复或改写镜像的子命令。
:::

现在根据“兼容性、性能、共享能力”三个条件，为下面场景选择路径：

| 场景 | 第一优先级 | 你的选择 |
| --- | --- | --- |
| 老旧来宾没有 VirtIO 驱动，只要求先启动 | 兼容性 |  |
| 通用 Linux 服务器，需要较好的磁盘与网络性能 | 性能与易管理 |  |
| 一台计算来宾独占整块 GPU | 极致性能 |  |
| 多台来宾共享一张支持虚拟功能的高速网卡 | 低延迟与共享 |  |

::: tip 阶段验收
你能从 XML 中指出一个虚拟设备模型，并能为四个场景分别说明“为什么选、牺牲了什么”，就完成了本节验证。
:::

### 本课实训提交与验收

三个观察任务分别出现在 3.2、3.3、3.4 对应原理小节之后。完成全部观察后，把三组命令输出分别截图提交：

1. **CPU 证据截图**：`vcpucount`、XML 的 `<vcpu>` 行和来宾 `lscpu` 输出；
2. **内存证据截图**：EPT/NPT 参数、`dommemstat` 和来宾 `free -h` 输出；
3. **I/O 证据截图**：设备清单或 XML，以及 `ls -lh`、`du -h`、`qemu-img info` 的大小对照。

结论不需要单独写文本：验收时能指着截图说清 vCPU 调度、内存分层和 VirtIO/qcow2 三个结论即可（见下方验收标准）。

验收标准：

* 能指出观察目标虚拟机（`VM_NAME` 变量指向的来宾，默认 `svr01`）的当前 vCPU 数与 EPT/NPT 状态，并说明“分配 vCPU ≠ 独占物理核”；
* 能分别说明 `Max memory`、`actual`、`rss` 和 `free -h` 属于哪一层观察口径；
* 能从设备清单或 XML 辨认 VirtIO，并用逻辑长度、宿主分配空间和虚拟容量解释 qcow2 动态分配；
* 除课前补装缺失的 `qemu-utils` 外，三组观察全程只读，未修改虚拟机配置或磁盘内容。

## 3.5 GPU 虚拟化：怎样分配图形与计算能力

::: details GPU 虚拟化：给虚拟机分算力
GPU 是高度并行的专用处理器，和 CPU 的虚拟化思路不同。三种主要路线：

| 路线 | 做法 | 性能 | 共享 |
| --- | --- | --- | --- |
| API 转发 | 把来宾图形 API 或渲染命令转给宿主 GPU | 取决于实现与工作负载 | 高 |
| GPU 直通 | 整块 GPU 给一台虚拟机，装原生驱动 | 极高 | 无 |
| vGPU | 通过时间片、媒介设备、SR-IOV 或硬件分区等机制提供虚拟 GPU | 高 | 高 |

vGPU 是共享数据中心 GPU 的常见路线之一，但“vGPU”不是单一实现：有的按时间片共享，有的使用媒介设备或 SR-IOV，有的提供硬件隔离分区。能否用于 VDI、AI 训练或云游戏，取决于具体 GPU、驱动、授权和工作负载。
:::

## 3.6 虚拟化安全：隔离还会从哪里失效

::: details 虚拟化安全：隔离不是绝对保险
虚拟化引入新的攻击面，核心威胁包括：

| 攻击 | 目标 | 常见手段或例子 | 代表案例 |
| --- | --- | --- | --- |
| 虚拟机逃逸 | 从虚拟机攻破宿主机/Hypervisor | 利用虚拟硬件驱动漏洞 | VENOM（虚拟软驱漏洞） |
| 侧信道泄露 | 跨隔离边界推断其他上下文中的数据 | 利用缓存、推测执行等微架构状态 | Spectre、Foreshadow |
| 横向移动 | 从已失陷来宾继续攻击其他系统 | 窃取凭据、利用错误网络隔离或服务漏洞 | 取决于具体网络与身份系统 |
| 管理平面攻击 | 控制整个虚拟化平台 | 利用管理软件漏洞、弱口令 | vCenter RCE |

防御思路：加固 Hypervisor（最小化攻击面、及时打补丁、可信启动）、强化隔离（网络分段、微分段）、保护管理平面（管理网络隔离、RBAC、MFA、审计日志）。虚拟化隔离能缩小影响范围，但安全仍需系统化建设。
:::

## 3.7 本章小测

先独立作答，再核对解析。下面的问题组会直接给出反馈。

{{assessment:lesson-03-check}}

## 本章小结

* CPU 虚拟化解决特权冲突：保护环把权限分级，硬件辅助虚拟化（VT-x/AMD-V）让来宾内核在受限模式原生运行，敏感指令通过 VM Exit/Entry 交给 Hypervisor。
* 内存虚拟化解决双重翻译：影子页表靠软件同步、开销大；EPT/NPT 让硬件一次完成 GVA→GPA→MPA，是现代性能关键。
* I/O 虚拟化在兼容、性能、共享之间权衡：软件模拟兼容好性能差，VirtIO 半虚拟化是现代默认，设备直通追求极致但无法共享，SR-IOV 兼顾高性能与共享。
* 三种虚拟化（CPU/内存/I/O）是业界通用机制，KVM、VMware、Hyper-V 都建立其上。

下一课将回到实训，用 virt-manager 创建一台带图形桌面的 Ubuntu Desktop 来宾 `desk01`，体验虚拟显卡、显示链路和增强功能带来的桌面体验。
