在 Linux 环境下,“万物皆文件”(Everything is a file)是核心哲学。访问硬件本质上就是通过 C 语言对 /dev/ 目录下的设备文件进行读写操作。
通常有两种主要路径:系统调用(System IO)和标准库(Standard IO)。
Linux 的做法是: 把这些差异全部封装在内核的驱动程序里。驱动程序负责把硬件的电平信号、协议转换成“流”。 对于 C 程序员来说,只需要学习一套
open/read/write/close,就能控制几乎所有的硬件。
这是最常用、最直接的方式。它直接与内核交互,没有应用层缓冲区,适合对实时性要求高或需要特殊控制(如控制引脚、设置波特率)的硬件。
open(): 打开设备文件。read() / write(): 接收/发送数据。ioctl(): 硬件控制的关键。用于执行非标准的文件操作(如设置串口参数、控制电机转速、读取传感器寄存器)。close(): 关闭设备。C
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdio.h>
int main() {
// 1. 打开设备文件
int fd = open("/dev/my_hardware_device", O_RDWR);
if (fd < 0) {
perror("打开设备失败");
return -1;
}
// 2. 写入数据(例如控制 LED 亮)
char buf[] = {0x01};
write(fd, buf, sizeof(buf));
// 3. IOCTL 控制(例如设置硬件模式)
// IOCTL_SET_MODE 是驱动程序定义的宏
// ioctl(fd, IOCTL_SET_MODE, arg);
// 4. 关闭设备
close(fd);
return 0;
}
使用 stdio.h 中的函数。它在系统调用之上增加了一个用户态缓冲区。
fopen() / fclose()fread() / fwrite()fprintf() / fscanf() (常用于访问 Sysfs 接口)在现代 Linux(如树莓派、嵌入式开发)中,很多硬件被映射为 /sys/class/ 下的文本文件。由于这些接口是基于文本的,使用标准 IO 会非常方便。
C
// 示例:通过 sysfs 修改 LED 亮度
FILE *fp = fopen("/sys/class/leds/led0/brightness", "w");
if (fp) {
fprintf(fp, "%d", 255); // 写入亮度值
fclose(fp);
}
| 特性 | 系统 IO (open/read) | 标准 IO (fopen/fread) |
|---|---|---|
| 缓冲区 | 无(直接进内核) | 有(减少系统调用次数) |
| 实时性 | 较高,响应快 | 较低,受缓存影响 |
| 功能性 | 支持 ioctl 等底层控制 |
仅限流式读写 |
| 硬件场景 | 字符设备、原始数据流、驱动交互 | 配置文件、Sysfs 虚拟文件 |
对于需要高速访问硬件(如显存、高速采样、大块 DMA 缓冲区)的情况,读写文件的效率可能不够。此时会使用 mmap()。
mmap 将设备文件直接映射到进程的地址空间。你可以像操作普通 C 语言数组/指针一样直接操作硬件寄存器,跳过了数据在内核态和用户态之间的多次拷贝。
/dev/ 下的文件通常需要 root 权限或特定的用户组(如 dialout 组用于串口)。open 时可以使用 O_NONBLOCK 标志。如果硬件数据没准备好,读取操作会立即返回而不是等待。您是想针对具体的硬件(如串口 UART、I2C 传感器、还是 GPIO)进行编程吗? 如果是的话,我可以为您提供更具针对性的 ioctl 参数说明或代码模板。