Linux 文件IO 目录IO

一、文件 IO(系统调用级 IO)

文件 IO 是 Linux 内核提供的系统调用接口 ,无用户层缓存,直接操作内核态文件描述符,相比标准 IO 更贴近底层,适合操作设备文件、管道、套接字等特殊文件,也可操作普通文件。

1. 文件 IO 与标准 IO 的核心区别

这是开发中选择接口的关键,两者对比如下:

表格

特性 标准 IO 文件 IO
接口类型 库函数(封装系统调用) 系统调用(内核接口)
缓存机制 有用户层缓存(全 / 行) 无用户层缓存
操作句柄 文件流指针FILE * 文件描述符int fd(非负整数)
适用文件 普通文件(ASCII / 二进制) 普通文件、设备文件、管道、套接字等
效率 普通文件读写效率高 特殊文件操作更高效

2. 核心系统调用接口

文件 IO 的核心接口为open/close/read/write/lseek,所有接口需包含头文件<unistd.h><fcntl.h>,文件描述符是其核心操作句柄(内核分配的非负整数,默认最小未使用)。

(1)open - 打开 / 创建文件,获取文件描述符
复制代码
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
  • 功能 :打开指定路径文件,若文件不存在且指定O_CREAT则创建,返回唯一的文件描述符;
  • 参数
    • pathname:文件路径(绝对 / 相对);
    • flags:打开模式(必选 + 可选组合),必选:O_RDONLY(只读)、O_WRONLY(只写)、O_RDWR(读写);可选:O_CREAT(创建)、O_TRUNC(清空)、O_APPEND(追加);
    • mode:创建文件时的权限(如06640777),仅flagsO_CREAT时有效,最终权限受umask掩码影响;
  • 返回值 :成功返回文件描述符(≥0);失败返回-1,设置错误码。

常用模式组合

复制代码
O_RDONLY;                  // 只读打开
O_WRONLY | O_CREAT | O_TRUNC; // 只写,不存在创建,存在清空
O_RDWR | O_CREAT | O_APPEND;  // 读写,不存在创建,存在追加
(2)close - 关闭文件描述符,释放资源
复制代码
int close(int fd);
  • 功能:关闭已打开的文件描述符,释放内核资源,避免资源泄漏;
  • 参数fd:要关闭的文件描述符;
  • 返回值 :成功返回0;失败返回-1
(3)read - 从文件描述符读取数据
复制代码
ssize_t read(int fd, void *buf, size_t count);
  • 功能 :从fd指向的文件中读取count字节数据,存入buf缓冲区;
  • 参数fd:文件描述符;buf:数据缓冲区;count:期望读取的字节数;
  • 返回值 :成功返回实际读取的字节数 ;读到文件末尾返回0;失败返回-1
(4)write - 向文件描述符写入数据
复制代码
ssize_t write(int fd, const void *buf, size_t count);
  • 功能 :将bufcount字节数据写入fd指向的文件;
  • 参数 :与read一致;
  • 返回值 :成功返回实际写入的字节数 ;失败返回-1
(5)lseek - 重定位文件描述符的偏移量
复制代码
off_t lseek(int fd, off_t offset, int whence);
  • 功能 :类似标准 IO 的fseek,设置文件读写的偏移量,实现随机读写;
  • 参数
    • offset:偏移量(可正可负,正数向文件末尾,负数向文件开头);
    • whence:偏移基准,SEEK_SET(文件开头)、SEEK_CUR(当前位置)、SEEK_END(文件末尾);
  • 返回值 :成功返回新的偏移量 ;失败返回-1

3. 系统默认文件描述符

Linux 启动进程时,会默认打开 3 个文件描述符,供进程与终端交互:

  • 0:标准输入stdin → 对应终端输入;
  • 1:标准输出stdout → 对应终端输出;
  • 2:标准错误stderr → 对应终端错误输出。

4. 文件 IO 实用示例

示例 1:read+write 实现文件拷贝(支持任意文件,如图片 / 视频)
复制代码
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>

#define BUF_SIZE 4096 // 缓冲区大小,与页大小一致,提升效率

int main(int argc, char *argv[]) {
    if (argc != 3) {
        printf("Usage: %s src_file dest_file\n", argv[0]);
        exit(1);
    }
    // 打开源文件(只读)、目标文件(只写,不存在创建,存在清空)
    int fd_src = open(argv[1], O_RDONLY);
    int fd_dst = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0664);
    if (fd_src == -1 || fd_dst == -1) {
        perror("open fail");
        exit(1);
    }
    // 读写拷贝
    char buf[BUF_SIZE] = {0};
    ssize_t n = 0;
    while ((n = read(fd_src, buf, BUF_SIZE)) > 0) {
        write(fd_dst, buf, n); // 按实际读取的字节数写入
    }
    // 关闭文件描述符
    close(fd_src);
    close(fd_dst);
    return 0;
}
示例 2:lseek 计算文件大小
复制代码
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s filename\n", argv[0]);
        return 1;
    }
    int fd = open(argv[1], O_RDONLY);
    if (fd == -1) {
        perror("open fail");
        return 1;
    }
    // 偏移到文件末尾,返回值即为文件总字节数
    off_t file_size = lseek(fd, 0, SEEK_END);
    printf("File size: %ld bytes\n", file_size);
    close(fd);
    return 0;
}

二、目录 IO(专门处理目录文件)

