【Linux】基础IO——文件描述符,重定向
话接上篇:
1.文件描述符fd
磁盘文件 VS 内存文件?
当文件存储在磁盘当中时,我们将其称之为磁盘文件,而当磁盘文件被加载到内存当中后,我们将加载到内存当中的文件称之为内存文件。磁盘文件和内存文件之间的关系就像程序和进程的关系一样,当程序运行起来后便成了进程,而当磁盘文件加载到内存后便成了内存文件。
进程想要访问文件必须先打开文件,一个进程可以打开多个文件,而系统当中又存在大量进程,也就是说,在系统中任何时刻都可能存在大量已经打开的文件,已经打开的文件会被加载到了内存中,这些文件也叫内存文件,反之,没有打开的文件就叫做磁盘文件。那么操作系统就要管理这些打开的文件。
如何管理就是先描述,再组织。操作系统为每个已经打开的文件创建各自的struct file结构体,然后将这些结构体以双链表的形式连接起来,那么操作系统对文件的管理也就变成了对这张双链表的增删改查等操作,在每个节点中不仅有链表的指针,还应该存在着文件的内容+属性,这些信息大部分在磁盘中就保留在文件内部了,加载的时候就从磁盘中把数据加载到内存。
而为了区分已经打开的文件哪些属于特定的某一个进程,我们就还需要建立进程和文件之间的对应关系。
进程和文件之间的对应关系是如何建立的?
当进程运行的时候,操作系统会将该程序的代码和数据加载到内存,然后创建对应的task_struct, mm_struct, 页表等…

task_struct 里面有一个指针,指向files_struct结构体,结构体里面有名为fd_array的指针数组,该数组的下标就是文件描述符fd。
使用read和write的时候要传入文件描述符,通过文件描述符找到这个数组中的指针,进而对文件访问。
当进程打开log.txt文件时,我们需要先将该文件从磁盘当中加载到内存,形成对应的struct file,将该struct file连入文件双链表,并将该结构体的首地址填入到fd_array数组当中下标为3的位置,使得fd_array数组中下标为3的指针指向该struct file,最后返回该文件的文件描述符给调用进程即可。

因此,我们只要有某一文件的文件描述符,就可以找到与该文件相关的文件信息,进而对文件进行一系列输入输出操作。
注意: 向文件写入数据时,是先将数据写入到对应文件的缓冲区当中,然后定期将缓冲区数据刷新到磁盘当中。
1.1.文件描述符的分配规则
我们之前连续打开了6个文件,我们发现文件描述符是从3开始的,并且是连续地址的。那真的是一直从3开始吗?下面我们看一段代码:
#include<stdio.h> #include<string.h> #include<unistd.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>int main(){ close(0);int fd1=open("./log1.txt",O_WRONLY|O_CREAT,0644);int fd2=open("./log2.txt",O_WRONLY|O_CREAT,0644);int fd3=open("./log3.txt",O_WRONLY|O_CREAT,0644);int fd4=open("./log4.txt",O_WRONLY|O_CREAT,0644);printf("%d\n",fd1);printf("%d\n",fd2);printf("%d\n",fd3);printf("%d\n",fd4);close(fd1);close(fd2);close(fd3);close(fd4);return 0;}

我们发现怎么fd从0开始了,而之后的又是从3开始了。现在我们在将2也关了,我们再来看结果会是如何。
#include<stdio.h> #include<string.h> #include<unistd.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>int main(){ close(0);close(2);int fd1=open("./log1.txt",O_WRONLY|O_CREAT,0644);int fd2=open("./log2.txt",O_WRONLY|O_CREAT,0644);int fd3=open("./log3.txt",O_WRONLY|O_CREAT,0644);int fd4=open("./log4.txt",O_WRONLY|O_CREAT,0644);printf("%d\n",fd1);printf("%d\n",fd2);printf("%d\n",fd3);printf("%d\n",fd4);close(fd1);close(fd2);close(fd3);close(fd4);return 0;}

