Linux 系统 IO 编程入门:从文件操作到硬件设备访问

在 Linux 系统开发中,"一切皆文件" 的哲学贯穿始终,无论是普通文本还是硬件设备,都可以通过统一的文件操作接口进行交互。本文将从基础概念出发,详细讲解 Linux 系统 IO 的核心函数与使用方法,帮助初学者快速掌握文件操作与硬件设备访问的精髓。

一、Linux 系统开发核心范畴

Linux 系统开发主要围绕四大核心模块展开,这些模块共同构成了嵌入式与服务器端应用开发的基础:

  1. 硬件设备访问:通过内核接口间接控制硬件(如显示器、串口),是嵌入式开发的核心
  2. 多任务处理:基于进程、线程等机制实现并发程序设计
  3. 网络编程:开发基于 TCP/IP、UDP 等协议的网络应用
  4. 数据库编程:通过数据库接口实现数据的存储与管理

对于系统 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 通过统一的文件操作接口,实现了对普通文件和硬件设备的一致访问。核心要点包括:

  1. 掌握 "打开 - 操作 - 关闭" 的基本流程
  2. 理解文件描述符的作用与生命周期
  3. 熟悉核心函数(open/read/write/mmap 等)的参数与返回值
  4. 利用 mmap 实现高效的设备操作

通过本文的学习,相信你已经对 Linux 系统 IO 有了初步认识。实际开发中,还需要注意错误处理、资源释放等细节,多动手实践才能真正掌握这一核心技术。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值