序言
设备虚拟化技术,一直是云计算领域最重要的基础技术之一。我们在虚拟机里面看到的形形色色的设备,比如:网卡,磁盘,键盘,鼠标等,都离不开这项技术的帮助。这篇文章,我们将从技术演进的角度来谈一谈 Linux 现有的以及即将到来的设备虚拟化技术。
Trap-and-emulate
在最早期阶段,设备虚拟化常常和机器模拟器技术,比如:QEMU,绑定在一起。我们可以通过 QEMU 模拟真实设备的所有寄存器布局和操作流程,当 QEMU 虚拟机里面设备驱动需要访问该虚拟设备的寄存器时,这条访问指令会被 trap 到 QEMU,由 QEMU 来进行处理。这样,虚拟机里面的设备驱动在操作该虚拟设备时就像在访问真实的硬件设备一样,设备驱动也不需要任何变更。
Virtio
通过上述这种 trap-and-emulate 的方式来模拟设备,虽然不需要对真实设备驱动进行变更,但是设备访问过程中,频繁的陷入&陷出带来了严重的性能问题,因此,virtio 这类半虚拟化技术应运而生,并于 2008 年合入 Linux 内核主线。
相较于 trap-and-emulate 这种方式,Virtio 不再拘泥于依赖已有的设备驱动,而是定义了一套全新的专用于虚拟设备的驱动框架。设备驱动清楚自己是在操作虚拟设备,因此,在真正的 I/O 路径上规避了大量可能导致陷入&陷出的 mmio/pio 操作,从而提高了性能。虚机里面的 Virtio 驱动和 QEMU 模拟的 Virtio 设备的数据交互,本质上是一套基于共享内存 + 环形队列的通信机制。核心数据结构(split virtqueue)包括:两个 ringbuffer (avail ring, used ring) 和一个 descriptor table。工作机制类似 DMA,虚机内 virtio 驱动首先会将一个请求在内存中需要传输的散列 buffer 的地址、长度组成一个个描述符写入到 descriptor table 中,然后将这些描述符对应的 descriptor table 上的 index 写入到 avail ring 中,并通过 eventfd 机制通知到宿主机上的 virtio backend。由于这些 ringbuffer、descriptor table 以及散列 buffer 都在共享内存中(虚机本