ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

WSL2 USB设备直通完全指南:用usbipd-win解决嵌入式开发痛点

WSL2 USB设备直通完全指南:用usbipd-win解决嵌入式开发痛点 做嵌入式开发的人很少有没被 WSL2 的 USB 问题折腾过的。平时在 Windows 上敲命令、编译、跑 Docker 都很顺手一旦插上 USB 转串口、单片机开发板或者 USB 网卡WSL2 里却什么都看不到。这不是你的 Linux 发行版装坏了而是 WSL2 本质上是一台轻量虚拟机Windows 默认没有把 USB 设备直通进去。我最早也被卡在这儿后来研究了一圈最终靠 usbipd-win 这个工具把问题彻底解决。这篇就完整说一下 WSL2 使用 USB 的方法包括原理、完整实操、自动化配置和踩坑记录适合做嵌入式、玩板子、搞刷机和需要在 Linux 环境操作 USB 设备的同学参考。1. WSL2 为什么天生没法直接用 USB 设备1.1 先搞清楚 WSL2 的真实“形态”很多人以为 WSL2 只是“Windows 里的一个 Linux 文件夹”甚至以为它和虚拟机软件一样能直接插 USB。实际上WSL2 是基于 Hyper-V 虚拟化平台运行的轻量级虚拟机它有一个独立的 Linux 内核和 Windows 之间靠虚拟网络和虚拟磁盘通信。正因为它有独立内核所以 WSL2 能跑 Docker、能加载内核模块、能良好地运行 Ubuntu 20.04/22.04但代价就是设备直通不再像 WSL1 那样“透明”。USB 设备在物理上插在 Windows 主机上Windows 首先看到它、驱动它。虚拟机里的 Linux 内核默认看不到这块硬件。这就像你把 U 盘插在宿主机上然后跑到另一台物理服务器上想读同一个 U 盘隔着一层硬件边界什么事都传不过去。WSL2 虚拟机没有像 VMware Workstation 那样的“连接 USB 设备”图形按钮所以需要额外方案。1.2 WSL1 和 WSL2 对 USB 的差别WSL1 和 WSL2 虽然都叫 WSL但对 USB 设备的处理逻辑完全不一样项目WSL1WSL2内核形态通过系统调用翻译层模拟 Linux 接口真实 Linux 内核虚拟机能否跑内核模块不能可以USB 转串口访问映射成/dev/ttyS*Windows 占用时可用默认不可见U 盘访问可挂载 Windows 盘符/mnt/c等无法直接读块设备可通过挂载驱动挂载 USB 块设备USB 网卡 / 抓包设备基本不可用需要直通方案内核级调试工具不支持支持但依赖设备直通WSL1 确实能“打开串口”但它不是真正的 USB 设备访问而是 Windows 的 COM 口被映射成 Linux 下的字符设备。你要玩 USB 抓包、USB 网卡、需要安装内核模块的调试工具WSL1 指望不上。WSL2 有了真实内核可设备又被虚拟机挡住所以就出现了“两不像”的尴尬局面。1.3 USB/IP 协议是当前主流的解决思路USB/IP 协议并不是什么新东西它把“USB 设备”虚拟成网络上的资源一台机器共享 USB 设备另一台机器通过网络连接到这个设备。Linux 内核原生就支持 USB/IP包括usbip-host和vhci-hcd模块。Windows 侧则可以通过开源工具usbipd-win实现 USB/IP 服务端。说白了在 Windows 上装一个 usbipd 服务Windows 把 USB 设备“共享”出来WSL2 里的 Linux 通过 USB/IP 协议去“远程连接”这个设备。设备在物理上仍然插在 Windows 上但 Linux 内核会把它识别成一个本地 USB 设备你可以正常读/dev/ttyUSB0、用lsusb看到它、用dmesg看到它的插入事件。微软官方文档现在也推荐用这种方式所以它不是小众野路子而是被官方认可的方案。2. 完整实操用 usbipd-win 把 USB 设备接到 WSL22.1 环境准备Windows 版本、WSL 内核要求先把基础条件列清楚Windows 10 版本 1903 及以上或者 Windows 11。旧版本我建议直接升级否则后面很多问题。WSL2 已经正常启用能正常跑 Ubuntu。WSL 内核需要包含 USB/IP 相关模块。一般来说从 2021 年下半年之后的 WSL 内核版本开始官方内核已经编译了vhci-hcd、usbip-host等模块。如果你的 WSL 特别老先运行wsl --update更新内核再继续。我建议在开始之前先打开管理员 PowerShell运行wsl --status和wsl --version确认 WSL2 状态。如果显示的还是 WSL1需要用wsl --set-default-version 2切换。很多人在usbipd attach时报错“WSL 2 is not running”其实不是 WSL 没启动而是默认发行版还是 WSL1。2.2 Windows 侧安装并启动 usbipdusbipd-win 的安装方式非常灵活。最简单的是在管理员 PowerShell 里用 wingetwinget install usbipd如果 winget 搜不到包或者网络慢可以直接去 GitHub 下载最新版本的 MSI 安装包安装过程一路 Next。装完之后系统里会多一个名为usbipd的 Windows 服务默认是开机自启的。你可以用 PowerShell 检查Get-Service usbipd如果服务没有运行手动启动Start-Service usbipd注意所有usbipd命令行操作都需要管理员权限因为绑定 USB 设备、附加设备都涉及系统级驱动接口。普通用户运行usbipd bind会直接报权限不足。2.3 wsl 侧安装 usbip 工具进入 WSL2 的 Ubuntu 终端先安装用户空间工具sudo apt update sudo apt install linux-tools-generic hwdata安装完成后usbip命令不一定直接在 PATH 里。Ubuntu 的linux-tools-generic包装完可执行文件通常在/usr/lib/linux-tools/内核版本-generic/usbip下面。你可以这样找ls /usr/lib/linux-tools/找到对应路径后做一个软链接以后用起来方便sudo ln -s /usr/lib/linux-tools/$(uname -r | awk -F- {print $1})-generic/usbip /usr/local/sbin/usbip这里还有一个关键步骤加载内核模块vhci-hcd它是 WSL2 虚拟机侧用来模拟 USB 主控制器Host Controller的模块sudo modprobe vhci-hcd lsmod | grep vhci如果modprobe vhci-hcd提示找不到模块说明你的 WSL 内核版本太老。要么运行wsl --update更新内核要么重启 WSL 后重试。模块加载之后/sys/devices/platform/vhci_hcd.0这个路径应该存在后面usbip attach需要它。2.4 绑定与连接的操作全流程在 Windows 管理员 PowerShell 里先查看当前所有 USB 设备usbipd list输出大概长这样BUSID VID:PID DEVICE STATE 1-2 0403:6001 USB Serial Converter Not shared 1-4 0951:1666 Mass Storage Device Not sharedBUSID就是总线 ID比如1-2DEVICE是设备描述STATE表示是否已被共享。要把某个设备共享给 WSL2先在 Windows 侧执行绑定usbipd bind --busid 1-2绑定之后设备状态变成Shared。注意bind 一次以后这个设备默认会一直处于共享状态除非你手动 unbind。也就是说你以后重新插拔设备它大概率还会保持共享不需要每次 bind。接下来有两种连接方式。如果你用的是新版 usbipd-win可以直接用自带的 WSL 附加命令在管理员 PowerShell 里执行usbipd attach --wsl --busid 1-2这个命令会自动找到正在运行的 WSL 发行版在 WSL 内部执行usbip attach不需要手动查 IP非常省事。如果不想用这个参数也可以老派手动连先确认 WSL 里的默认网关 IP这个 IP 通常就是 Windows 主机在 WSL 虚拟网络里的地址ip route show default | awk {print $3}然后在 WSL 里执行sudo usbip attach -r 上面的IP -b 1-2执行完用lsusb查看如果出现对应的设备说明连接成功。使用完毕后可以在 PowerShell 里执行usbipd detach --busid 1-2或者直接在 WSL 里断开sudo usbip detach -p 0-p后面的数字是端口号一般第一个附加的设备是 0。我建议优先用 Windows 侧detach因为它的状态管理更明确不会让设备卡在“Attached”状态。3. 典型设备的接入体验与参数解析3.1 USB 转串口设备FT232、CP2102、CH340USB 转 TTL 串口是嵌入式调试最常用的设备也是我在 WSL2 里使用频率最高的 USB 设备。以常见的 FT232VID:PID 为 0403:6001为例把设备 bind 并 attach 到 WSL2 之后你需要确认 Linux 识别到了哪个串口设备dmesg | tail -30 ls -l /dev/ttyUSB*一般 FT232 和 CH340 会注册为/dev/ttyUSB0CP2102 也会是/dev/ttyUSB0而原生 USB CDC 设备比如很多 STM32 板载调试器会注册为/dev/ttyACM0。如果你的设备管理器里 Windows 侧装的是 FTDI 驱动Linux 侧也恰好有ftdi_sio内核模块两者互不干扰。访问串口通常需要权限。Ubuntu 下串口设备默认属主是root:dialout所以你要把当前用户加入dialout组sudo usermod -aG dialout $USER重新打开 WSL 终端然后用picocom或minicom连接sudo apt install picocom picocom -b 115200 /dev/ttyUSB0-b 115200是波特率具体根据你的设备而定。如果picocom提示无法打开设备先看dmesg有没有权限错误再看是不是设备被 Windows 侧的程序占用了。USB/IP 连接有点特殊设备一旦被 WSL attach 过去Windows 侧会短暂地从设备管理器里“消失”这是正常的。如果在 Windows 侧有串口调试工具还开着端口最好先关掉再 attach。3.2 嵌入式开发板、刷机工具与 ADB 设备做单片机开发的人经常会遇到一个场景Windows 上有 PlatformIO、STM32CubeProgrammer、Amlogic USB Burning Tool 这些刷机工具但它们只能识别 Windows 下的 USB 设备。一旦你想在 WSL2 里用openocd、pyusb直接操作 USB 设备就得先把设备从 Windows 手里“拿过来”。方法还是一样只是选取的 BUSID 要精确。比如某块 RK3566 开发板进入 MaskROM 模式后usbipd list会显示Rockchip USB之类的设备。绑定后 WSL2 里执行lsusb能看到对应的 Vendor ID 和 Product ID然后就可以用 Linux 下的刷机工具、pyusb脚本去访问设备。这里有个坑如果设备在 Windows 侧被某个工具轮询占用bind可能会失败或者状态一直是Attached。这是因为 usbipd 不能绑定一个已经被 Windows 驱动独占且正在高速读写的设备。解决办法是先把 Windows 侧的刷机工具、设备管理器关掉必要时在设备管理器里禁用设备然后再bind。如果实在不行也可以先usbipd unbind --busid BUSID再重新bind。3.3 U 盘和移动硬盘的挂载U 盘、移动硬盘这类大容量存储设备在 WSL2 里用 USB/IP 也能接但要注意使用方式。attach 之后用lsblk或sudo fdisk -l查看新增的块设备。比如一台设备可能变成/dev/sdb分区是/dev/sdb1。手动挂载sudo mkdir -p /mnt/usb sudo mount -o uid$(id -u),gid$(id -g) /dev/sdb1 /mnt/usb如果 U 盘是 NTFS 或 exFAT 格式WSL2 内核一般都能识别挂载时不用额外装驱动。如果发现mount报错先确认格式sudo blkid /dev/sdb1老内核可能需要安装linux-tools或 exfat 工具但 Ubuntu 22.04 的默认内核已自带 exFAT 支持。需要特别提醒的是usbip 方案不适合频繁拷贝超大文件。因为 USB/IP 的链路是“USB 设备数据 → Windows 驱动 → 网络协议 → Linux vhci 驱动”多了一层转换。拷贝一个大镜像文件速度还行但大量小文件会有明显的延迟和 CPU 占用。如果只是想在 WSL2 里读 Windows 下的文件直接用/mnt/c、/mnt/d挂载目录简单得多没必要走 USB 直通。4. 权限、自动化与安全配置4.1 让 WSL 用户免 sudo 访问 USB 设备每次操作 USB 设备都加sudo很烦。除了上面提到的加入dialout组还可以通过 udev 规则控制权限。比如创建文件/etc/udev/rules.d/99-usb-serial.rules写入SUBSYSTEMtty, GROUPdialout, MODE0660如果你想更宽松一点允许所有用户访问某些 USB 设备SUBSYSTEMusb, ATTRS{idVendor}0403, MODE0666写完后重载规则sudo udevadm control --reload-rules sudo udevadm trigger注意 udev 规则对已经 attach 的 usbip 设备通常也生效。如果改完没有反应可以先usbipd detach再重新attach让设备重新触发内核事件。4.2 开机自动绑定与自动 attach我平时最实用的配置是“Windows 开机自动绑定 WSL 启动自动 attach”。usbipd-win 新版支持--auto-attach选项绑定设备时加上这个参数以后启动 WSL 时设备会自动附加到 WSL 里usbipd bind --auto-attach --busid 1-2如果你想完全手动控制不想装操作系统级的计划任务也可以写一个小脚本。Windows 侧创建一个usb-attach.bat主要执行usbipd bind --busid 1-2 usbipd attach --wsl --busid 1-2放到“启动”文件夹或者任务计划程序里以管理员权限运行。WSL 侧也可以放一个/etc/profile.d/usb-attach.sh每次打开终端自动执行 attach。不过我不建议把自动 attach 写得太激进因为有些设备 Windows 侧程序还在用强行绑走会造成设备冲突。最稳妥的是需要用的设备手动 attach不需要的设备保持Not shared状态。4.3 安全注意事项与端口说明USB/IP 服务默认监听 TCP 3240 端口。在 Windows 防火墙里usbipd-win 安装时会默认放行局域网访问。因为 USB/IP 协议本身没有加密你的 USB 设备数据在网络上相当于明文传输所以不要把这个端口暴露到公网或不可信网络。如果是在公司网络里使用最好只在受信任的网段内操作。另外不要随便 bind 鼠标、键盘等 HID 输入设备。一旦你把键盘 bind 到 WSL2Windows 侧会失去对这个键盘的输入能力如果你只有一个键盘且正在用它输入命令很可能当场“黑屏”只能强制重启或拔掉别的输入设备。我在测试时曾试着 bind 一个无线鼠标结果鼠标立刻失灵最后硬生生用键盘 reboot 才恢复。5. 踩坑记录与问题排查5.1 找不到 usbip 命令、模块加载失败这是最常见的坑。usbip命令找不到多半是linux-tools-generic安装不完整或内核版本不匹配。建议执行sudo apt install --reinstall linux-tools-generic linux-tools-$(uname -r | grep -oP ^\d\.\d)-generic如果报错说没有linux-tools-6.x.x-generic说明你的 WSL 内核不在 Ubuntu 软件源支持的版本列表里。这种时候不要硬装直接找/usr/lib/linux-tools/下已有的版本用软链接把可用的usbip链过去。模块加载失败modprobe vhci-hcd: FATAL: Module vhci-hcd not found通常是内核太老或自定义内核没启用 USB/IP。先升级 WSL再试。5.2 attach 后 lsusb 看不到设备设备 attach 成功后lsusb仍然没有可能原因有三个。第一vhci-hcd模块没加载先sudo modprobe vhci-hcd再试第二WSL 发行版里跑了多个实例usbipd attach --wsl选错了发行版或会话可以手动指定发行版名称usbipd attach --wsl --busid 1-2 --distribution Ubuntu第三设备在 Windows 侧没有正常识别usbipd list里显示的是“Unknown USB Device (Device Descriptor Request Failed)”。这种一般是 Windows 驱动问题先去设备管理器更新或重装驱动确认 Windows 能正常看到设备后再 bind。5.3 绑定失败或设备一直处于“Attached”如果执行usbipd bind报错提示被占用多半是设备正被 Hyper-V 或 Windows 驱动独占。尤其是 USB 网卡、声卡这类设备Windows 系统服务会一直占用。可以打开设备管理器找到对应设备右键“禁用设备”然后再 bind或者先把 Windows 侧相关软件彻底退出。usbipd list里设备状态如果一直是Attached说明此前有一次 attach 没有正常 detach。不要急着重新 bind先执行usbipd detach --busid 1-2如果 detach 提示设备不存在检查 Windows 服务管理器里usbipd服务是否正常必要时重启服务。这种“虚拟状态卡住”的情况我遇到好几次重启服务比反复重装工具更管用。5.4 连接后频繁掉线、传输不稳定USB/IP 的连接基于网络WSL2 的 NAT 网络正常情况下很稳定但如果 Windows 的 IP 地址变了比如从 Wi-Fi 切换到有线网络原来 attach 的会话会断掉。手动 attach 时需要重新确认默认网关 IPip route show default | awk {print $3}如果是高带宽设备比如 USB 摄像头、UVC 采集卡USB/IP 的实时性会明显不如直连。因为 vhci 驱动模拟的主控制器有额外的协议开销帧率跟直插 Windows 相比会有下降。这种情况下我建议用网络摄像头走 RTSP或者用 VMware/VirtualBox 这类更完整的虚拟机方案做设备直通而不是硬用 WSL2。5.5 排查速查表现象可能原因解决方法usbipd bind提示权限不足不是管理员运行用管理员 PowerShell/CMDWSL 内usbip不存在工具未安装或路径不对安装linux-tools-generic软链接到/usr/local/sbinmodprobe vhci-hcd失败WSL 内核过旧wsl --update重启 WSLattach 超时Windows IP 变化 / 防火墙拦截检查默认网关放行 TCP 3240lsusb 能看到但无法读写串口权限不足加入dialout组或用 udev 规则设备状态一直 Attached上次 detach 未成功usbipd detach --busid BUSID重启 usbipd 服务键盘鼠标 bind 后失灵HID 设备被共享以最快方式 reboot不要 bind 输入设备6. 替代方案与选型建议6.1 继续用 WSL1对串口最省事如果你的需求只是 USB 转串口没有内核模块和 Docker 的硬性要求其实可以给某个发行版单独设置 WSL1wsl --set-version Ubuntu-20.04 1WSL1 会把 Windows 的 COM 口映射成/dev/ttyS0、/dev/ttyS1程序访问串口时直接走 Windows 驱动。优点是连接稳定、权限简单、不用折腾 USB/IP。缺点是它毕竟不是一个完整的内核很多 Linux 下的串口调试工具、内核级抓包、USB 协议分析功能用不了。适合“只想跑个脚本读串口”的轻量场景。6.2 VirtualHere 等商业工具VirtualHere 是另一种 USB over Network 方案Windows 侧运行服务端WSL2 或局域网内的其他机器运行客户端。它比 usbipd-win 多一些图形化管理功能对某些奇葩设备兼容性更好也支持多客户端同时访问。但它是商业软件免费版有设备数量限制而且它不是微软官方推荐路线配置复杂度也不低。对于绝大多数开发者usbipd-win 已经足够了没必要多花钱。6.3 什么时候该换别的路USB/IP 方案不是万能的。如果你要实时传输视频流、要用 USB 3.0 超高速外设、或者设备对延迟极其敏感建议直接放弃“在 WSL2 里直通 USB”这个念头。这时候可以这样选设备在 Windows 上跑服务WSL2 里通过网络 API 访问或者用一个轻量 Linux 物理机/虚拟机专门处理 USB再或者用支持 PCI/USB 直通的传统虚拟机方案。方案选型要结合你的实际带宽需求和系统复杂度不要为了“能在 WSL2 里 lsusb”而硬上。从我个人的实际体会来说usbipd-win 最适合的场景就是嵌入式调试USB 转串口、STM32 烧录器、JTAG 调试器、USB 网卡这些设备的读写频率不高但需要你在完善的 Linux 环境里跑工具链。一次配置好之后日常体验几乎和原生 Linux 没区别。唯一要养成的习惯是用完记得 detach让设备回到 Windows这样两边都不会出现“设备被谁抢走”的困惑。如果你还没有搭好这套流程建议按文章里的步骤走一遍特别是那个--auto-attach参数配上之后是真的省心。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进