我们发现0和2也被用起来了。现在我们就明白了文件描述符的分配规则是从最小的未被使用的下标开始的
事实上
Linux下进程默认会打开三个文件描述符,0:标准输入、1:标准输出、2:标准错误。
0,1,2对应的物理设备一般是:键盘、显示器、显示器。
我们之前验证了文件描述符默认是从3开始的,也就是说0,1,2是默认被打开的。
- 0代表的是标准输入流,对应硬件设备为键盘;
- 1代表标准输出流,对应硬件设备是显示器;
- 2代表标准错误流,对应硬件设备为显示器。
当一个进程被创建时,OS就会根据键盘、显示器、显示器形成各自的struct file,将这3个struct file链接到文件的双链表当中,并将这3个struct file的地址分别填入fd_array数组下标为0、1、2的位置,至此就默认打开了标准输入流、标准输出流和标准错误流。
文件描述符的分配规则:分配最小的,没有被占用的。如果我把0号关闭,那么为新文件分配的时候就从最小的0分配。
2.重定向
2.1.输出重定向
1.输入重定项。
我们之前学习过的输出重定向就是,将我们本应该输出到显示器上的数据重定向输出到另一个文件中。那他的原理是什么了?

例如: 如果我们想让本应该输出到“显示器文件”的数据输出到log.txt文件当中,那么我们可以在打开log.txt文件之前将文件描述符为1的文件关闭,也就是将“显示器文件”关闭,这样一来,当我们后续打开log.txt文件时所分配到的文件描述符就是1。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main()
{close(1);// 打开文件int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd < 0){perror("open");exit(1);}// 打开成功printf("fd: %d\n", fd);printf("fd: %d\n", fd);printf("fd: %d\n", fd);printf("fd: %d\n", fd);fprintf(stdout, "hello fprintf\n");const char* s = "hello fwrite\n";fwrite(s, strlen(s), 1, stdout);fflush(stdout);// 关闭文件close(fd); return 0;
}

通过上面的现象也可以看出,打印的数据没有到显示器上,而是到了磁盘的文件中,这是为什么呢?
上面就说过,0、1、2默认是被打开的,对应的就要打开显示器,所以stdout的文件描述符就是1,所以C语言的接口fprintf认识的就是stdout或者说就是1,我们一开始就关闭了1号文件描述符,把数组下标为1的位置设置为NULL,然后打开了log.txt文件,此时1没有被占用,所以就把下标为1的位置填入log.txt的结构体的地址,log.txt的文件描述符就是1了,但是上层的C语言函数认识的还是1,他们还是继续往1中写入,这样就不能打印到屏幕而是重定向到了文件中。
重定向的本质是在操作系统中更改fd对应的内容,上面演示的这就就叫做输出重定向。
2.2.输入重定向
输入重定向就是,将我们本应该从一个键盘上读取数据,现在重定向为从另一个文件读取数据。

比如说我们的fget函数是从标准输入读取数据,现在我们让它从log1.txt当中读取数据,我们在scanf读取数据之前close(0).这样键盘文件就被关闭,这样一样log1.txt的文件描述符就是0.
int main()
{close(0);// 打开文件int fd = open("log.txt", O_RDONLY);if (fd < 0){perror("open");exit(1);}printf("fd: %d\n", fd);char buffer[64];fgets(buffer, sizeof(buffer), stdin);printf("%s\n", buffer);// 关闭文件close(fd);return 0;
}

关闭了0号文件描述符,所以打卡的新文件的文件描述符就变成了0,然后读取了文件中的第一行数据。
2.3.追加重定向
还有一种就是追加重定向,更改一下选项就行了。
int main()
{close(1);// 打开文件int fd = open("log.txt", O_WRONLY | O_APPEND | O_CREAT);if (fd < 0){perror("open");exit(1);}printf("%d\n", fd);fprintf(stdout, "append success\n");fflush(stdout);// 关闭文件close(fd);return 0;
}

【注意】:“>”输出重定向修改的只是1号也就是stdout标准输出,所以尽管程序中有两行代码,一行向1号文件描述符中打印,另一行向2号文件描述符中打印,那么使用输出重定向只会使1号文件描述符重定向,2号还是打印到显示器上。
![]()
2.4.dup2
我们发现我们上面只能通过close关闭对应的文件描述符实习对应的输出重定向和输出重定向,那我们能不能不关闭呢?
要完成重定向我们只需对fd_array数组当中元素进行拷贝即可。
例如,我们若是将fd_array[3]当中的内容拷贝到fd_array[1]当中,因为C语言当中的stdout就是向文件描述符为1文件输出数据,那么此时我们就将输出重定向到了文件log.txt。而在linux当中就给我们提供了这个系统调用:


