【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 等函数最终调用的是系统调用。本文直接使用系统调用接口 openreadwriteclose 进行文件操作,并重点讲解文件描述符的概念、分配规则以及重定向的底层原理。

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_RDONLYO_WRONLYO_RDWR 之一,可与其他标志按位或,如 O_CREAT(不存在则创建)、O_APPEND(追加)、O_TRUNC(截断)。

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

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

2.2 writeread

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 将成为 02

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 fileprintf 底层向 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添加完整的重定向功能。

相关推荐
万联WANFLOW23 分钟前
SD-WAN 控制平面高可用怎么做?SDWAN 控制器挂了,全网会发生什么
运维·网络·分布式·架构
酱学编程1 小时前
【从零到一实现一个 AI Agent 框架 · 第四篇】04. 任务规划:拆解复杂目标 -
服务器·网络·数据库·人工智能
SkyWalking中文站2 小时前
认识 Horizon UI · AI Assistant:用日常语言查询你的可观测性数据
运维·监控·自动化运维
艾莉丝努力练剑3 小时前
OpenCode AI 编程:Ubuntu 24.04 环境安装与使用指南
linux·服务器·网络·人工智能·tcp/ip·ubuntu
崇山峻岭之间3 小时前
Keil5输出hex转换为bin的设置
linux·运维·服务器
Hoxy.R4 小时前
KingbaseES读写分离高可用集群扩容、备库重建与故障切换实战
运维·数据库
谷雪_6584 小时前
Linux 网络命名空间:从内核原理到企业级容器网络架构全景实战
linux·网络·架构
Tian_Hang4 小时前
Eclipse Ditto 的权限策略
java·服务器·前端·网络·ide·ubuntu·eclipse
qq_163135755 小时前
Linux 【03- chgrp命令超详细教程】
linux
CodeStats5 小时前
【编程语言】深度梳理C/C++、Java、Python、Go、Rust的区别
java·linux·c语言·c++·python·rust·go