Linux 中目录是特殊的文件 ,普通的文件 IO 无法直接遍历、读取目录内容,需使用专门的目录 IO 接口 (头文件<dirent.h><sys/stat.h>),核心实现目录的打开、关闭、遍历、创建、删除等操作。

1. 核心目录 IO 接口

目录 IO 的操作句柄是目录流指针DIR * ,核心接口分为目录操作opendir/closedir/readdir)和目录管理mkdir/rmdir/chdir/getcwd)两类。

(1)目录操作核心接口
opendir - 打开目录,获取目录流指针
复制代码
DIR *opendir(const char *name);
  • 功能:打开指定路径的目录文件,返回目录流指针;
  • 参数name:目录路径(绝对 / 相对);
  • 返回值 :成功返回DIR *;失败返回NULL
closedir - 关闭目录流指针
复制代码
int closedir(DIR *dirp);
  • 功能:关闭目录流指针,释放内核资源;
  • 参数dirp:已打开的目录流指针;
  • 返回值 :成功返回0;失败返回-1
readdir - 读取目录项信息
复制代码
struct dirent *readdir(DIR *dirp);
  • 功能 :逐行读取目录流中的目录项 (目录下的文件 / 子目录),每次调用返回下一个目录项,读到末尾返回NULL
  • 参数dirp:目录流指针;
  • 返回值 :成功返回struct dirent *(目录项结构体);失败 / 读到末尾返回NULL

核心结构体struct dirent(存储目录项信息):

复制代码
struct dirent {
    ino_t          d_ino;     // 节点号
    off_t          d_off;     // 目录项偏移量
    unsigned short d_reclen;  // 结构体长度
    unsigned char  d_type;    // 文件类型(普通文件/目录/链接等)
    char           d_name[256];// 文件名/目录名(核心)
};
(2)目录管理核心接口
mkdir - 创建目录
复制代码
int mkdir(const char *pathname, mode_t mode);
  • 功能 :创建指定路径的目录,权限由mode指定(如0777);
  • 参数pathname:目录路径;mode:目录权限;
  • 返回值 :成功返回0;失败返回-1
rmdir - 删除空目录
复制代码
int rmdir(const char *pathname);
  • 功能 :删除指定的空目录(非空目录需先删除内部文件);
  • 返回值 :成功返回0;失败返回-1
chdir - 切换当前进程的工作目录
复制代码
int chdir(const char *path);
  • 功能 :类似 Shell 命令cd,切换进程的当前工作目录;
  • 返回值 :成功返回0;失败返回-1
getcwd - 获取当前工作目录的绝对路径
复制代码
char *getcwd(char *buf, size_t size);
  • 功能 :将当前进程的工作目录绝对路径存入buf缓冲区;
  • 参数buf:存储路径的缓冲区;size:缓冲区大小;
  • 返回值 :成功返回buf首地址;失败返回NULL

2. 目录 IO 实用示例

示例 1:遍历指定目录下的所有文件 / 子目录
复制代码
#include <stdio.h>
#include <dirent.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s dirname\n", argv[0]);
        exit(1);
    }
    // 打开目录
    DIR *dp = opendir(argv[1]);
    if (dp == NULL) {
        perror("opendir fail");
        exit(1);
    }
    // 遍历目录项
    struct dirent *p = NULL;
    while ((p = readdir(dp)) != NULL) {
        // 过滤.和..(当前目录和上级目录)
        if (strcmp(p->d_name, ".") == 0 || strcmp(p->d_name, "..") == 0) {
            continue;
        }
        printf("文件名:%s\n", p->d_name);
    }
    // 关闭目录流
    closedir(dp);
    return 0;
}
示例 2:获取 / 切换当前工作目录
复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define BUF_SIZE 256

int main() {
    char buf[BUF_SIZE] = {0};
    // 获取当前工作目录
    getcwd(buf, BUF_SIZE);
    printf("当前工作目录:%s\n", buf);
    // 切换工作目录到/home
    if (chdir("/home") == -1) {
        perror("chdir fail");
        exit(1);
    }
    // 再次获取
    getcwd(buf, BUF_SIZE);
    printf("切换后工作目录:%s\n", buf);
    return 0;
}
相关推荐
小白同学_C9 小时前
Lab4-Lab: traps && MIT6.1810操作系统工程【持续更新】 _
linux·c/c++·操作系统os
今天只学一颗糖9 小时前
1、《深入理解计算机系统》--计算机系统介绍
linux·笔记·学习·系统架构
青云计划9 小时前
知光项目知文发布模块
java·后端·spring·mybatis
赶路人儿10 小时前
Jsoniter(java版本)使用介绍
java·开发语言
探路者继续奋斗10 小时前
IDD意图驱动开发之意图规格说明书
java·规格说明书·开发规范·意图驱动开发·idd
不做无法实现的梦~11 小时前
ros2实现路径规划---nav2部分
linux·stm32·嵌入式硬件·机器人·自动驾驶
消失的旧时光-194311 小时前
第十九课:为什么要引入消息队列?——异步系统设计思想
java·开发语言
yeyeye11111 小时前
Spring Cloud Data Flow 简介
后端·spring·spring cloud
A懿轩A11 小时前
【Java 基础编程】Java 面向对象入门:类与对象、构造器、this 关键字,小白也能写 OOP
java·开发语言
乐观勇敢坚强的老彭12 小时前
c++寒假营day03
java·开发语言·c++