- 函数功能: dup2会将fd_array[oldfd]的内容拷贝到fd_array[newfd]当中。
- 函数返回值:调用成功返回0,失败返回-1
使用的过程中需要注意:
- 如果oldfd不是有效的文件描述符,则dup2调用失败,并且此时文件描述符为newfd的文件没有被关闭。
- 如果oldfd是一个有效的文件描述符,但是newfd和oldfd具有相同的值,则dup2不做任何操作,并返回newfd。
只需要把想要重定向的文件在数组中拷贝过去,比如我想要输出重定向,重定向到某个文件,那么1就代表标准输出,所以就要改变1的指向,就把3的地址拷贝过去,这样1就指向了重定向的文件。
输入重定向也是一样的,0是标准输入,就要从其他文件输入,就把其他文件的地址拷贝到0的位置。
下面通过dup2演示一下前面的输出重定向:
1 #include<stdio.h>2 #include<sys/types.h>3 #include<sys/stat.h>4 #include<unistd.h>5 #include<fcntl.h> 6 int main()7 {8 int fd=open("./log.txt",O_WRONLY|O_CREAT,0644);9 dup2(fd,1);10 printf("hello world\n");11 printf("hello world\n");12 13 }

相关文章:
【Linux】基础IO——文件描述符,重定向
话接上篇: 1.文件描述符fd 磁盘文件 VS 内存文件? 当文件存储在磁盘当中时,我们将其称之为磁盘文件,而当磁盘文件被加载到内存当中后,我们将加载到内存当中的文件称之为内存文件。磁盘文件和内存文件之间的关系就像程…...
1.0 Android中Activity的基础知识
一:Activity的定义 Activity是一个应用组件,它提供了一个用户界面,允许用户执行一个单一的、明确的操作,用户看的见的操作都是在activity中执行的。Activity的实现需要在manifest中进行定义,不让会造成程序报错。 1.…...
线代知识点总结
目录 一.初等行/列变换 1.计算行列式时,行列变换都可 2.求矩阵的秩时,行列变换都可 3.解线性方程组时,仅能使用初等行变换 4.判定解的情况,单纯求r(A),r(A,b)的过程行列变换都可 5.求向量组极大无关组、线性表出关系&#x…...
案例学习-存量更新规划实施探索(武汉)
案例学习-存量更新规划实施探索(武汉) 武汉市在早期旧城更新实践中发现零散化的更新往往导致资源配置分散、城市建设破碎化等弊病,特别是由于过于强调项目自身“经济平衡”,在实施过程中也逐步暴露出住宅占比过大、强度偏高、公服…...
C#操作MySQL从入门到精通(17)——使用联结
前言: 我们在查询数据的过程中有时候查询的数据不是来自一个表而是来自多个表,本文使用的测试数据如下: 本文使用了两个表student_info、address_info student_info的数据如下: address_info的数据如下: 1、内联结 所谓内联结就是求交集,两个表都有的数据才是有效数…...
MyBatis 关于查询语句上配置的详细内容
1. MyBatis 关于查询语句上配置的详细内容 文章目录 1. MyBatis 关于查询语句上配置的详细内容2. 准备工作3. SQL查询结果,返回为POJO实体类型4. SQL查询结果,返回为List<POJO\> 集合类型5. SQL查询结果,返回为Map 集合6. SQL查询结果&…...
基于STM32和人工智能的智能家居监控系统
目录 引言环境准备智能家居监控系统基础代码实现:实现智能家居监控系统 4.1 数据采集模块4.2 数据处理与分析4.3 控制系统4.4 用户界面与数据可视化应用场景:智能家居管理与优化问题解决方案与优化收尾与总结 1. 引言 随着智能家居技术的快速发展&…...
这三款使用的视频、图片设计工具,提供工作效率
Videograp Videograp是一款专注于视频生成的工具,特别适合需要快速剪辑和编辑视频的用户。Videograp具备以下特点: 影音比例转换:Videograp支持调整视频的分辨率和比例,使其更适合不同的播放环境和设备。 AI快剪:该工…...
[Algorithm][贪心][最长递增子序列][递增的三元子序列][最长连续递增序列][买卖股票的最佳时机][买卖股票的最佳时机Ⅱ]详细讲解
目录 1.最长递增子序列1.题目链接2.算法原理详解3.代码实现 2.递增的三元子序列1.题目链接2.算法原理详解3.题目链接 3.最长连续递增序列1.题目链接2.算法原理详解3.代码实现 4.买卖股票的最佳时机1.题目链接2.算法原理详解3.代码实现 5.买卖股票的最佳时机 II1.题目链接2.算法…...
手把手教你入门vue+springboot开发(三)--登录功能后端
文章目录 前言一、redis安装二、后端代码1.修改application.yml文件2.增加utils文件3.增加Result类4.修改UserController类5.修改UserMapper类6.修改UserService和UserServiceImpl类7.增加LoginInterceptor类8.增加WebConfig类9.修改pom.xml文件 前言 前两篇我们用vuespringbo…...
三款有3D效果的js图表库
1、G2简洁的渐进式可视化语法。https://g2.antv.antgroup.com/manual/extra-topics/3d-charts 2、 https://www.highcharts.com/https://www.highcharts.com/ 3、https://www.fusioncharts.com/charts/pie-doughnut-charts/donut-chart-in-3d?frameworkjavascripthttps://www…...
【SQLAlChemy】表之间的关系,外键如何使用?
表之间的关系 数据库表之间的关系分为三种: 一对一关系(One-to-One):在这种关系中,表A的每一行都与表B的一行关联,反之亦然。例如,每个人都有一个唯一的社保号,每个社保号也只属于…...
Linux 基础IO 二
1.文件描述符的分配规则 #include<stdio.h> #include<string.h> //#include<unistd.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h>int main() {close(1);//fd分配原则,从最小的开始,没有被占用…...
找工作小项目:day15-macOS支持、完善逻辑
macOS支持、完善逻辑 目前的代码可以在Linux上完美运行编译,在Windows上也可以通过WSL编译运行源代码,但是在MacBook上却无法运行编译,这主要是由于macOS上没有epoll,取而代之的很相似的kqueue。由于操作系统不同,我们…...
植物大战僵尸杂交版 v2.0.88 mac版 Plants vs. Zombies 杂交版下载
特别注意:该游戏最低系统要求为macOS Sonoma 14.X,低于此系统版本的请勿下载! 游戏介绍 植物大战僵尸杂交版是由B站UP主“潜艇伟伟迷”制作的一款结合了《植物大战僵尸》原有元素与创新玩法的游戏。这款游戏以其独特的“杂交”植物概念在B站…...
PHP中的while循环:用法、技巧与最佳实践
在PHP编程中,while循环是一种基本且常用的控制结构,用于重复执行代码块,直到指定条件为假。while循环在处理未知迭代次数的任务时特别有用,例如读取文件内容、处理用户输入或动态生成数据等。与for循环不同,while循环适…...
如何解决跨境传输常见的安全及效率问题?
在当今全球化的商业版图中,企业为了拓展国际市场和增强竞争力,跨境传输数据已成为一项不可或缺的业务活动。合格的数据跨境传输方案,应考虑以下要素: 法律合规性:确保方案符合所有相关国家的数据保护法律和国际法规&am…...
『大模型笔记』主成分分析(PCA)解释:简化机器学习中的复杂数据!
主成分分析(PCA)解释:简化机器学习中的复杂数据 文章目录 一. 主成分分析(PCA)解释:简化机器学习中的复杂数据!二. 参考文献一. 主成分分析(PCA)解释:简化机器学习中的复杂数据! 主成分分析(Principal Component Analysis,简称PCA)通过 将大型数据集中的维度减少…...
springboot与flowable(5):任务分配(表达式)
在做流程定义时我们需要给相关的用户节点指派对应的处理人。在flowable中提供了三种分配的方式。 一、固定分配 在分配用户时选择固定值选项确认即可。 二、表达式 1、值表达式 2、方法表达式 三、表达式流程图测试 1、导出并部署 导出流程图,复制到项目中 部署流…...
如何使用CCS9.3打开CCS3.0工程
如何使用CCS9.3打开CCS3.0工程 点菜单栏上的project,选择Import Legacy CCSv3.3 Porjects…,弹出对话框,通过Browse…按钮导入一个3.3版本的工程项目; 选择.pjt文件,选择Copy projects into worlkspace 右击选择P…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...

