system: [SYSTEM]

user: 3. 基于SPICE协议的USB重定向识别原理和性能分析 基于SPICE协议的USB重定向是将客户端本地的USB设备映射到QEMU虚拟机中的过程，这样虚拟机在USB驱动程序的配合下，就可以像访问本地USB设备一样，访问客户端的USB设备。本地客户端将USB外设通过网络方式，映射到服务端的虚拟化层；服务端虚拟机管理程序QEMU通过创建虚拟的USB重定向字符设备，供虚拟机中的驱动层来访问。 当客户端本地的SPICE客户端连接到服务端QEMU虚拟机时，其会同spice-server建立通道连接，如果客户端有USB设备需重定向，则会同服务端spice-server建立USBRedir通道来收发数据，服务端QEMU会模拟出Redirect字符设备，同spice-server通道关联，这样从通道接收到的数据就会提交给Redirect字符设备，然后由控制器提交到虚拟缓冲区供虚拟机读取；如果Redirect字符设备有数据需要发送，则写入到对应的spice-server通道，由USBRedir通道来负责将数据发送到客户端。基于SPICE协议的USB设备重定向架构图如图2所示。 图2. 基于SPICE协议的USB重定向架构图 从图2可以看出： 1) 客户端USB设备同内核态的控制器进行交互，二者之间通过端点endpoint建立逻辑上对应的管道pipe来通信； 2) 内核态经过USB核心处理负责进行驱动的配置和读取操作，向上提供了接口给USB Fs； 3) 用户态则通过LibUSB的API来配置和读写操作； 4) Spice-Gtk则直接调用LibUSB相关接口来初始化和注册对应的事件处理函数，然后通过USBRedir模块来读取数据，并经过协议封装后，通过SPICE的vmc通道发送给服务端； 5) 服务端spice-server同spice-vmc模块进行耦合，从spice-server接收到的数据，通过qemu-char字符设备接口写入到虚拟的字符设备Redirect中，然后经过USBRedir部分的协议解析后，提交到虚拟缓冲区，供USB控制器处理； 6) USB控制器获取的数据则写入到Redirect字符设备中，然后经过USBRedir协议的处理后，调用spice-vmc的接口，最终通过spice-server的通道发送给客户端。 在传统PC上使用USB时，USB设备直接连接PC USB控制器，识别速度快，用户体验好，USB设备的数据传输不会造成其它影响，而对于基于SPICE协议的桌面虚拟化应用来说，USB设备是通过客户端和主机的网络重定向到虚拟机中的，由于各种延迟的存在导致USB识别速度普遍偏慢，极大的影响用户使用体验。本文从USB重定向识别过程出发，分析基于SPICE协议的USB重定向识别原理和传输数据的性能，进而提出相应的优化方案。 3.1. 基于SPICE协议的USB重定向识别原理 USB识别过程也就是重定向开始的过程，当客户端检测到有USB设备插上或者有USB已经存在，且该USB设备允许重定向，则首先会通过LibUSB的接口打开该设备，获取到设备句柄，然后调用USBRedir提供的usbredirhost_set_device接口来走重定向逻辑，此时，USB重定向已经开始。 usbredirhost_set_device会根据设备句柄获取到设备结构，然后会调用LibUSB的接口来获取设备描述符和配置描述符，再调用LibUSB的libusb_set_auto_detach_kernel_driver或libusb_detach_kernel_driver接口将其从内核态detach，最后调用libusb_claim_interface对设备进行宣告，至此，/dev/下对应的设备会消失，彻底由LibUSB来接管。 接下来会对USBRedir进行适当的配置，包括端点信息、最大包长等。 最后会复位设备，获取设备的速率模式，再将设备的接口描述符有关信息、端点有关信息写入到队列，SPICE中的USB重定向通道会负责对该队列中的数据发送到服务端。 至此，服务端的虚拟机中设备管理器中已经可以看到相应的USB设备了。 后续过程，磁盘控制器会进行磁盘的读写，才能看到对应的USB磁盘。 3.2. 基于SPICE协议的USB重定向识别过程数据传输性能分析 对于USB设备，根据其设备类型的不同，主要有SCSI/HID/UVC等几种协议。SCSI协议主要用于USB存储设备(U盘、USB硬盘、USB读卡器等)，HID作为人机接口设备采用的协议(键盘、鼠标等)，UVC主要用于USB视频设备(Camera、DV等)。 在虚拟机中通过Bus Hound USB抓包工具分析确实有大量的数据会通过Data In发送到主机，为了确定其数据内容和采用的协议，这里通过Wireshark来抓包，并且对USB协议进行分析，可以知道，大量的数据传输是主机需要通过SCSI command命令来读取U盘对应的Flash扇区导致，即主机需要知道U盘的扇区相关的信息，才能显示该磁盘。可以参考文档《SCSI Commands Reference Manual.pdf》。 通过Wireshark抓包，对request请求报文(如图3所示)和respond响应报文(如图4所示)的协议分析可以验证，此过程确实采用SCSI的CDB Read命令来读取了LUN上的大量的设备块信息，且数据量较大。 为了方便对整个数据传输过程中的理解，这里给出了客户端USB大容量存储设备数据流图，如图5所示： 图3. URB请求报文 图4. URB响应报文 图5. USB大容量存储设备数据流图 3.2.1. 协议层面分析 从抓包分析我们知道，USB重定向U盘识别过程进行了大量扇区数据的读取操作，通过抓包可以看出其协议封装，对USB Mass Storage的读写是按如下协议进行的： 图6. USB大容量存储协议封装 从图6中可以看出，首先是磁盘SCSI系统层读取，然后经过USB Mass Storage层的封装，再经过USB URB的封装，这三层为常规USB设备读取过程协议。 通过对SPICE重定向通道得到其协议封装，这里通过图示来画出网络数据包的协议封装： 图7. USB重定向通道协议封装 从图7中可以看出，USB数据是封装到USBRedir协议中进行传输的。经过USBRedir层的封装后，就可以经过SPICE封装通过SPICE通道发送，SPICE协议是承载于TCP层之上的。 由此可知，在经过QEMU模拟的控制器后，先将磁盘读写和USBMS以及USB URB解析后转换成USBRedir协议，才经过SPICE协议进行发送。 3.2.2. 虚拟机内部磁盘读取分析 在虚拟机内部，通过微软提供的Diskmon磁盘读写监控工具，监控USB识别中的磁盘扇区读写，可以看出每次IO读取的数据量大小。 对比Windows7和Windows10发现，Windows10每次IO读取的数据量最大可达1 M字节大小；而Windows7每次IO读取的数据量在4 K字节大小。 因此，磁盘读写IO是影响U盘识别速度的一大原因。 3.2.3. 虚拟机内部抓包分析 在虚拟机内部，通过Wireshark携带的USBPcap工具来抓取USB端口的数据可以知道，进行了大量的数据读取操作。 对比Windows7和Windows10，发现，Windows10每次读取的数据包长度为64 K；而Windows7每次读取的数据包长度为4 K。 根据协议封装，Windows10在IO层面将DISK.SYS驱动的1次IO请求，由USBSTOR.SYS驱动拆分为16次对USB总线的请求，16次USB总线请求的响应则由DISK.SYS驱动合并成了1次IO数据响应；而Windows7则没有进行IO的合并过程，DISK.SYS驱动1次IO请求就是4 K，USBSTOR.SYS驱动也是按4 K请求的。 3.2.4. 从USB控制器角度分析 由于采用USB2.0和USB3.0的控制器对IO数据长度和USB数据包的长度没有影响。因此说明，控制器层面不会进行数据包的合并操作。 目前推断，这块应该是USB3.0控制器的特性。通过跟踪QEMU相关代码，确认Windows7采用3.0控制器，内部数据包长度也是4 K。但是Windows7采用3.0控制器，识别时间要接近于Windows10。但是随着U盘容量越大，Windows7采用3.0的控制器，也没有Windows10的效果，说明Windows10对磁盘IO进行过优化。 根据以上的分析可以知道，影响USB重定向识别速度的因素，主要包括磁盘读写IO和USB控制器等。此外，如果在网络堵塞情况下，识别过程传输的大量未经压缩的数据也是导致识别速度偏慢的原因之一。