
开头前阵子有个朋友跑来问我说他在 WSL2 里编译好了嵌入式工具链结果插上 USB 转串口的小板子lsusb里空空如也折腾半天怎么也读不到/dev/ttyUSB0。这个问题几乎每一个认真用过 WSL2 的人都会撞上——WSL2 本质上是一个跑在轻量虚拟机里的 Linux 内核Windows 的 USB 驱动栈跟它之间隔着一堵墙设备插在宿主机上Linux 侧理论上根本看不见。但这绝不意味着没得玩。串口调试、ADB、U 盘挂载、开发板烧录、USB 协议抓包这些我在 WSL2 里全部实际跑通过靠的是一个叫 usbipd-win 的开源小工具以及 USB/IP 这套协议。这篇文章我把自己摸出来的完整流程、踩过的坑、还有不同场景下的剁手建议一次性写清楚希望能帮你在 WSL2 里把 USB 设备用起来。整个过程不复杂但坑点特别多比如为什么 attach 之后 Windows 侧设备消失、为什么lsusb明明看到了设备却没有串口节点、为什么有些设备怎么 bind 都没反应。这些内容官方文档写得很简略网上中文资料又七零八落我按自己的实操经验重新整理了一遍。无论你是刚装好 WSL2 准备做嵌入式开发还是已经受够了 Windows 原生驱动折磨的老手这篇都值得往下看。1. 为什么WSL2访问USB这么“麻烦”——架构差异与解决思路1.1 WSL1和WSL2的底层架构差异很多人在第一步就把原因搞错了。WSL1 用的是系统调用翻译层Linux 程序发的系统调用在 Windows 内核里被“翻译”成 NT 调用这种方式下设备访问的体验其实更接近原生——Windows 里能看到的 COM 口、USB 设备WSL1 里也能直接找到因为底层就是同一个驱动栈。但 WSL1 在文件系统性能、内核兼容性上有不少短板尤其是跑 Docker 和需要完整内核模块的负载时非常吃力。WSL2 的架构完全不同它把真正的 Linux 内核跑在 Hyper-V 隔离层里通过 9P 文件协议和虚拟网卡跟 Windows 通信。内核是独立的一套Windows 的设备驱动栈它一概不认所以 USB 设备插在宿主机上WSL2 里就是物理隔离的状态。这也是为什么 WSL2 的性能更强、内核模块更全代价却是外设直通变得曲折。简单类比WSL1 像是“远程操作”别人家的电脑Windows 装了驱动你就能用WSL2 像是“搬了新家”跟旧房子只有一条网线相连家里的电器USB设备留在旧房子里新家自然摸不到只能想办法把电器“搬过来”。这个比喻能帮你理解后面所有操作的逻辑。1.2 三条可行路线对比usbipd、共享文件夹、网络转发既然物理隔离那想用 USB 设备靠什么我试过三条路结论很明确第一条直接把设备作为一个网络服务暴露过来。这就是 USB/IP 协议做的事也是 usbipd-win 的核心方案。Windows 侧把 USB 设备“共享”出来WSL2 内核里的 vhci_hcd 虚拟主机控制器把设备“挂”进来。这是目前最通用、性能最接近真实的方案也是本文的主角。第二条Windows 侧共享文件夹绕开 USB 本身。比如你插了一个 U 盘直接在 Windows 里把 U 盘目录通过/mnt/c共享给 WSL2 读。这个方案对新文件系统exFAT、NTFS和移动硬盘可用但只有块级或文件级访问设备特性完全丧失。比如你根本没法控制 USB 设备的引脚、边带信号、复位时序遇到串口、HID、ADB 这些设备就完全没辙。第三条通过网络转发。如果 USB 设备本身带网络能力或者你只是想把数据转发到 Linux 侧用 socat、TCP 中转这类方式处理。但有严重局限性只适用于数据流型设备而且配置繁琐延迟还高不推荐日常使用。结论很清楚想在 WSL2 里获得接近原生的 USB 体验usbipd-win 就是事实标准。后面我用大篇幅讲它。2. 工具选型复盘usbipd-win 凭啥成为标准答案2.1 USB/IP协议与WSL内核内置支持先聊原理这能帮你避掉后面一堆莫名其妙的报错。USB/IP 协议最早是 Linux 内核里的一套远程设备共享方案思想非常简单设备插在 A 机器上A 机器通过内核模块把 USB 请求/响应封装成 TCP 包发出去B 机器上的 vhci_hcd 驱动接收这些包在自己的 USB 子系统里“模拟”出一个虚拟主机控制器再枚举出设备端点。对用户空间来说这就是一块真实的 USB 总线lsusb、libusb、ttyUSB0全都能正常工作。WSL2 的官方内核从 5.10.60 几个版本之后就开始内置vhci_hcd和usbip客户端支持这意味着你不需要自己在 WSL 里编译内核模块更新 WSL 内核就自带这个能力。很多教程让你在 WSL 里apt install linux-tools或者手动编译 usbip 工具那是远古方案。现代 WSL2 用户真正要做的只是安装 Windows 侧的 usbipd-win然后在 PowerShell 里执行两三条命令。我再补一句原理层面的关键点usbipd-win 在 Windows 侧实现了一个过滤器驱动把设备的驱动栈“接管”下来。设备一旦被 attach 到 WSLWindows 侧原有的驱动会暂时卸载设备的控制权整体移交到 Linux 内核。这个机制决定了你在 WSL 里对设备做任何操作都是真实的 USB 传输不是文件拷贝那种模拟行为所以用它来跑时间敏感型场合串口波特率、HID 轮询也没问题。2.2 安装与前置条件速查安装分两步Windows 侧装 usbipd-winWSL2 侧基本不用动。我建议你按下面的环境要求先自查一遍别漏项项目要求备注Windows 版本Windows 10 1809 以上Windows 11 均可推荐 Windows 11网络栈更稳定WSL 版本必须 WSL2wsl -l -v查看老版本先升级WSL 内核5.10.60新版自动更新wsl --update保持最新PowerShell管理员权限bind/list 部分命令需要管理员usbipd-win4.x 及以上直接用 winget 安装安装命令一行就够了在管理员 PowerShell 里执行winget install usbipd如果 winget 找不到包就去 GitHub 搜 usbipd-win 项目下载最新的 MSIX 或 MSI 安装包手动装。安装完成后Windows 会多一个叫 usbipd 的服务默认开机自启。这里有个我踩过的坑早期版本安装完以后服务不会立刻识别设备必须重启一次 PowerShell 或者注销重登。如果你装完发现usbipd list连设备都列不出来大概率不是没装对而是操作端权限不对。窗口标题上没有“管理员”两个字list 命令经常正常但 bind 一定会报 Access Denied。这个我后面在排查章节还会细说。3. 5分钟接入USB设备从绑定到附加的完整操作3.1 管理员权限下的绑定bind接上一个常见使用场景你插了一个 USB 转 TTL 串口板子CH340/CP210x 这类想在 WSL2 里访问它。第一步管理员 PowerShell 里先列出所有 USB 设备usbipd list输出大概长这样BUSID VID:PID DEVICE STATE 3-1 1a86:7523 USB-Serial CH340 (COM3) Not attached 3-3 0bda:0316 USB 3.0 Card Reader Not attached先解释一下这个输出里的几个字段。BUSID是设备在 Windows 设备树上的位置比如3-1表示 USB 控制器 3 下的端口 1这个 id 之后的所有命令都要用到。VID:PID是设备厂商 ID 和产品 ID用来精确定位设备。STATE里有Not attached、Attached、Bound几种状态理解它们特别重要。现在执行绑定usbipd bind --busid 3-1绑定是什么意思通俗讲就是告诉 Windows这个设备从此刻起允许被 USB/IP 共享。执行完以后设备的STATE会变成Attached或者Bound并且 Windows 的设备管理器里会在设备旁边多出一个 USBIP 相关的虚构节点。需要注意的是bind 并不等于设备已经被 WSL 使用Windows 自己仍然可以正常访问它只是一个“授权”动作。如果你想插上设备以后自动 attach 到 WSL2可以用--auto-attachusbipd bind --busid 3-1 --auto-attach这个参数适合那些每天都要插拔的开发板调试工具省去每次手敲 attach 命令的麻烦。但我提醒一句auto-attach 只对“新插入”时刻生效插着之后再改参数不会立刻触发需要重新插拔一次。3.2 挂载到WSL2attach与验证bind 完成以后真正的重头戏是 attach。这一步把设备从 Windows 侧“拔下来”并插到 WSL2 的虚拟 USB 总线上。命令如下usbipd attach --wsl --busid 3-1如果系统里有多个发行版可以用--wsl发行版名指定usbipd attach --wslUbuntu-22.04 --busid 3-1执行成功以后你在 Windows 侧肉眼可见的变化是设备管理器里原来的 USB 设备消失了多出一个未知设备或者直接没有显示。别慌这是正常现象——设备已经被“物理”移交到 WSL 侧了。Windows 端的驱动会短暂卸载如果你在 Windows 侧开着串口软件占用了设备attach 会失败并提示设备忙先把占用程序关掉再执行。回到 WSL2 终端先验证有没有识别lsusb如果看到类似这一行说明设备已经进来了Bus 001 Device 002: ID 1a86:7523 QinHeng Electronics CH340 serial converter再看串口节点ls /dev/ttyUSB0 /dev/ttyACM0看到/dev/ttyUSB0就大功告成。这里有个细节CH340/CP210x 这类 USB-UART 芯片通常生成/dev/ttyUSB0而 CDC ACM 类设备比如很多 Arduino 板载 USB生成/dev/ttyACM0两者命名来源不同调试时别只盯着 ttyUSB 找。3.3 用完记得归还detach操作很多人第一次 attach 成功以后用完了直接把 USB 线拔了或者关掉 WSL结果下次插上设备发现 Windows 识别不到或者状态一直怪怪的。正确做法是用完以后在 Windows 侧执行 detach让设备从 WSL2 里“拔出来”归还给 Windows 驱动栈。usbipd detach --busid 3-1执行完这个命令设备管理器里设备会重新出现Windows 的驱动开始正常工作。我建议在流程上养成习惯日常开发板调试可以保持 bind 状态一直开着但每次用完必须 detach。如果你用 auto-attach即使系统重启后 WSL 没启动设备也可能被自动绑定这时候插上设备Windows 会出现“设备未被识别”的情况执行一次 detach 就好。提示detach 之前最好确保 WSL 侧已经卸载了相关文件系统或关闭了占用设备的进程。比如 U 盘挂载在/mnt/usb直接 detach 可能导致缓存数据丢失正确顺序是sync sudo umount /mnt/usb再回 Windows 执行 detach。4. 实测有效的几种典型USB场景4.1 USB转串口TTL/Console嵌入式调试必备这是我在 WSL2 里用得最多的场景。用 USB 转 TTL 接路由器、嵌入式开发板、交换机 console 口以前得在 Windows 下装驱动、开超级终端、选 COM 口偶尔还跟其他软件抢设备。现在直接 usbipd attach 进来WSL2 里就想干嘛干嘛。设备 attach 之后串口节点通常是/dev/ttyUSB0直接上 minicom 或 screensudo minicom -D /dev/ttyUSB0 -b 115200或者用 screensudo screen /dev/ttyUSB0 115200退出 screen 用CtrlA然后按K这个跟串口本身没关系但新手常卡在这。我实际测试过 CH340、CP2102、FT232 三种最常见的 USB-UART 芯片工作都很稳定波特率 115200 和 921600 都没丢包。如果输出乱码多半是波特率不对或者电平不匹配跟 USB passthrough 无关。有一个细节值得单独说有些 USB 转串口芯片比如 FT232R在 Windows 侧有驱动锁如果你 bind 的时候设备正在被某个软件占用attach 会超时。我建议把 Windows 侧干扰软件先关掉比如某个串口监控工具、厂商烧录工具再执行 attach。4.2 U盘与移动硬盘传文件比共享文件夹还方便WSL2 访问 Windows 文件系统虽然可以用/mnt/c但性能差强人意尤其是大量 IO 的场景。USB 存储设备直通以后WSL2 里直接操作原生块设备读写性能比 9P 协议高一个量级。流程跟前面一样先 bind 再 attach。看到设备在 lsusb 里出现后查一下块设备编号lsblk假设 U 盘显示为sdb挂载命令sudo mkdir -p /mnt/usb sudo mount /dev/sdb1 /mnt/usbFAT32/exFAT/U 盘默认能直接挂载NTFS 需要 ntfs-3g一般发行版默认就有。移动硬盘如果有多个分区sdb1、sdb2分别挂载即可。这里我提醒几个坑第一U 盘拔掉之前必须在 Linux 侧umount否则文件系统缓存写不回去第二有些带硬件加密的 U 盘比如 SanDisk 的加密闪存盘在 WSL2 里可能只有块设备但无法解锁因为加密逻辑依赖 Windows 驱动这类设备就不要直通了第三有些 USB 3.0 读卡器在 attach 后只有 USB 2.0 速度这是 vhci_hcd 模拟控制器的固有限制测过多次读取速度在 30 MB/s 上下能接受就用不能接受就回到 Windows 侧操作。4.3 Android手机ADB调试解决设备授权问题把 Android 手机通过 USB 连到 Windows然后 WSL2 里跑 adb这个需求在移动开发、CTF、刷机爱好者里很常见。流程是一样的插好手机、打开 USB 调试然后在管理员 PowerShell 里usbipd list找到对应总线 ID。注意一点手机可能同时暴露很多 USB 接口MTP、ADB、RNDISusbipd list里会看到好几行。你 bind 的一定要是标着Android或者ADB的那个设备普通 MTP 接口 bind 进去了也没意义。attach 成功以后在 WSL2 里执行adb kill-server adb devices如果列表能看到设备但显示unauthorized说明 WSL2 里的 adb 第一次连接需要在手机屏幕上确认 RSA 指纹授权。如果授权完还是识别不了检查一下你 WSL2 里的 adb 版本老版本对上新手机容易出问题建议升级到最新版 platform-tools。还有一个很常见的坑Windows 上装过手机厂商助手或者旧版驱动会把设备的 VID 绑定成奇怪的东西导致 usbipd list 里看不到 ADB 接口。这时候去 Windows 设备管理器把该设备卸载再重新插拔一次通常就好了。4.4 系统烧录与固件升级RK等开发板开发板烧录这个场景usbipd-win 给了 WSL2 一个很大的优势很多厂商的烧录工具只有 Linux 版在 Windows 下要么跑虚拟机、要么装双系统现在直接在 WSL2 里烧录就行。我自己实测过 Rockchip 系列开发板比如 RK3566、RK3588 小板子的操作流程先在 Windows 侧把设备 bind再 attach 到 WSL2然后使用rkdeveloptool工具列出设备、下载 loader、写入固件。整个过程在 WSL2 里跑得很顺烧录速度和 Windows 原生没有明显差别。流程示意一下# WSL2 中确认设备 lsusb # 列出设备 rkdeveloptool ld # 烧录 update.img rkdeveloptool uf update.img要注意的是烧录过程比较长中途千万别在 Windows 侧打开设备管理器或者给设备通电重启否则会中断传输。另外一些烧录工具会自己操作内核模块比如加载 USB 驱动或者重置控制器如果你发现 attach 之后工具枚举不到设备先确认内核模块工作正常sudo dmesg | grep -i usb看看有没有报错。常见的是 USB 设备在 attach 后被识别为“不可用的 device descriptor”这种一般和设备固件有关换个 USB 口从 3.0 接口换到 2.0 接口经常能解决。4.5 Linux侧USB协议抓包usbmonUSB 协议分析是很多硬件玩家和 CTF 选手的需求。WSL2 里设备直通以后你可以直接用 Linux 的 usbmon 机制抓取 USB 总线上跑的请求和响应数据这对理解设备协议、逆向固件、排查通信问题非常有用。操作分三步。先加载 usbmon 模块sudo modprobe usbmon然后确认设备对应的总线编号比如lsusb显示Bus 001 Device 002那总线就是 1对应的抓包接口通常是/dev/usbmon1。用 tshark 或者 Wireshark 直接抓sudo tshark -i usbmon1 -w /tmp/usb_capture.pcap也可以用带图形界面的 WiresharkWSLg 支持运行 Linux GUI 程序wireshark -i usbmon1抓包里能看到 USB 描述符请求、控制传输、中断传输、批量传输的各种细节。我拿一个 HID 键盘设备试过WSL2 抓到的事件流跟 Windows 侧的分析仪结果基本一致只是多了虚拟控制器的枚举开销不影响协议层分析。如果你想看更底层的物理信号那就得上硬件分析仪了软件方案覆盖不了电气层。5. 常见问题排查与避坑实录5.1 attach报错 Access Denied拒绝访问怎么办这个报错我见得最多90% 的原因是 PowerShell 不是以管理员身份运行。usbipd bind和usbipd attach都要求管理员权限因为要操作驱动栈。解决办法很简单右键 PowerShell 图标选“以管理员身份运行”再执行命令。还有一种情况bind 的时候是管理员但 attach 的时候换了普通窗口也会失败。把 bind、attach、detach 三个操作都统一放在管理员 PowerShell 里执行养成习惯就少踩坑。如果管理员权限确认了还报 Access Denied检查一下这个设备是不是已经被别的会话绑定了。比如另一个 WSL 发行版或者 Docker Desktop 里的 Linux 虚拟机占了设备先在那边 detach再回来 attach。5.2 加了设备但 lsusb 不出现attach 命令已经提示成功但 WSL2 里lsusb死活看不到这种情况我排查过三次原因基本是内核模块没加载。WSL2 官方内核默认编译了vhci_hcd但部分老版本内核或者自定义内核可能缺失执行一下lsmod | grep vhci modprobe vhci-hcdmodprobe vhci-hcd如果报Module not found那就说明当前内核没有这个模块。解决方案是把 WSL 内核更新到最新版wsl --update更新完以后wsl --shutdown重启 WSL再重新 attach 一次。如果还是不行看内核版本uname -r低于5.10.60的版本usbip 支持确实不完整强制更新 WSL 组件即可。还有一个隐蔽情况你用--wsl发行版名attach 到了 A 发行版但你现在看的是 B 发行版。思路理清attach 指定了哪个发行版就去哪个发行版里看设备。5.3 busid 漂移与自动attachWindows 的设备总线 ID 并不是一成不变的。同一个 U 盘插在 USB 2.0 口和 USB 3.0 口usbipd list里的 BUSID 可能从3-1变成3-2拔掉重插也可能从3-1变成4-1。你写死--busid 3-1的命令换了个口就失效。解决办法有两条。一条是每次都先usbipd list重新看一次 busid手动 attach。另一条是 bind 的时候加--auto-attach这样设备一插入服务会自动判断并 attach 到默认 WSL 发行版不用手动指定 busid。usbipd bind --busid 3-1 --auto-attach不过 auto-attach 也有副作用如果你同时插了多个设备可能出现 attach 顺序不对的问题比如手机和 U 盘同时插入auto-attach 全给你绑了然后你需要手动 detach 不想用的那个。适合固定设备、固定口的场景流动设备还是手动 bind 稳妥。5.4 USB设备在Windows侧突然消失这是 attach 成功的直观表现但很多人不知道这是正常的。设备在 Windows 侧消失是因为整个设备被移交给了 WSL2 的虚拟控制器Windows 驱动栈认为设备已经拔掉了。WSL2 里用完以后执行 detachWindows 侧设备就会重新出现。如果 detach 之后设备还是不出来可以试试在管理员 PowerShell 里禁用再启用 USB 控制器或者直接重启 Windows基本都能恢复。这个问题在 usbipd-win 的 issue 区经常有人提属于驱动栈残留的问题不影响正常使用但拔插频繁的时候会让人心里发毛。5.5 特殊设备驱动与内核模块补充不是所有 USB 设备 attach 到 WSL2 后都能直接工作。原因分两类设备需要专有驱动或者设备类别特殊。专有驱动型比如某些 USB 无线网卡Realtek 8811CU/8812CU 这类芯片在 Linux 侧需要对应的驱动模块。即使 attach 成功内核没有驱动lsusb 能看到 VID/PID但没有wlan0接口。这时候需要自己装驱动、编译模块或者用内核自带的兼容驱动。这不是 usbip 的问题是 Linux 侧驱动生态的问题。设备类别特殊型最有代表性的是 USB hub。attach 一个 hub 进来只是让 WSL2 看到这个 hub 本身hub 下面挂的鼠标键盘并不会自动透传。要逐个 attach 末端设备。另一个坑是 USB 音频设备和摄像头这两个在 vhci_hcd 上有时候同步有问题会出现断流SDK 越老的设备越明显。如果是追求低延迟的音频场景WSL2 并不适合这个要提前有心理预期。设备类型WSL2直通体验推荐方案USB 转串口CH340/CP210x/FT232稳定放心用U盘/移动硬盘稳定但速度上限约30MB/s大文件拷贝建议Windows侧处理Android ADB稳定注意授权和驱动版本USB 无线网卡依赖Linux驱动先查内核模块USB HID键鼠能用但意义不大保持Windows侧使用USB音频设备偶尔断流不建议直通USB hub无法递归透传逐个attach子设备结尾我在 WSL2 里已经把 USB 透传这套方案用了将近一年中间也换过 VirtualBox 直通、Windows 侧驱动模拟这些歪路最后还是稳定在 usbipd-win 上。最大的体会是这套工具链一旦跑通开发效率提升真的非常明显——串口调试、开发板烧录、ADB 抓日志全在一个终端窗口里搞定Windows 侧那些驱动冲突、蓝屏问题基本绕开了。不过我也要坦率地说它并不是万能的性能上比原生 PCIe 直通有差距特殊设备也偶尔要折腾。最后再送你一个实用小建议第一次搭环境的时候别用最重要的设备试拿一个便宜的 USB 转串口板子把 bind/attach/detach 整个流程跑通再换别的设备这样即使出问题也不心疼排查起来也更快。