深入理解 Linux 文件 IO 与目录 IO:系统调用与库函数的本质区别

一、标准 IO vs 文件 IO:库函数与系统调用的本质区别

今天系统学习了 Linux 下的文件 IO 和目录 IO,这一块和标准 IO 最大的区别就是:标准 IO 是库函数,文件 IO 是系统调用

标准 IO(stdio.h)

  • 性质:属于 C 库函数,是对系统调用的封装。
  • 特点:自带用户层缓冲区,可移植性好,适合普通文件读写。
  • 局限:不适合直接操作设备文件、网络通信等场景。

文件 IO(unistd.h、fcntl.h)

  • 性质 :属于 Linux 系统调用,直接和内核打交道。
  • 特点无用户层缓冲区,移植性差(与操作系统强相关)。
  • 适用场景:操作硬件、串口、网络设备等底层资源。

一句话记住:库函数封装系统调用;系统调用是内核给用户的接口。

二、四大文件 IO 函数:open / close / read / write / lseek

1. open - 打开或创建文件

c 复制代码
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
  • pathname:文件路径
  • flags 常用标记
    • O_RDONLY:只读
    • O_WRONLY:只写
    • O_RDWR:读写
    • O_CREAT:文件不存在就创建,必须给第三个参数 mode 权限
    • O_EXCL:文件存在则报错,常和 O_CREAT 搭配使用
    • O_TRUNC:文件存在则清空内容
    • O_APPEND:追加写入
  • 返回值 :成功返回文件描述符 fd (最小可用的非负整数),失败返回 -1

三个默认文件描述符

  • 0:标准输入 stdin
  • 1:标准输出 stdout
  • 2:标准错误 stderr

标准 IO 与文件 IO 打开模式对照表

标准 IO 模式 文件 IO 等效 flags
"r" O_RDONLY
"r+" O_RDWR
"w" `O_WRONLY
"w+" `O_RDWR
"a" `O_WRONLY
"a+" `O_RDWR

2. write - 写入数据

c 复制代码
ssize_t write(int fd, const void *buf, size_t count);
  • 往文件描述符 fd 指向的文件中写入 count 个字节
  • 数据来源:缓冲区 buf
  • 返回值:成功返回实际写入的字节数,出错返回 -1

3. read - 读取数据

c 复制代码
ssize_t read(int fd, void *buf, size_t count);
  • 从文件描述符 fd 指向的文件中读取最多 count 个字节
  • 数据存储:存入缓冲区 buf
  • 返回值:
    • 成功:返回实际读取的字节数
    • 文件结束:返回 0
    • 出错:返回 -1

4. lseek - 移动文件读写位置

c 复制代码
off_t lseek(int fd, off_t offset, int whence);
  • 移动文件读写偏移量(文件指针)
  • whence 参数
    • SEEK_SET:从文件开头偏移 offset
    • SEEK_CUR:从当前位置偏移 offset
    • SEEK_END:从文件末尾偏移 offset
  • 返回值:成功返回偏移后距离文件开头的总偏移量,失败返回 (off_t)-1

经典练习 :使用 open-read-write 组合实现图片拷贝功能。

三、目录 IO(遍历文件夹核心)

头文件:<dirent.h>

1. opendir - 打开目录

c 复制代码
DIR *opendir(const char *name);
  • 打开指定目录,返回目录流指针 DIR*
  • 失败返回 NULL

2. closedir - 关闭目录

c 复制代码
int closedir(DIR *dirp);
  • 关闭目录流
  • 成功返回 0,失败返回 -1

3. readdir - 读取目录项

c 复制代码
struct dirent *readdir(DIR *dirp);
  • 读取目录中的下一项
  • 返回 struct dirent 指针,包含目录项信息
  • 读到末尾或出错时返回 NULL

重点成员

  • d_name[256]:文件/目录名字符串

实际应用 :上周遍历 ~/Music 目录筛选音视频文件,就是用 opendir + readdir 循环读取文件名,然后判断后缀名。

4. chdir & getcwd - 工作目录操作

  • 获取当前工作目录

    c 复制代码
    char *getcwd(char *buf, size_t size);
  • 修改进程当前工作目录

    c 复制代码
    int chdir(const char *path);

四、易错点与注意事项

  1. open 权限问题

    • 使用 O_CREAT 标志时,一定要传递 mode 权限参数,否则创建的文件权限可能不符合预期。
  2. 文件描述符分配原则

    • 遵循最小可用分配原则:0、1、2 被占用后,新打开的文件会依次分配 3、4、5...
  3. 缓冲区差异

    • 文件 IO 没有用户层缓冲区write 调用直接进入内核
    • 标准 IO 有缓冲区,需要 fflush 或文件关闭时才真正落盘
  4. read 返回值理解

    • 返回 0:表示已到达文件末尾(EOF),不是错误
    • 返回 -1:表示读取过程中发生错误
  5. 目录遍历注意事项

    • readdir 会读取到当前目录(.)和父目录(..
    • 遍历时记得跳过这两个特殊目录项,避免无限循环或逻辑错误

五、总结

Linux 文件 IO 和目录 IO 是系统编程的基础,理解它们与标准 IO 的区别至关重要:

  • 文件 IO 是底层系统调用,直接与内核交互,适合需要精细控制的场景
  • 标准 IO 是高层库函数,提供缓冲和便捷接口,适合普通应用开发
  • 目录 IO 提供了遍历文件系统的标准方法,是许多工具和应用程序的基础

掌握这些系统调用,不仅能编写更高效的程序,还能深入理解 Linux 系统的工作机制。

相关推荐
qetfw1 小时前
Debian OpenLDAP 目录服务配置:DN、LDIF、ldapsearch 与认证验证
linux·运维·debian
词却1 小时前
Linux基础:Linux环境搭建与远程连接
linux
不悔哥1 小时前
Linux 小电视:稚晖君的开源桌面终端,从 6 层板到 Quark
linux
qq_349447952 小时前
在ollama下部署DeepSeek-r1模型(linux机器)
linux·运维·服务器
hehelm2 小时前
仿muduo库实现高并发服务器—Channel类
linux·服务器·开发语言·网络·c++
末代iOS程序员华仔2 小时前
Codex + Figma 生成 Objective‑C (UIKit) 完整工作流
c语言·开发语言·figma
小此方2 小时前
Linux加餐(二):藏在Linux中的设计模式(二)单例模式与线程池
linux·单例模式·设计模式
猫吃了源码2 小时前
Linux系统制作 Ubuntu Server 24.04 启动U盘详细教程
linux·服务器·ubuntu
hehelm2 小时前
仿muduo库实现高并发服务器—EventLoop
linux·服务器·网络·c++