ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux文件I/O与系统调用详解

Linux文件I/O与系统调用详解 摘要在 Linux 操作系统中“一切皆文件”Everything is a file是极其核心的设计哲学。除了普通的文本与二进制文件外网络 Socket、管道、硬件设备如串口、显示屏、触摸屏等在应用层均被抽象为文件进行操作。本文将聚焦于 Linux 系统层面的文件 I/OFile I/O又称系统调用 I/O深入讲解其核心 APIopen、read、write、lseek、close、文件描述符FD的分配机制并深度对比标准 I/O与文件 I/O在实现原理、缓冲区机制及适用场景上的本质区别。一、什么是文件 I/O文件 I/O 是 Linux内核直接提供的文件操作接口属于系统调用System Call。与 ANSI C 标准库提供的标准 I/O如 fopen、fread、fwrite不同文件 I/O 不经过 C 库缓冲直接由内核处理因此实时性更高常用于设备文件操作或底层的网络编程中。标准 I/O vs 文件 I/O 架构对比[file-tag: code-generated-file-fdtablepng]二、关键概念文件描述符File Descriptor, FD在标准 I/O 中我们使用文件流指针 FILE * 来标记打开的文件而在文件 I/O 中内核使用文件描述符fd来表示被打开的文件。1. 文件描述符的本质●文件描述符是一个小的非负整数。●每当进程打开一个文件内核会在进程的“文件描述符表”中分配一个索引并将其返回给应用层。●Linux 操作系统默认可分配的文件描述符范围通常为 0 ~ 1023共 1024 个。2. 系统默认打开的三个文件描述符每个 Linux 进程在启动时默认都已经打开了三个文件描述符分别对应标准 I/O 中的三个流标准 I/O 文件流指针文件 I/O 描述符宏定义unistd.h对应设备stdin0STDIN_FILENO标准输入键盘stdout1STDOUT_FILENO标准输出显示屏/终端stderr2STDERR_FILENO标准错误显示屏/终端3. FD 的分配原则最小未被使用原则操作系统分配 FD 时总是挑选当前列表中最小的、且未被占用的非负整数。[file-tag: code-generated-file-fileioarchitecturepng]三、文件 I/O 核心 API 详解需要包含的头文件#include sys/types.h#include sys/stat.h#include fcntl.h#include unistd.h1. 打开/创建文件open()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: 若文件不存在则创建■O_TRUNC: 打开时清空文件■O_APPEND: 追加写入mode: 当创建新文件指定 O_CREAT时设置权限如 0664。●返回值成功返回文件描述符 (fd)失败返回 -1。标准 I/O 与 文件 I/O 模式对应关系标准 I/O (fopen)文件 I/O 标志 (open flags)说明rO_RDONLY只读文件必须存在rO_RDWR读写文件必须存在wO_WRONLY | O_CREAT | O_TRUNC, 0664只写不存在则创建存在则清空wO_RDWR | O_CREAT | O_TRUNC, 0664读写不存在则创建存在则清空aO_WRONLY | O_CREAT | O_APPEND, 0664只写不存在则创建追加写入aO_RDWR | O_CREAT | O_APPEND, 0664读写不存在则创建末尾追加2. 关闭文件close()int close(int fd);●功能关闭指定的文件描述符释放内核资源。●注意如果程序中打开文件后未进行 close()会导致文件描述符泄漏进程可用的 FD 资源被耗尽。3. 读取文件read()ssize_t read(int fd, void *buf, size_t count);●参数fd: 打开的文件描述符。buf: 存放读取数据的内存首地址。count: 期望读取的字节数。●返回值 0: 实际读取到的字节数。 0: 读到文件末尾EOF。-1: 读取失败。4. 写入文件write()ssize_t write(int fd, const void *buf, size_t count);●参数fd: 打开的文件描述符。buf: 待写入数据的内存首地址。count: 期望写入的字节数。●返回值成功返回实际写入的字节数失败返回 -1。5. 文件读写位置定位lseek()off_t lseek(int fd, off_t offset, int whence);●参数offset: 相对于起始位置的偏移量字节可正可负。whence: 基准位置■SEEK_SET: 文件开头■SEEK_CUR: 当前读写位置■SEEK_END: 文件末尾●返回值成功返回当前读写位置到文件开头的总字节数失败返回 -1。常用实用技巧1.获取文件大小off_t filesize lseek(fd, 0, SEEK_END);2.创建空洞文件抢占磁盘空间int fd open(hole.file, O_WRONLY | O_CREAT | O_TRUNC, 0664);lseek(fd, 1024 * 1024 * 10, SEEK_SET); // 偏移到 10MB 处write(fd, \0, 1); // 写入一个字节形成空洞文件四、实战演练基于文件 I/O 实现文件复制cp以下示例演示如何利用 open / read / write / close 实现高效的文件拷贝工具#include stdio.h#include stdlib.h#include sys/types.h#include sys/stat.h#include fcntl.h#include unistd.hint file_copy(const char *srcfile, const char *dstfile){// 1. 打开源文件只读int fdsrc open(srcfile, O_RDONLY);if (fdsrc -1) {perror(open srcfile error);return -1;}// 2. 打开目标文件只写不存在则创建存在则清空权限0664int fddst open(dstfile, O_WRONLY | O_CREAT | O_TRUNC, 0664);if (fddst -1) {perror(open dstfile error);close(fdsrc);return -1;}// 3. 循环读写char buff[1024] {0};ssize_t size 0;while ((size read(fdsrc, buff, sizeof(buff))) 0) {write(fddst, buff, size);}// 4. 关闭文件描述符close(fdsrc);close(fddst);return 0;}int main(int argc, const char *argv[]){if (argc 3) {printf(Usage: %s srcfile dstfile\n, argv[0]);return -1;}if (file_copy(argv[1], argv[2]) 0) {printf(File copy successfully!\n);}return 0;}五、文件 I/O 与 标准 I/O 全方位对比对比维度标准 I/O (Standard I/O)文件 I/O (File I/O)操作对象FILE *文件流指针int fd文件描述符实现层级ANSI C 标准库函数高层抽象Linux 内核系统调用底层接口可移植性高支持 POSIX、Windows、macOS 等弱特定于 POSIX / Linux 系统缓冲区机制有缓冲区行缓冲、全缓冲、无缓冲减少内核切换提高小数据频繁读写效率无用户缓冲区实时性高适用场景普通文本/二进制文件的顺序/格式化读写设备文件串口、网络 Socket、驱动硬件补充标准 I/O 的三种缓冲区模式1.行缓冲Line Buffered大小通常为1024 字节 (1KB)。常见于终端设备stdin、stdout。刷新条件遇到 \n、缓冲区满、程序结束或调用 fflush()。2.全缓冲Fully Buffered大小通常为4096 字节 (4KB)。常见于普通磁盘文件操作。刷新条件缓冲区满、关闭文件、程序结束或调用 fflush()。3.无缓冲Unbuffered数据直接写入目的地如标准错误流 stderr。六、辅助拓展函数格式化与字符串截取在 Linux C 编程中结合文件操作常常用到以下辅助函数1. 格式化输出函数●fprintf(FILE *stream, const char *format, ...)向指定流写入格式化数据。●dprintf(int fd, const char *format, ...)向指定文件描述符写入格式化数据。●sprintf(char *str, const char *format, ...)格式化输出到字符串数组中用于字符串拼接与格式转换。2. 字符串截取strtok()char *strtok(char *str, const char *delim);●功能根据分隔符 delim 切割字符串 str。●注意strtok 会修改原字符串将分隔符替换为 \0首次调用传 str后续继续切割需传 NULL。总结1.Linux 下一切皆文件硬件设备、文件、管道、Socket 均可使用文件 I/O 统一操作。2.文件 I/O 返回并使用的是文件描述符fd分配遵从最小未被使用原则。3.进行普通文件读写优先考虑标准 I/O含缓冲区减少系统调用开销而在硬件交互、设备驱动、实时性要求高的场景下则应使用文件 I/O。
返回列表