做嵌入式和虚拟化运维这行,被问得最多的一类问题,不是"内核怎么编译",而是"我的 USB 设备在虚拟机里就是不认"。U盘插上没反应、USB 转串口在客户机里反复掉线、下载器一跑就断、加密狗第一天好好的第二天罢工——这些现象的根子,往往都指向同一件事:USB 虚拟化做得对不对。这篇就把我这些年踩过的坑、测过的方案、以及那些文档里不会写的细节,系统性地摊开讲一遍,从 USB 协议本身为什么难虚拟,到 KVM、VMware、Hyper-V 上的透传实操,再到 USB over IP、设备端 gadget 虚拟化、抓包排错和安全白名单。不管你是刚接触虚拟化的运维,还是天天跟 STM32、Zynq 打交道的嵌入式工程师,应该都能从里面找到能直接抄的配置和能少走的弯路。
1. USB虚拟化到底在虚拟什么:先拆清协议,再谈方案
很多人一上来就问"哪个软件能虚拟 USB",这个问题本身就问偏了。USB 虚拟化不是一个功能,而是一组技术,不同的场景解决的是完全不同的问题。不先把概念分清,后面选型一定翻车。
1.1 三类被混为一谈的"USB虚拟化"
我见过的需求基本可以归到下面四类,它们的技术路径差得很远:
| 类型 | 典型场景 | 本质 | 代表实现 |
|---|---|---|---|
| 主机侧透传 | 虚拟机用物理U盘、加密狗 | 把宿主机的物理设备直接交给客户机驱动 | QEMU usb-host、VMware USB Pass-through |
| 远程重定向 | 云桌面映射本地串口 | 将 USB 请求(URB)打包走网络 | USB/IP、usbredir |
| 设备端虚拟化 | 一块板子模拟成U盘/串口/HID | 设备控制器(UDC)软件模拟 | Linux USB Gadget、STM32 USB Device |
| 纯软件模拟 | 虚拟机里凭空多个键盘 | 无真实硬件,纯模型 | QEMU 模拟 HID、U盘 |
透传是"把真东西借出去",重定向是"把信号传过去",gadget 是"把板子装成别人",纯模拟是"无中生有"。这四类的排错思路完全不同:透传失败看宿主机是否抢占,重定向失败看网络和协议,gadget 失败看描述符和枚举,纯模拟失败看 QEMU 参数。
1.2 必须被"保真"传递的:描述符、端点、传输类型
USB 是典型的主从轮询式总线,主机发话,设备应答。虚拟化要做的核心工作,就是把设备的"自我介绍"原封不动地递到客户机面前。这套自我介绍就是描述符层级:
- 设备描述符:idVendor、idProduct、bcdUSB、bMaxPacketSize0
- 配置描述符:供电方式、最大功耗、接口数量
- 接口描述符:bInterfaceClass(CDC、HID、MSC、UAC 等)
- 端点描述符:端点地址、方向、传输类型、wMaxPacketSize、bInterval
透传时如果这段信息被宿主机的驱动"加工"过,客户机拿到的就是残缺描述符,表现就是"识别成未知设备"。所以一个铁律是:透传走的必须是原始 USB 层,而不是宿主机已经枚举并绑定驱动的设备节点——这一点后面 QEMU 和 usbipd 的选择上会反复提到。
传输类型则决定了虚拟化能否胜任:
- 控制传输:枚举、命令,延迟不敏感,几乎都能虚拟
- 批量传输:U盘、打印机,带宽敏感但对抖动容忍
- 中断传输:键鼠、HID,依赖轮询间隔 bInterval
- 等时传输:音频、摄像头,对时序和抖动极度敏感,最容易被虚拟化毁掉
1.3 为什么USB这么难虚拟:从微帧和高速握手说起
USB 全速是 12 Mbps,高速是 480 Mbps,低速 1.5 Mbps。高速模式下,主机每125 微秒发一个微帧;等时传输就挂在这套时间框架里。透传引入的任何延迟抖动,都会让等时流出现爆音或掉帧,这跟网络带宽够不够是两码事——是时间精度问题。
再一个是高速握手:设备接入后,主机会先复位,设备通过 chirp K/J 序列申请切换到高速。这个过程是硬件层面的电气协商。当你把设备透传给虚拟机,宿主机控制器已经完成了握手,虚拟机里的虚拟控制器并不需要重新协商,QEMU 会把协商结果直接呈现给客户机。这也解释了为什么有些设备透传后"速度档位"不对——它按全速而非高速被呈现了。
理解了这三点,你就能预判:U盘基本都能透传,音频和摄像头透传要看平台,而需要精确时序的下载器尽量别走远程。下面是具体平台的实操。
2. 主流虚拟化平台的USB透传:QEMU、VMware、Hyper-V怎么选
选平台本质上是在选"谁来完成枚举的接管"和"延迟有多低"。我把三种最常用的方案拆开讲,包括那些只在出问题时才会暴露的参数。
2.1 QEMU的两个入口:usb-host和usb-redir
QEMU 提供了两条完全不同的路:
- usb-host:宿主内核通过
usbfs把设备暴露给 QEMU,QEMU 直接跟设备对话,性能最好,本地透传首选。 - usb-redir:走 usbredir 协议,设备经网络传到 QEMU,配合 SPICE 常用,适合远程桌面场景。
usb-host 的关键在于设备定位方式,我强烈建议用总线号和端口号,而不是 vendor:product:
# 不推荐:同型号设备多个时会抓错 -device usb-host,vendorid=0x0403,productid=0x6001 # 推荐:按物理端口锁定,稳定可复现 lsusb -t # 先看清楚 bus 和 port -device usb-host,hostbus=1,hostport=4为什么?想象你有一排十个 FT232 串口插在同一个扩展坞上,vendor:product 完全一样。用 ID 匹配,QEMU 可能把第 3 个当成第 1 个给你,串口号全乱。按hostbus/hostport锁定,插哪个口就是哪个设备,这是我在产线测试环境里用命换来的经验。
2.2 libvirt里手写hostdev:路径匹配的坑
用 libvirt 管理时,配置在<hostdev>里写。同样推荐用地址而不是 ID:
<hostdev mode='subsystem' type='usb' managed='yes'> <source> <address bus='1' port='4'/> </source> </hostdev>managed='yes'让 libvirt 自动在透传前把设备从宿主机驱动上解绑、透传后再绑回。如果这里报错,八成是宿主机某个服务(比如 ModemManager、usbguard)在设备一插入时就抢走了,导致解绑失败。解决办法是给设备加 udev 规则,把宿主机对该设备的自动绑定屏蔽掉。
还有一个高频现象:USB 设备热插拔后 bus/port 变了(换到别的口),XML 里写死的地址失配,虚拟机里就再也看不到设备。要么固定插口,要么改用vendor/product加allow过滤,二选一,别指望它自动跟。
2.3 VMware的USB仲裁:为什么它总"抢"
VMware Workstation 有一整套 USB 仲裁机制(Windows 上是vmware-usbarbitrator服务在管)。它的问题不在能不能透传,而在"抢得太积极":U盘一插,宿主机和虚拟机都想接管,结果两边驱动打架。
我的标准做法是:
- 在虚拟机设置里勾选显示所有 USB 输入设备;
- 配置"自动连接"规则时,明确指定新 USB 设备连接到虚拟机还是宿主机;
- 客户机内装好对应驱动,尤其是 USB 转串口这类需要厂商 VCP 驱动的设备。
Windows 客户机里,如果设备管理器里出现一个带感叹号的"其它设备"或反复刷新,通常是宿主机已经绑定了驱动,客户机拿不到原始描述符。这时先把宿主机侧的厂商驱动卸载(保留系统自带的通用枚举),再插上让它透传,客户机里再装驱动,顺序反了就是白费劲。
2.4 "USB资源不足"背后是控制器端口耗尽了
这个报错在 QEMU 和 VMware 上都常见。根因不是主机 USB 口不够,而是虚拟机内部的虚拟 USB 控制器端口用满了。
QEMU 默认可能只配了一个 UHCI/EHCI 控制器,端口数量有限。解决办法是按需增加控制器,并明确指定是 USB 3.0(xHCI)还是 2.0:
-device qemu-xhci,id=xhci,p2=8,p3=8 # 给足够多的 2.0/3.0 端口 -device usb-host,bus=xhci.0,hostbus=1,hostport=4一个 xHCI 控制器能挂的端口有限,设备一多就得加控制器实例。这也是为什么有人明明只透传了两个设备却报资源不足——他透传的是组合设备(比如带复合接口的 HID),一个物理设备占用了多个虚拟端口。
3. USB over IP与终端虚拟化:让串口和加密狗跨越物理机
本地透传解决"同一台机器",但企业里更常见的需求是"设备在这栋楼,虚拟机在那栋楼"。这就是 USB over IP 和云桌面 USB 重定向的地盘。
3.1 USB/IP协议的组成与usbipd-win
USB/IP 把 USB 请求块(URB)在网络上传输。它由两端组成:
- 服务端:运行
usbipd,导出物理设备;内核模块usbip-host - 客户端:内核模块
vhci_hcd造一个虚拟主机控制器,把远程设备挂上来
Linux 上的经典命令:
# 服务端:查看并绑定可导出的设备 usbip list -l usbip bind -b 1-4 # 客户端:查看远程设备并挂载 usbip list -r 192.168.1.100 usbip attach -r 192.168.1.100 -b 1-4而在 WSL2 场景里,微软官方推的是usbipd-win:Windows 侧跑服务,把设备共享给 WSL2 的 Linux 内核,然后利用 WSL2 自带的 USB/IP 客户端挂载。命令大致是:
# Windows 侧(管理员) usbipd list usbipd bind --busid 1-4 usbipd attach --wsl --busid 1-4这一步的前提是 WSL2 本身能起来,而 WSL2 依赖硬件虚拟化。如果启动时报虚拟化未启用,那是 BIOS/UEFI 里的虚拟化开关和 Windows 侧"基于虚拟化的安全性"(VBS)占用了 hypervisor 导致的,属于平台配置问题,得先把底层虚拟化环境理顺,USB 的活儿才谈得上。
3.2 云桌面里的USB映射策略:设备级还是端口级
在终端虚拟化/云桌面(Citrix、各类 VDI 方案)里,USB 重定向一般有两种粒度:
- 设备级重定向:整个设备映射到虚拟桌面,客户机拿到完整设备,兼容性最好,但要求网络稳定。
- 端口级/协议级重定向:只把串口数据流抽象成虚拟 COM 口转发,性能好、带宽低,但丢掉了 USB 原生语义,遇到需要精确控制的加密狗或专用设备就失效。
选择原则很简单:加密狗、专用采集卡走设备级;普通扫码枪、普通串口走协议级。加密狗这类设备对时序和握手敏感,协议级抽象常常导致"许可证无效",因为厂商的加密逻辑依赖底层 USB 交互。
3.3 哪些设备适合远程映射,哪些千万别
| 设备类型 | 传输类型 | 远程映射建议 |
|---|---|---|
| U盘/存储 | 批量 | 可以,注意带宽 |
| USB转串口 | 中断+批量 | 很适合,延迟容忍度高 |
| 加密狗 | 控制+中断 | 可用,需设备级,测试验证 |
| USB麦克风 | 等时 | 不建议,容易爆音 |
| USB摄像头 | 等时 | 不建议远程 |
| JTAG下载器 | 控制+批量 | 不建议,时序敏感 |
网络抖动对等时传输是致命的。本地透传时 USB 的时间精度就够勉强了,再叠加一层网络,音视频基本没救。这不是配置能解决的,是物理规律。
4. 设备端视角:把开发板虚拟成USB设备
前面都是从主机角度讲,但嵌入式圈子里的"USB 虚拟化"经常指另一件事:让 MCU 或者 Zynq 之类把自己模拟成某个 USB 设备。这是 USB Gadget 的领域。
4.1 MCU做USB设备:描述符和枚举是重灾区
STM32、Zynq 裸机下做 USB 从机,最容易翻车的不是代码逻辑,而是描述符写错导致枚举失败。枚举过程大致是:
- 主机复位设备,设备在地址 0 应答;
- 主机
GET_DESCRIPTOR拿设备描述符前 8 字节,知道端点 0 包长; - 主机分配新地址
SET_ADDRESS; - 主机读完整配置描述符;
SET_CONFIGURATION使能端点,枚举完成。
任何一个描述符的长度字段对不上(比如配置描述符里wTotalLength少算了接口和端点),主机就会直接中断枚举。这也是"圈圈教你玩USB"那类教程反复强调描述符的原因——它是协议的地基。
另外常被问的:MCU 没有 USB 差分信号引脚怎么办?很多低成本 MCU 根本没有原生 USB PHY,或者差分引脚被占用了。常见的替代思路是外挂一个 USB 转串口芯片(CH340、CP2102N、FT232 之类),把 MCU 的 UART 桥接到 USB,对主机呈现为一个虚拟 COM 口。虽然这不是严格意义的"USB 设备虚拟化",但对功能而言是高效解法。
4.2 转串口驱动在虚拟化环境的安装顺序
FT232R、FT231X、CP2102N、CH340 这几种芯片在虚拟机里最容易出问题。它们各自需要不同的驱动,而且安装顺序错了必炸:
| 芯片 | 官方驱动 | 虚拟化注意点 |
|---|---|---|
| FT232R / FT231X | FTDI VCP | 透传前先卸宿主机FTDI驱动,让原始枚举透传 |
| CP2102N | Silicon Labs VCP | 客户机装驱动后再插设备 |
| CH340/CH341 | 沁恒驱动 | Windows下免驱常失效,需手动装 |
| Z-TEK力特 USB转232 | 随芯片而定 | 认准芯片型号再下驱动 |
正确顺序是:宿主机确认不绑定厂商驱动 → 透传设备 → 客户机内安装 VCP 驱动 → 验证 COM 口。反过来的话,宿主机把设备绑定了,透传给客户机的就是残缺设备,客户机里怎么装驱动都是感叹号。
一个实操细节:Windows 10/11 对 FTDI 设备的驱动匹配很激进,会自动装一个"USB Serial Converter"。如果虚拟机里想要的 COM 口号被宿主机占着,先在宿主机设备管理器里把该设备禁用(不是卸载),透传后再释放。
4.3 USB Blaster、Platform Cable这类下载器为什么在虚拟机里总掉
FPGA 工程师的痛点。USB Blaster(Altera/Intel)、Xilinx Platform Cable 这类 JTAG 下载器,本质上是对时序敏感的中断+批量设备,透传一旦引入抖动,就表现为:
- 编程烧录中途断开;
- 反复 "USB firmware loader" 加载失败;
- 客户机报无法加载硬件驱动。
原因有两层:一是 JTAG 时序容差小,透传延迟吃不消;二是 Windows 客户机里跟宿主机抢设备的进程多。我的建议是:
涉及 FPGA 烧录、量产烧写这类操作,优先用物理机或给虚拟机做 USB 控制器的 PCIe 直通(把整个 xHCI 控制器直通给虚拟机),而不是设备级透传。整控制器直通绕开了 QEMU 的逐设备模拟,时序问题少一大半。
5. 让看不见的总线可观测:抓包、排错与安全
USB 排错最痛苦的地方在于"看不见"。设备管理器只告诉你"未知设备",具体哪一步枚举失败的,得靠抓包和内核日志。
5.1 Wireshark配USBPcap:过滤器和描述符怎么看
Windows 上装 USBPcap,Wireshark 就能抓到 USB 流量。关键过滤器:
usb.device_address == 5 # 只看某个地址的设备 usb.transfer_type == 0x02 # 只看批量传输 usb.bDescriptorType == 0x02 # 只看配置描述符抓包时最有价值的是枚举阶段:看GET_DESCRIPTOR的应答、SET_ADDRESS、SET_CONFIGURATION,能直接定位是描述符不合法还是请求无响应。注意 USBPcap 只能抓宿主机侧的流量,设备一旦透传进虚拟机,宿主机就抓不到了——要在客户机里装抓包环境,或者做控制器直通后在外层抓。
5.2 枚举失败的完整排查链路
我通常按这个顺序走,从底到上:
# 1. 内核是否看到设备 dmesg | grep -i usb # 2. 拓扑和速度档位 lsusb -t # 3. 详细描述符 lsusb -v -d 0403:6001 # 4. 是否被驱动占用 usb-devices | grep -A5 FTDI第一步dmesg就能筛掉一半问题:如果日志里根本没有新设备,说明电气层或控制器就没识别,跟虚拟化无关。如果看到了设备但lsusb -v读描述符报错,那是设备或线材问题。如果宿主机识别正常但客户机不行,才轮到虚拟化配置背锅。
Windows 客户机侧对应地看设备管理器和事件查看器。那个常被吐槽的 "WD SES Device" 实际上是西数移动硬盘的加密/管理接口,跟透传无关,别被它带偏。
5.3 用usbguard给USB虚拟化加一道安全闸
USB 是攻击面最广的接口之一——BadUSB 那类伪装成键盘的设备至今有效。在服务器和虚拟化宿主上,我建议上usbguard做白名单:
usbguard list-devices usbguard allow-device 4 usbguard generate-policy > rules.conf规则可以按 vendor、product 甚至序列号放行。这样插进来一个没登记的 U 盘,默认是拒绝的。注意 usbguard 会和 libvirt 的managed='yes'抢设备,所以要在规则里给待透传的设备放行,否则透传必失败——这俩的冲突我吃过不止一次。
5.4 Web Serial这类新玩法与虚拟化的关系
浏览器里的 Web Serial API 让网页直接读写串口,很多人拿它做在线烧录、设备调试。它的底层还是要枚举 USB 转串口设备。如果这台上位机本身跑在虚拟机里,Web Serial 能不能用,取决于虚拟化层把串口设备透传得是否干净——设备级透传通常正常,协议级重定向就未必,因为浏览器拿到的是虚拟 COM 口,握手细节可能缺失。Android USB 通信、STM32 的 DFU 升级也同理,客户端能不能拿到完整的 USB 语义,是成败关键。
6. 我个人在长期实操中的几点体会
折腾了这么多环境,有几个经验我觉得比任何单条命令都值钱。第一,永远优先按物理端口定位设备,vendor:product 匹配在设备一多的时候就是灾难制造机。第二,透传的成败很多时候在插设备之前就决定了,宿主机的驱动绑定、usbguard 规则、仲裁服务,这些都要提前清理干净,等出问题了再查,成本翻几倍。第三,时序敏感的设备别硬上虚拟化,FPGA 下载器、音视频采集、精确计时的仪器,能用物理机或控制器直通就别省这点硬件钱,稳定性是用真金白银换来的。第四,抓包是 USB 排错的终极大招,学会看枚举阶段的请求应答,比在论坛上问一百遍都管用。这些内容后续还可以往 PCIe 直通、SR-IOV 这些更底层的方向延伸,如果大家对整控制器直通的配置细节感兴趣,我下次可以单独拉一篇讲 QEMU 的 VFIO 绑定和 IOMMU 分组。