Context

整理一份 survey:Linux 如何看見並操作 USB 裝置,以及如何把 USB device 掛進 Docker 容器。動機是 robotics 場景常需要在容器內存取 USB 序列裝置(感測器、Arduino/ESP32、相機),而熱插拔常出問題。

Key Insights

  • Linux 的 USB 鏈路usbcore 列舉 → 依 class/ID 載入 driver(usb-storage→block、cdc_acm/ftdi_sio→tty)→ 產生 /dev 節點 → **udev(使用者空間)**負責命名、權限與自動化。儲存類用 mount,序列類用 tty + dialout 群組。
  • 穩定命名靠 udev rules:綁 VID/PID 或序號做固定 SYMLINK,避免 ttyUSB0 編號隨插拔漂移。/dev/serial/by-id/ 是現成的穩定路徑。
  • Docker 掛 USB 的本質 = 兩件事:把 device node 放進容器 /dev device cgroup 開白名單。--device 一次做到兩者(最小權限,生產推薦)。
  • 熱插拔的坑全來自「udev 不在容器內跑」--device 綁的是啟動當下的 inode,重插後失效。解法:掛父目錄(-v /dev:/dev/dev/bus/usb)+ --device-cgroup-rule='c <major>:* rmw',或主機端 udev symlink。
  • 三方案權衡--device(安全、不支援熱插拔)/--privileged -v /dev:/dev(方便但隔離全失,僅開發)/--device-cgroup-rule + /dev/bus/usb(可控 + 支援熱插拔 + libusb,robotics 推薦)。

lsusb ↔ udev rules 的關聯

  • 兩者看的是 sysfs 的同一份資料,只是用途不同:lsusb 是「」(查 kernel 已列舉的 USB 清單),/etc/udev/rules.d/ 是「」(udevd 收到 uevent 時的動作劇本)。udev 不主動掃 USB,被動等事件。
  • 橋是 idVendor/idProductlsusb 印的 10c4:ea60 就是 rule 裡 ATTRS{idVendor}=="10c4" 的比對值。流程恆為:lsusb 找 VID:PID → 寫進 rule。
  • ATTR vs ATTRS(最易錯):sysfs 是樹,USB 父節點才有 idVendor,driver 建的子節點(ttyUSB/sdX)沒有。綁 VID/PID 要用 有 S 的 ATTRS{}(往父層找)KERNELS/SUBSYSTEMS 同理。
  • udevadm info -a /dev/ttyUSB0 是接起兩端的橋接工具:分段印出每一層可用的 KERNELS/ATTRS,直接照含 idVendor/serial 的父層段落抄進 rule。

Windows / WSL2 延伸

  • WSL2 = Hyper-V 輕量 VM 裡的真 kernel,所有發行版共用同一 kernel,預設看不到 Windows 的 USB。WSL1 完全不支援 USB 直通。
  • 過繼標準方案:微軟官方 usbipd-win(USB/IP)。流程:Windows usbipd bind --busid <id>(admin,一次性)→ usbipd attach --wsl --busid <id> → WSL2 內出現 /dev/ttyUSB0 等 → Docker --device 照常掛。拔插/重開機後需重新 attach(--auto-attach)。
  • 最大坑:Docker Desktop(WSL2 backend)容器跑在 docker-desktop 發行版,USB 直通常不穩;建議在 WSL2 發行版裡裝原生 Docker Engine,attach 到該發行版即可乾淨使用。
  • 有些晶片需手動 modprobe ftdi_sio / cdc_acm / usb_storage;儲存類若只取檔案用 /mnt/<drive> (drvfs) 更簡單,不必走 usbip。

Connections