在 Linux 系统开发中,"一切皆文件" 的哲学贯穿始终,无论是普通文本还是硬件设备,都可以通过统一的文件操作接口进行交互。本文将从基础概念出发,详细讲解 Linux 系统 IO 的核心函数与使用方法,帮助初学者快速掌握文件操作与硬件设备访问的精髓。
一、Linux 系统开发核心范畴
Linux 系统开发主要围绕四大核心模块展开,这些模块共同构成了嵌入式与服务器端应用开发的基础:
- 硬件设备访问:通过内核接口间接控制硬件(如显示器、串口),是嵌入式开发的核心
- 多任务处理:基于进程、线程等机制实现并发程序设计
- 网络编程:开发基于 TCP/IP、UDP 等协议的网络应用
- 数据库编程:通过数据库接口实现数据的存储与管理
对于系统 IO 而言,我们重点关注的是硬件设备访问与文件操作的统一逻辑。
二、文件操作基础概念
2.1 文件的定义与类型
在 Linux 中,文件不仅包括普通数据文件,还包括硬件设备的抽象表示。主要类型有:
| 类型标识 | 说明 | 示例 |
|---|---|---|
- | 普通文件 | 源码文件、可执行程序 |
d | 目录文件 | /home、/etc |
l | 链接文件 | 快捷方式 |
c | 字符设备 | 键盘、串口(/dev/ttyS0) |
b | 块设备 | 硬盘、U 盘(/dev/sda1) |
p | 管道文件 | 进程间通信临时文件 |
s | 套接字文件 | 网络通信文件(/var/run/docker.sock) |
2.2 文件描述符(FD)
文件描述符是 Linux 中标识已打开文件 / 设备的非负整数,本质是内核文件记录表的索引。程序启动时会自动打开三个标准描述符:
STDIN_FILENO = 0:标准输入(键盘)STDOUT_FILENO = 1:标准输出(终端)STDERR_FILENO = 2:标准错误(终端)
所有文件操作都通过文件描述符进行,需通过open()等函数获取,使用完毕后必须关闭以释放资源。
2.3 文件权限
Linux 文件权限分为所有者、组用户和其他用户三类,每类包含读(r)、写(w)、执行(x)权限,对应八进制值分别为 4、2、1。例如:
rw-r--r--表示:所有者可读写,组用户和其他用户仅可读rwxr-xr-x表示:所有者拥有全部权限,组用户和其他用户可读写执行
三、系统 IO 核心函数详解
系统 IO 是直接调用内核接口的文件操作方式,无缓冲机制,适合对硬件设备的精准控制。核心函数如下:
3.1 access:检查文件存在性与权限
用于操作前验证文件是否存在或是否拥有指定权限:
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char**argv) {
if(argc < 2) {
printf("用法: %s <文件路径>\n", argv[0]);
return -1;
}
// 检查文件是否存在
if(access(argv[1], F_OK) == -1) {
perror("文件不存在或无权限");
return -1;
}
// 检查是否有读权限
if(access(argv[1], R_OK) == 0) {
printf("拥有读权限\n");
}
return 0;
}
3.2 open:打开或创建文件
打开已有文件或创建新文件,返回文件描述符:
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main() {
// 以读写方式打开显示器设备
int fd = open("/dev/fb0", O_RDWR);
if(fd == -1) {
perror("打开设备失败");
return -1;
}
// 创建新文件(权限:所有者读写,其他只读)
int file_fd = open("test.log", O_WRONLY | O_CREAT, 0644);
if(file_fd == -1) {
perror("创建文件失败");
close(fd);
return -1;
}
close(fd);
close(file_fd);
return 0;
}
flags参数关键选项:
- 必选项:
O_RDONLY(只读)、O_WRONLY(只写)、O_RDWR(读写) - 附加项:
O_CREAT(创建文件)、O_APPEND(追加模式)、O_TRUNC(清空文件)
3.3 read/write:读写数据
从文件 / 设备读取或写入数据:
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
int main() {
int fd = open("test.txt", O_RDWR | O_CREAT, 0644);
if(fd == -1) {
perror("打开失败");
return -1;
}
// 写入数据
const char* data = "Hello Linux IO";
write(fd, data, strlen(data));
// 移动到文件开头
lseek(fd, 0, SEEK_SET);
// 读取数据
char buf[128] = {0};
read(fd, buf, sizeof(buf)-1);
printf("读取内容: %s\n", buf);
close(fd);
return 0;
}
3.4 lseek:移动文件指针
调整读写位置,实现随机访问或获取文件大小:
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char**argv) {
if(argc < 2) return -1;
int fd = open(argv[1], O_RDONLY);
if(fd == -1) {
perror("打开失败");
return -1;
}
// 获取文件大小(将指针移到末尾)
off_t size = lseek(fd, 0, SEEK_END);
printf("文件大小: %ld 字节\n", size);
// 移到文件中间位置
lseek(fd, size/2, SEEK_SET);
close(fd);
return 0;
}
3.5 mmap:高效文件映射(零拷贝技术)
将文件 / 设备直接映射到进程内存,操作内存即操作文件,大幅提升效率:
#include <fcntl.h>
#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main() {
// 打开文件
int fd = open("map_test.txt", O_RDWR | O_CREAT, 0644);
if(fd == -1) { perror("open"); return -1; }
// 写入初始内容
const char* init = "初始内容";
write(fd, init, strlen(init));
// 获取文件大小
int size = lseek(fd, 0, SEEK_END);
// 建立内存映射
char* map_addr = (char*)mmap(
NULL, // 系统自动分配地址
size, // 映射大小
PROT_READ | PROT_WRITE, // 读写权限
MAP_SHARED, // 修改同步到文件
fd, 0 // 文件描述符和偏移量
);
if(map_addr == MAP_FAILED) { perror("mmap"); return -1; }
// 操作内存即操作文件
printf("原内容: %s\n", map_addr);
strcpy(map_addr, "映射修改");
// 解除映射
munmap(map_addr, size);
close(fd);
return 0;
}
四、硬件设备访问实践
以显示器设备(/dev/fb0)为例,演示如何通过系统 IO 控制硬件:
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <stdio.h>
int main() {
// 打开显示器设备
int fd = open("/dev/fb0", O_RDWR);
if(fd == -1) {
perror("无法打开显示器");
return -1;
}
// 获取屏幕参数
struct fb_var_screeninfo info;
ioctl(fd, FBIOGET_VSCREENINFO, &info);
int width = info.xres; // 宽度
int height = info.yres; // 高度
int bpp = info.bits_per_pixel / 8; // 每像素字节数
// 计算帧缓冲大小并映射
int size = width * height * bpp;
int* lcd_mem = (int*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(lcd_mem == MAP_FAILED) {
perror("映射失败");
close(fd);
return -1;
}
// 填充红色(RGB格式:0x00FF0000)
for(int i = 0; i < width * height; i++) {
lcd_mem[i] = 0x00FF0000;
}
// 清理资源
munmap(lcd_mem, size);
close(fd);
return 0;
}
五、总结
Linux 系统 IO 通过统一的文件操作接口,实现了对普通文件和硬件设备的一致访问。核心要点包括:
- 掌握 "打开 - 操作 - 关闭" 的基本流程
- 理解文件描述符的作用与生命周期
- 熟悉核心函数(open/read/write/mmap 等)的参数与返回值
- 利用 mmap 实现高效的设备操作
通过本文的学习,相信你已经对 Linux 系统 IO 有了初步认识。实际开发中,还需要注意错误处理、资源释放等细节,多动手实践才能真正掌握这一核心技术。
1121

被折叠的 条评论
为什么被折叠?



