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/idProduct:
lsusb印的10c4:ea60就是 rule 裡ATTRS{idVendor}=="10c4"的比對值。流程恆為:lsusb 找 VID:PID → 寫進 rule。 ATTRvsATTRS(最易錯):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)。流程:Windowsusbipd 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。