WSL 2 访问 USB 设备:用 usbipd-win 共享 J-Link 调试器与串口

WSL 2 访问 USB 设备:用 usbipd-win 共享 J-Link 调试器与串口
乔阳WSL 2 跑在轻量虚拟机里,有自己的 Linux 内核,自然也有自己的 USB 设备树。插在 Windows 上的 J-Link、USB 转串口模块,WSL 2 默认看不到。这不是 bug,是架构使然。要在 WSL 里烧 STM32、看串口日志,得想办法把 Windows 侧的 USB 设备塞进 WSL 2 这台虚拟机。
微软官方给出的答案就是 usbipd-win。它是一个开源项目,在 Windows 上实现 USB/IP 协议服务端,把 USB 设备通过网络转发给 WSL 2 内核。下面记录我用它共享 J-Link 和串口做 STM32 开发的完整流程,以及踩过的几个坑。
为什么 WSL 2 看不到 USB 设备
先说清楚问题来源。WSL 1 和 Windows 共享内核,所以 COM 口能直接映射成 /dev/ttyS*。WSL 2 不一样,它是一个真正的 Hyper-V 轻量级虚拟机,跑着自己独立的 Linux 内核(微软维护的 WSL2 内核)。一个 USB 设备要么挂在 Windows 的驱动上,要么挂在 WSL 内核的驱动上,不能同时挂两边。Windows 拿着设备,WSL 内核的 USB 子系统里就空着。
所以需要一座桥。usbipd-win 干的就是这件事:在 Windows 侧做 USB/IP 服务端,把设备导出;WSL 2 内核里早就编译了 vhci-hcd(虚拟主机控制器驱动),做 USB/IP 客户端,接收过来的设备重新挂到 WSL 的 USB 总线上。
这套机制要求 WSL 内核版本不低于 5.10.60.1,老内核没有 usbipd 客户端支持。
环境准备
- Windows 11(22000 或更高)或 Windows 10(需应用商店版 WSL)
- x64 或 ARM64 处理器,x86 不支持
- WSL 2 已更新到最新,内核 5.10.60.1 或更高
检查 Windows 版本:Win + R 输入 winver。
检查 WSL 内核版本:
1 | # 在 WSL 中执行 |
更新 WSL 内核:
1 | # 在 PowerShell 中执行 |
Windows 10 用户装 Microsoft 应用商店版的 WSL 即可,不用手动编译内核。
安装 usbipd-win
两种方式任选其一。
winget 一行搞定(推荐):
1 | winget install --interactive --exact dorssel.usbipd-win |
或者去 usbipd-win 发布页 下 .msi 手动装。
装完会有三样东西:后台服务 USBIP 设备主机、usbipd 命令行工具(已加入 PATH)、一条放行本地子网的防火墙规则。
共享设备的两步:bind 和 attach
usbipd 的流程分两步,理解了就不会乱。
bind 把设备标记为”可共享”。设备此时仍然归 Windows 用,只是注册到 usbipd 的导出列表里。需要管理员权限,一次绑定永久有效,重启后还在。
attach 真正把设备从 Windows 摘下来,转发给 WSL 2 虚拟机。执行那一刻起,Windows 这边就访问不了该设备了,直到 detach 或物理拔掉。attach 不需要管理员权限,但要求 WSL 正在运行。
查看设备
管理员权限打开 PowerShell,列出所有 USB 设备:
1 | usbipd list |
输出里找到目标设备的 BUSID,比如 4-4。
bind
1 | # 管理员权限 |
再跑一次 usbipd list,状态变成 Shared 就对了。
attach 到 WSL
attach 之前先确保 WSL 终端开着,虚拟机处于运行状态。WSL 闲置太久会休眠,休眠了设备也就没了。
1 | usbipd attach --wsl --busid 4-4 |
在 WSL 里验证:
1 | lsusb |
能看到设备说明转发成功。所有 WSL 2 发行版共享同一个虚拟机,所以 attach 一次,各个发行版都能用。
detach
用完手动分离:
1 | usbipd detach --busid 4-4 |
设备回到 Windows。物理拔掉效果一样。
注意 attach 不是持久的,WSL 重启后要重新 attach,bind 才是持久的。新版 usbipd-win 带 --auto-attach 参数,设备一插上就自动转发,频繁插拔的场景很方便。
权限问题:非 root 访问设备
attach 之后设备虽然出现在 WSL 里,但默认只有 root 能碰。每次 sudo 不现实,正经做法是写 udev 规则。
串口权限
串口设备一般出现为 /dev/ttyUSB0 或 /dev/ttyACM0,属于 dialout 组。把用户加进去:
1 | sudo usermod -a -G dialout $USER |
加完重新登录生效。
通用 udev 规则
针对具体 VID/PID 放权限,这样比较干净:
1 | sudo nano /etc/udev/rules.d/99-usb-device.rules |
参考内容(按实际设备改 VID/PID):
1 | # J-Link 调试器 |
重新加载:
1 | sudo udevadm control --reload-rules |
STM32 开发实战
J-Link 调试器
先在 Windows 侧找到 J-Link:
1 | usbipd list |
bind 加 attach:
1 | usbipd bind --busid 2-3 |
WSL 里验证:
1 | lsusb | grep -i jlink |
J-Link 驱动在 WSL 里要单独装。SEGGER 官网下 Linux 版 JLink_Linux_Vxxx.tgz,解压后 JLinkExe 就能用。Windows 上装的 J-Link Commander 在这里帮不上忙,因为设备已经被转发走,Windows 那边看不见了。
串口
在设备管理器里看 COM 口号辅助定位,然后到 usbipd list 里找对应的 USB Serial Converter / CH340 / CP2102 设备。
1 | usbipd bind --busid 3-2 |
WSL 里:
1 | ls /dev/ttyUSB* /dev/ttyACM* |
长期用还是写 udev 规则,见上一节,免得每次开机都 chmod。
常见坑
设备附加后 WSL 看不到。 先确认 bind 成功(状态 Shared),再确认 WSL 在运行。还不行就重启 usbipd 服务:
1 | # 管理员 PowerShell |
内核版本过低。 uname -a 看 WSL 内核版本,低于 5.10.60.1 的 usbipd 客户端用不了:
1 | wsl --shutdown |
WSL 休眠导致设备掉线。 WSL 空闲一段时间会休眠,attach 的设备跟着断。开发时留一个终端窗口开着,或者用 tmux 维持会话:
1 | sudo apt install tmux |
每次重启都要重新 attach。 bind 是持久的,attach 不是。嫌烦可以用 --auto-attach,或者写个 PowerShell 脚本批量 attach:
1 | # connect-stm32.ps1 |
适用场景
不止 STM32。凡是 WSL 2 默认摸不到的 USB 设备都能用这套:
- J-Link / ST-Link 烧录调试
- USB 转串口看日志
- Arduino、ESP32 刷写
- 逻辑分析仪、USB 示波器
代价是设备在 Windows 和 WSL 之间二选一,attach 期间 Windows 那边用不了。需要两边频繁切换的场景,--auto-attach 配合物理插拔会比较顺手。







