【Linux】系统级文件I/O与文件描述符深度剖析

目录

[1. 引言](#1. 引言)

[2. 系统调用接口](#2. 系统调用接口)

[2.1 open 函数](#2.1 open 函数)

[2.2 write 与 read](#2.2 write 与 read)

[2.3 close](#2.3 close)

[3. 文件描述符(File Descriptor, fd)](#3. 文件描述符(File Descriptor, fd))

[3.1 默认打开的三个文件描述符](#3.1 默认打开的三个文件描述符)

[3.2 文件描述符的本质](#3.2 文件描述符的本质)

[3.3 文件描述符分配规则](#3.3 文件描述符分配规则)

[4. 重定向的原理](#4. 重定向的原理)

[4.1 输出重定向示例](#4.1 输出重定向示例)

[4.2 使用 dup2 系统调用](#4.2 使用 dup2 系统调用)

[5. 小结](#5. 小结)


1. 引言

C标准库的 fopen 等函数最终调用的是系统调用。本文直接使用系统调用接口 open、read、write、close 进行文件操作,并重点讲解文件描述符的概念、分配规则以及重定向的底层原理。

2. 系统调用接口

2.1 open 函数

c

复制代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

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_APPEND(追加)、O_TRUNC(截断)。

  • mode:当使用 O_CREAT 时,指定新文件的权限(如 0644),会受到进程 umask 的影响。可通过 umask(0) 临时清除掩码。

返回值: 成功返回一个非负整数------文件描述符;失败返回 -1。

2.2 write 与 read

c

复制代码
ssize_t write(int fd, const void *buf, size_t count);
ssize_t read(int fd, void *buf, size_t count);
  • fd:文件描述符。

  • 返回值:成功返回实际读/写的字节数,出错返回 -1。

2.3 close

c

复制代码
int close(int fd);

关闭文件描述符,释放相关资源。

3. 文件描述符(File Descriptor, fd)

3.1 默认打开的三个文件描述符

Linux进程启动时,默认打开三个文件描述符:

  • 0:标准输入(stdin)

  • 1:标准输出(stdout)

  • 2:标准错误(stderr)

它们对应的物理设备通常是键盘和显示器。我们可以直接使用 read(0, buf, size) 从键盘读入,使用 write(1, buf, len) 输出到屏幕。

3.2 文件描述符的本质

文件描述符是一个小整数 ,它是进程打开文件表的索引。内核为每个进程维护一个 files_struct 结构体,其中包含一个指针数组 fd_array[],每个元素指向一个 struct file 对象(表示一个打开的文件)。文件描述符就是这个数组的下标。

3.3 文件描述符分配规则

规则: 在当前进程中,fd_array[] 中找到最小的未被使用的下标,分配给新打开的文件。

验证实验:

  • 正常打开一个文件,输出 fd 通常为 3(因为0、1、2已被占用)。

  • 先调用 close(0) 或 close(2),再打开文件,则新文件的 fd 将成为 0 或 2。

4. 重定向的原理

4.1 输出重定向示例

c

复制代码
close(1);                // 关闭标准输出
int fd = open("myfile", O_WRONLY | O_CREAT, 0644);
printf("fd: %d\n", fd);  // 这里 fd 为 1
fflush(stdout);

现象: printf 的内容没有出现在屏幕上,而是写入了 myfile 文件。这是因为 close(1) 释放了下标1,随后 open 分配了最小的可用下标1,于是文件描述符1现在指向了 myfile 对应的 struct file。printf 底层向 stdout(即 fd=1)写入数据,数据便进入了文件。

重定向的本质: 改变文件描述符下标对应的 struct file 指针,使其指向不同的打开文件。

4.2 使用 dup2 系统调用

dup2 可以更便捷地实现重定向:

c

复制代码
#include <unistd.h>
int dup2(int oldfd, int newfd);

功能:让 newfd 指向 oldfd 所指向的文件,如果 newfd 已打开,则先关闭它。

示例:实现输出重定向

c

复制代码
int fd = open("log", O_CREAT | O_RDWR, 0644);
dup2(fd, 1);   // 将标准输出重定向到 fd 对应的文件
printf("这条消息将写入 log 文件\n");

同理,输入重定向使用 dup2(fd, 0),追加重定向使用 O_APPEND 标志打开文件后再 dup2。

5. 小结

本文详细介绍了系统级文件I/O接口、文件描述符的概念、分配规则以及重定向的底层原理。下一篇我们将把这些知识应用到之前实现的迷你Shell中,为Shell添加完整的重定向功能。

相关推荐
未济3 天前
linux 配置环境变量
linux
傲世仙尊3 天前
目录即文件-Ext文件系统收尾篇
linux·c语言
虎头金猫3 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
_艾伦 耶格尔.3 天前
进程间通信
linux
AI职业加油站3 天前
AI智能体应用工程师证书:政策红利下的职业新风口
大数据·运维·人工智能·学习·职场发展
Liuqy-053 天前
Linux IO编程——静态库、动态库
linux
此冬歌咏3 天前
K8s 节点故障实战:优雅驱逐 31 秒,硬故障 331 秒,以及那个永远 Pending 的 Pod
运维·k8s
彧azz3 天前
Linux 环境下 Redis 学习总结:数据类型、持久化、锁、事务、主从与缓存问题
linux·redis·笔记·学习·面试
-梅3 天前
linux(8) 软硬链接
linux·运维·服务器
Wang's Blog3 天前
Java 项目实战: 外卖平台-文件下载与ServletOutputStream回写浏览器
服务器·项目开发