一句话主线:免 sudo 访问硬件,不是"放开权限",而是把"谁能碰这个设备"显式建模成 Linux 组。
设备树管"节点存不存在"、udev 管"节点归谁/权限多少"、组管"用户算不算自己人"——三层各司其职,缺一层都白搭。
0. 适用场景
- 服务账号(如
pipeline3d)访问ttyUSB/i2c/spidev/gpiochip,报Permission denied或access-fail
- 不想给业务进程 root,也不愿每条命令都
sudo
- 目标平台:i.MX8 等嵌入式 Linux(Yocto / Buildroot / Debian rootfs 均适用)
1. 第一性原理:组 = 硬件访问的授权单位
Linux 把"对某类资源的访问权"打包成 组(group)。设备节点带一个属组,只要用户属于该组,就能按权限位读写——无需 root。
三层授权模型:
层 | 负责回答 | 由谁决定 |
资源 | 设备节点存不存在( /dev/i2c-3…) | 设备树 + 内核驱动 |
属组 | 节点归哪个组、权限几位 | udev 规则 |
成员 | 用户算不算"自己人" | usermod -aG |
udev 在做什么:
记住这条链,串口 / i2c / spi / gpio 乃至任何字符设备,都是同一套打法。
2. 入门案例:串口与 dialout
2.1 dialout 是什么
- 名字来源:早年通过 modem「拨号外联(dial out)」访问串口,组因此得名。
- 管什么:udev 把这些节点的属组设为
dialout—— /dev/ttyS*(板载串口)/dev/ttyUSB*(USB 转串口,如 CP210x / FTDI)/dev/ttyACM*(USB CDC,如 Arduino、部分调试器)
- 典型权限:
crw-rw---- root dialout,即只有 root 与dialout成员可读写。
dialout 是 Debian/Ubuntu 系惯例。Arch 及部分发行版用 uucp 管串口。换平台前先 ls -l 看真实属组,别默认。2.2 诊断五连
先在板子上跑这套,定位到底是"权限"还是"占用"问题:
命令 | 含义 | 作用 |
id pipeline3d | 打印 UID/GID 及所属附加组 | 确认账号当前在哪些组(重点看有无 dialout) |
groups pipeline3d | 只列出所属组名 | 与上条互证,更直观 |
ls -l 设备节点 | 看权限位、属主、属组;吞掉报错、永不中断脚本 | 判断设备归哪个组、权限是否 rw-rw---- |
sudo -u 测试 -r/-w | 以 pipeline3d 身份实测可读可写 | 最关键:把"猜权限"变成"实测权限" |
fuser -v 设备 | 列出正在占用该设备的进程 | 排查"被别的进程独占",与权限问题区分 |
2.3 修复 + 生效
usermod -a -G dialout:把账号追加进 dialout 组。
systemctl restart:进程的组身份在启动时确定,不重启进程,新组权限不生效——这正是要重启服务的原因。
2.4 两个坑
-a不能漏:usermod -G(无-a)会清空其它附加组。
- dialout 不一定是真实属组:若
ls -l显示设备属组是tty/uucp/ 自定义组,加进 dialout 就是无效操作。先看属组,再决定加哪个组。
3. 常见 Linux 组速查
类别 | 组名 | 授权访问 |
设备 | dialout | 串口 / modem(ttyUSB、ttyACM、ttyS) |
设备 | tty | 终端设备 /dev/tty* |
设备 | audio / video | 声卡 /dev/snd、摄像头/帧缓冲 /dev/video、/dev/fb |
设备 | i2c / spi / gpio | 嵌入式总线设备(本文重点) |
设备 | plugdev | 可热插拔设备(U 盘等) |
设备 | kvm / render | /dev/kvm、GPU 渲染节点 /dev/dri/render* |
提权 | sudo(Debian系)/ wheel(RHEL/Arch系) | 允许用 sudo 提权 |
提权 | adm | 读系统日志 /var/log/*(只读排障常用) |
服务 | docker | 访问 Docker daemon(≈ root,慎加) |
服务 | www-data / nginx | Web 服务运行身份 |
网络 | netdev | 管理网络接口 |
历史 | uucp | 历史 UUCP;部分发行版用它管串口 |
自查命令(比背列表有用):
4. 进阶战场:i.MX8 上 gpio / i2c / spi 免 sudo
套路恒定三步:建组 → 写 udev 规则 → 加组重登。
4.1 第一步:建组并加用户
加组后必须重新登录(或重启服务),当前会话不会自动刷新组身份。
4.2 第二步:写 udev 规则
写入
/etc/udev/rules.d/99-embedded.rules:4.3 第三步:重载并验证
4.4 GPIO 的两套接口(最易卡住)
新接口(推荐) | 旧接口(已废弃) | |
路径 | /dev/gpiochip*(字符设备) | /sys/class/gpio/(sysfs export) |
工具 | libgpiod:gpioget / gpioset / gpiomon | echo N > export 手动导出 |
udev 能否搞定 | ✅ 上面那条规则直接生效 | ⚠️ 麻烦,见下 |
强烈建议直接上 libgpiod(新接口),i.MX8 内核早已支持,sysfs 接口在 5.x 起标记 deprecated。
若被迫用旧 sysfs:
echo N > export 会动态新建 gpioN 目录且属主 root,普通 GROUP= 抓不住后生成的子节点,需脚本 hook:%p 是 udev 占位符(设备 sysfs 路径)。即便如此 export 新引脚仍可能有竞态——这就是该迁移到 chardev 的理由。4.5 i.MX8 特定三坑
- 先确认 init 是 systemd-udev 还是 busybox mdev。很多 NXP BSP / Buildroot 镜像用 mdev,那
/etc/udev/rules.d/根本不被读取,得改/etc/mdev.conf:
判断:
ps | grep -E 'udevd|mdev'。- 设备树要先 enable 对应节点。
/dev/i2c-3、/dev/spidev1.0不会凭空出现——dts 里&i2c3/&ecspi*须status = "okay";spidev 还需子节点声明 compatible(新内核对 spidev 直接绑定会 warn,可能要加白名单)。没节点,udev 规则无的放矢。
- 只读 rootfs(Yocto):若
/etc只读,规则要打进 image recipe(recipes-core/udev-rules),而非运行时写入。
5. 通用 Checklist
免 sudo 五步自检:
- 建组
groupadd -f <grp>
- 写规则
/etc/udev/rules.d/*.rules(确认是 udev 而非 mdev)
- 重载
udevadm control --reload-rules && udevadm trigger
- 加组
usermod -aG <grp> <user>(-a别漏),重登或重启服务
- 验证
ls -l看属组/权限 + 以目标用户实测-r/-w
一句话收尾:设备树管"存不存在",udev 管"归谁/权限",组管"算不算自己人"。三层模型记牢,权限问题不再靠 sudo 硬扛。
- Author:felixfixit
- URL:http://www.felixmicrospace.top/article/linux_device_permission
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!










