Linux学习之路 -- 文件 -- 文件描述符

前面介绍了与文件相关的各种操作,其中的各个接口都离不开一个整数,那就是文件描述符,本文将介绍文件描述符的一些相关知识。

目录

<1>现象

<2>原理

文件fd的分配规则和利用规则实现重定向


<1>现象

我们可以先通过printf把文件描述符打印出来,我们可以多打开几个文件,看看文件描述符有什么特别,再解释原理

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>#define filename "file.txt"
int main()
{int fd1 = open("log1.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);int fd2 = open("log2.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);int fd3 = open("log3.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);int fd4 = open("log4.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);int fd5 = open("log5.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);printf("fd1:%d\n",fd1);printf("fd2:%d\n",fd2);printf("fd3:%d\n",fd3);printf("fd4:%d\n",fd4);printf("fd5:%d\n",fd5);return 0;
}

运行结果

可以看见,文件的描述符从3开始增加,在3之前的0 1 2 其实已经被用掉了。看到这些数字,其实我们可以联想到数组的下标(因为文件描述符不能小于0),那么我们就可以假设一个文件描述符就代表该文件在某一数组中的位置(至于这个数组是什么,后面详谈),那是谁用了0 1 2 呢?其实是标准输入、输出和错误三个文件。

<2>原理

前面介绍过文件的几种系统调用的接口,我们不难发现,这些接口其实和C语言库中的文件操作函数非常相似,实际上C语言的文件操作函数就是封装系统调用接口。既然C语言底层是系统调用接口,那我们在使用C语言的文件函数,这些系统调用接口所需要文件描述符在哪里呢? 当然是在FILE 这个结构体当中,这个结构体是C标准库自己封装的一个结构体。 

既然FILE这个结构体中含有文件描述符,那我们就可以通FILE来查看标准输入、输出和错误的文件的文件描述符是多少。下面演示一下代码

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>#define filename "file.txt"
int main()
{printf("%d\n",stdin->_fileno); // _fileno是FILE结构体中文件描述符的成员名printf("%d\n",stdout->_fileno);printf("%d\n",stderr->_fileno);
}

运行结果

  这就印证了0 1 2 这三个文件描述符是一开始就被标准输入、输出和错误占用了。

文件描述符的具体含义

前面说过,打开文件的进程,所以研究文件的各种操作,就是研究进程与磁盘数据的关系。而我们在日常运行进程时,我们可能要打开很多的文件,对于这些文件,系统自然是需要管理的。而在linux中,OS就通过创建了一个file的结构体(别的系统也有可能是其他的数据结构)来描述一个被打开的文件,file这个结构体中包含了很多内容,这里比较重要的是文件属性、方法集和缓冲区。而这些文件的file又可以通过指针链接起来。进程PCB中有一个结构体指针,该结构体指针指向的结构体是用于描述该进程打开的所有文件,它也称为文件描述表,其中就包含了每个文件的file指针。

所以当我们需要打开一个新文件时,先在磁盘中找到对应的位置,然后生成新的struct file,初始化struct file中的数据,并将其指针填入struct file* fd_array[] 中,最后再将对应fd_array的下标返回给上层,我们就可以使用返回的fd进行文件操作。而我们如果要关闭文件时,就释放对应的struct file。

题外话

在linux中我们把所有的东西都看成文件,底层的硬件设施也可以看成文件。对于这些硬件肯定有读方法和写方法,这些硬件的读写方式肯定是不一样的。但我们可以用一个struct file来描述这些读写方法,并将这些不同的读写方法重名成相同的函数名,这样我们在调用硬件的一些函数时,就可以忽略底层的差异,直接使用上层的接口

文件fd的分配规则和利用规则实现重定向

直接先说规则:最小的没有被使用的数组下标,会分配给最新打开的文件。我们用一段代码演示一下上述的规则。

从运行结果上来看,我们关闭了文件描述符为0的文件,然后重新创建了一个log1.txt文件(该文件之前不存在)。这个新建文件的文件描述符为零,不是3,说明上述的分配规则是正确的。

输出重定向

我们先用一段代码实现一下输出重定向,再解释一下原理

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>#define filename "file.txt"
int main()
{close(1);int fd = open("log1.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);printf("fd:%d\n",fd);return 0;
}

运行结果

这里我们可以发现,运行代码时,显示器上并没有显示出对应的文件描述符。而log1.txt文件里面出现了对应的文件描述符。这就是我们所熟悉的输出重定向,因为printf这个函数底层只认fileno (文件描述符在struct file里面的成员名)。而这里我们把标准输出文件关闭了,给打开的log1.txt分配的文件描述符就是1,printf只会朝文件描述符为1的文件里面打印。所以才会出现上述现象。以此类推,我们也可以写出输入重定向,下面演示一下

先创建一个long.txt文件,向里面写入一些内容(这里为了方便演示,我只输入了一段数字),然后关闭stdin(也就是标准输入),然后系统给long.txt分配文件描述符为0,使用scanf读取数据,而scanf只认文件描述符为0的文件,所以scanf会从long.txt中读取数据。

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>#define filename "file.txt"
int main()
{close(0);int fd = open("long.txt",O_RDONLY);int a = 0;scanf("%d",&a);printf("%d\n",a);return 0;
}

运行结果

同理,如果我们需要实现追加重定向,我们只需要在原来输出重定向的基础上,把打开文件的方式中的O_TRUNC修改成O_APPEND即可,其余的均不变。、

dup2函数

像上述实现重定向的方式颇为繁琐,有没有更好的方式呢?当然是有的。这里文件描述符代表的数组里面存的是struct file的指针,如果我们要实现重定向,只要将数组对应下标的内容进行交换即可。而这里由于stdout可以不被使用,所以我们可以把新打开文件对应的指针,直接拷到stdout对应的数组下标中即可。

这里有专门的系统调用来帮我们实现这一功能,这个接口就是dup2(另外两个暂时不学)。

这个函数会把对应数组下标的内容拷贝到另外一个数组下标的对应内容上,而在这里就是把oldfd对应的数组下标内容拷贝到newfd对应数组下标的内容里面,所以最后只剩下oldfd对应数组下标内容。

如果要实现重定向操作,我们就不需要再关闭文件描述符,只需要使用dup2这个接口即可。

以上就是全部内容,如果文中有不对之处,还望各位大佬指正,谢谢!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3015854.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

04_SpringCloud

文章目录 单体架构与微服务架构的介绍单体架构微服务架构 微服务的实现服务之间的调用服务注册中心Eureka 注册中心Eureka的自我保护机制Nacos注册中心 单体架构与微服务架构的介绍 单体架构 单体架构 所有的代码最终打包成一个文件(jar包)&#xff0c;整个系统的所有功能单元…

爆赞好文之java反序列化之CB超详细易懂分析

java反序列化之CB超详细易懂分析 CB1环境搭建前言分析PropertyUtilsBeanComparatorPriorityQueue CB2环境搭建前言exp CB1 环境搭建 pom.xml <dependencies><dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils&l…

生成gitee公钥

1、打开设置 2、设置SSH公钥 3、生成公钥 4、复制终端输出的公钥&#xff0c;放到这里&#xff0c;标题随便取。 5、测试 ssh -T gitgitee.com 最后用这个测试

IT 项目管理介绍和资料汇总

IT项目管理到底是什么&#xff1f;是对组织承担的任何信息技术项目的成功监督。IT项目经理负责规划、预算、执行、领导、故障排除和维护这些项目。IT项目经理可能会做的事情包括&#xff1a; 1、硬件安装 2、软件、网站和应用程序开发 3、网络和云计算解决方案的升级和/或推出…

软考中级-软件设计师(八)算法设计与分析 考点最精简

一、算法设计与分析的基本概念 1.1算法 算法&#xff08;Algorithm&#xff09;是对特定问题求解步骤的一种描述&#xff0c;有5个重要特性&#xff1a; 有穷性&#xff1a;一个算法必须总是在执行又穷步骤后结束&#xff0c;且每一步都可在又穷时间内完成 确定性算法中每一…

KAN: Kolmogorov–Arnold Networks

KAN: Kolmogorov–Arnold Networks 论文链接&#xff1a;https://arxiv.org/abs/2404.19756 代码链接&#xff1a;https://github.com/KindXiaoming/pyKAN 项目链接&#xff1a;https://kindxiaoming.github.io/pyKAN/intro.html Abstract 受Kolmogorov-Arnold表示定理的启…

访问网络附加存储:nfs

文章目录 访问网络附加存储一、网络附加存储1.1、存储类型1.3、通过NFS挂载NAS1.4、NFS挂载过程服务端客户端 二、实验&#xff1a;搭建NFS服务端及挂载到nfs客户端服务端客户端测试命令合集服务端客户端 访问网络附加存储 一、网络附加存储 1.1、存储类型 DAS&#xff1a;Di…

mysql的数据结构及索引使用情形

先来说下数据的一般存储方式&#xff1a;内存(适合小数据量)、磁盘(大数据量)。 磁盘的运转方式&#xff1a;速度 旋转&#xff0c;磁盘页的概念&#xff1a;每一页大概16KB。 1、存储结构 哈希 是通过hash函数计算出一个hash值的&#xff0c;哈希的优点就是查找的时间复杂度…

图片编辑工具-Gimp

一、前言 GIMP&#xff08;GNU Image Manipulation Program&#xff09;是一款免费开源的图像编辑软件&#xff0c;具有功能强大和跨平台的特性。 GIMP作为一个图像编辑器&#xff0c;它提供了广泛的图像处理功能&#xff0c;包括但不限于照片修饰、图像合成以及创建艺术作品…

SpringMVC响应数据

三、SpringMVC响应数据 3.1 handler方法分析 理解handler方法的作用和组成&#xff1a; /*** TODO: 一个controller的方法是控制层的一个处理器,我们称为handler* TODO: handler需要使用RequestMapping/GetMapping系列,声明路径,在HandlerMapping中注册,供DS查找!* TODO: ha…

【notepad++】使用

1 notepad 下载路径 https://notepad-plus.en.softonic.com/download 2 设置护眼模式 . 设置——语言格式设置——前景色——黑色 . 背景色——RGB &#xff1a;199 237 204 . 勾选“使用全局背景色”、“使用全局前景色” . 保存并关闭

Python专题:二、Python小游戏,体验Python的魅力

希望先通过一个小的游戏让大家先对Python感兴趣&#xff0c;兴趣是最好的老师。 小游戏的运行结果&#xff1a; 1、在sublime编辑器里面写如下代码&#xff1a; import randomnum random.randint(1, 100) # 获得一个随机数 is_done False # 是否猜中的标记 count 0 # 玩…

软件设计师-应用技术-数据结构及算法题4

考题形式&#xff1a; 第一题&#xff1a;代码填空 4-5空 8-10第二题&#xff1a;时间复杂度 / 代码策略第三题&#xff1a;拓展&#xff0c;跟一组数据&#xff0c;把数据带入代码中&#xff0c;求解 基础知识及技巧&#xff1a; 1. 分治法&#xff1a; 基础知识&#xff1…

美易官方:英伟达业绩将难以撑起股价?

美股市场似乎总是对各大公司的业绩表现抱有极大的期待&#xff0c;就像一个永远填不饱的“巨胃”。在这样的市场环境下&#xff0c;即使是业绩骄人的公司也可能难以支撑其股价。英伟达&#xff0c;这家在图形处理单元&#xff08;GPU&#xff09;领域享有盛誉的公司&#xff0c…

语音识别--kNN语音指令识别

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…

英语学习笔记5——Nice to meet you.

Nice to meet you. 很高兴见到你。 词汇 Vocabulary Mr. 先生 用法&#xff1a;自己全名 / 姓 例如&#xff1a;Mr. Zhang Mingdong 或 Mr. Zhang&#xff0c;绝对不能是 Mr. Mingdong&#xff01; Miss 女士&#xff0c;小姐 未婚 用法&#xff1a;自己全名 / 姓 例如&#…

ESP32 IDF linux下开发环境搭建

文章目录 介绍升级Python环境下载Python包配置编译环境及安装Python设置环境变量 ESPIDF环境搭建下载esp-idf 代码编译等待下载烧录成功查看串口打印 介绍 esp32 官方文档给的不是特别详细 参考多方资料 最后才完成开发 主要问题在于github下载的很慢本教程适用于ubuntu deban…

跨境支付行业研究

1. 行业基本情况 随着全球人均购买力增强、互联网普及率提升、支付渠道的进一步成熟、物流等配套设施的完善&#xff0c;网络购物已经成为全球兴起的消费习惯。另一方面&#xff0c;跨境电商对传统贸易的替代已经成为趋势。跨境电商在交易成本和便利程度上都有明显的优势 图1 …

《我的医养信息化之路》之三十二:中医馆

今年五一节的气候有点冷&#xff0c;走到小区又湿又暗的、寂静的小道上&#xff0c;树上的雨水滴到头上&#xff0c;不免感到孤独而寒冷。还好路很短&#xff0c;很快就回到办公室&#xff0c;开了电灯和电脑&#xff0c;刚刚的冷意已经消失了&#xff0c;我开始审核今天中医馆…

C++ 数据内存分布揭秘:从栈到堆的探索之旅

目录 1. 栈(Stack) 2. 堆(Heap) malloc和new的区别 堆与栈在C中的异同点详解 3. 数据段(Data Segment) 4. 代码段(Code Segment) 5. 动态内存分配的陷阱 当我们谈论C编程时&#xff0c;对内存布局的理解至关重要。本文将深入探讨C中各种变量和数据结构在内存中的分布情况…