可行,但设备文件行为由驱动决定,非普通文件:如/dev/random会阻塞,/dev/mem需root权限,/dev/input/event*返回二进制结构,须按协议解析,否则出现乱码、eperm或read返回0等错误。

直接用open()读取/dev/下的设备文件可行吗
可行,但必须清楚设备文件不是普通文件——它背后是内核驱动提供的接口,行为由驱动决定。比如/dev/random会阻塞,/dev/mem需要root权限且受CONFIG_STRICT_DEVMEM限制,/dev/input/event*返回的是struct input_event二进制流,不能当文本读。
常见错误现象:read()返回0(设备已EOF)、-1并报EPERM或EACCES、读到乱码(没按协议解析二进制结构)。
- 先确认权限:
ls -l /dev/xxx,必要时用sudo setfacl -m u:$USER:r /dev/xxx或加用户到dialout/input组 - 用
strace -e trace=open,read,ioctl ./your_program看系统调用是否被拒绝 - 不要用
fstream或std::ifstream——它们默认按文本模式处理换行和EOF,对设备文件不可靠
ioctl()在读取设备状态时为什么比read()更常用
很多设备不支持“流式读取”,而是通过ioctl()交换控制信息。例如/dev/ttyS0串口需先用TCGETS获取当前参数,/dev/input/event0需EVIOCGID查设备ID,/dev/i2c-1必须用I2C_RDWR发读写消息。
关键点:ioctl命令码是设备特定的,必须包含对应头文件(如<linux></linux>、<linux></linux>),且需用_IOR/_IOW宏生成。
-
int fd = open("/dev/input/event0", O_RDONLY); ioctl(fd, EVIOCGID, &id)才能拿到设备类型和厂商ID - 忘记
memset(&req, 0, sizeof(req))清零结构体,可能导致内核拒绝执行ioctl - 某些ioctl(如
MEMGETREGIONCOUNT)返回值是负数表示错误,不能只判== 0
读/sys/class/或/proc/里的虚拟文件算不算“读设备文件”
算,而且更安全、更常用。这些是内核暴露的只读接口,无需特殊权限(多数情况下),返回纯文本,适合快速获取状态。比如/sys/class/power_supply/BAT0/capacity读电量,/proc/cpuinfo读CPU信息。
注意它们不是真实文件系统,不支持seek()、stat()可能返回0 size,且内容可能随时变化。
- 用
std::ifstream读/sys/路径完全OK,但记得.rdbuf()->in_avail()不可靠,应读到eof()为止 -
/sys/class/backlight/intel_backlight/brightness这类路径在不同硬件上名字不同,需先opendir("/sys/class/backlight")枚举 - 别把
/proc/sys/当只读——写入它会修改内核参数,比如echo 1 > /proc/sys/net/ipv4/ip_forward
用libudev替代手动遍历/dev/的好处在哪里
硬编码/dev/sda或/dev/ttyUSB0极易出错——热插拔后设备名会变,UDEV规则可能重命名。libudev通过监听内核netlink事件,提供稳定、可过滤的设备发现机制。
它不帮你读数据,但能可靠定位目标设备节点路径,再配合open()或ioctl()操作。
- 初始化:调用
udev_new(),然后udev_enumerate_new()+udev_enumerate_add_match_subsystem(udev, "tty") - 匹配条件要具体:仅用
subsystem不够,加上udev_enumerate_add_match_property(e, "ID_VENDOR_ID", "0403")才准确定位FTDI设备 - 别忘了
udev_device_get_devnode()返回的是const char*,且设备拔出后指针立即失效
真正的难点不在打开设备,而在理解每个设备节点背后的驱动模型和通信协议——/dev下的每个文件都是一个契约,你得按它的规则来,而不是按文件系统的直觉来。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











