Linux文件fd剖析

学习之前,首先要认识什么是文件?

  1. 空文件也是要在内存中占据空间的,因为它还有属性数据。
  2. 文件 = 属性 + 内容
  3. 文件操作 = 对内容 + 对属性 或者对内容和属性的操作
  4. 标定一个文件的时候,必须使用:路径+文件名,文件具有唯一性
  5. 如果没有指明文件路径,默认是对当前路径的文件进行访问
  6. 文件没有被打开的时候是不能进行访问的
  7. 二进制可执行文件在没有运行的时候,所谓的文件操作都没有执行
  8. 磁盘上文件被分为被打开的文件和没有被打开的文件

总结:文件操作的本质,是进程和被打开文件之间的关系。

一、文件操作

1.1 使用C接口进行文件操作(用C语言头文件和C语言在Liunx下的表达形式)

在将我们的程序编译完成以后,再运行,发现生成了一个新的文件,并且文件中的内容和我们代码中写的一样。

  • 这个过程中,使用的是C语言的接口进行文件操作。
  • 以写的方式打开文件名问log.txt的文件,没有的这个文件的话就会创建。
  • 使用C接口向该文件中写入内容。

不同的编程语言都有文件操作的接口,包括C++,Java,Python,php等等语言,并且它们的操作接口函数都不一样,但是它们所在的系统都是Linux系统。

无论上层语言如何变化,但是进行文件操作的时候,各种语言最终都会调用Linux的文件操作的系统调用接口。

1.2 文件操作的系统调用

open函数:

可以看到,函数声明有两个,一个是两个参数的,一个是三个参数的,它们必然不是函数重载,因为Linux是用纯C实现的。

  • const char* pathname:这是文件路径,也就是我们要打开的文件所在的路径,其中包括文件名,如果没有路径只有文件名的话,默认在当前路径打开。
  • int flags:打开方式选项标志位。在使用C语言进行文件操作的时候,打开方式有“w”,“r”,“a”等方式,系统调用open也有,只是将这些标志放在了一个32位的变量中。
  • mode_t mode:它是权限值,如果这个文件不存在,那么以写的方式打开的时候就会创建这个文件,在创建文件的时候需要给这个文件设定权限(使用八进制数)。如果这个文件存在的话,那么就不用传第三个参数了,因为文件的权限已经确定了。
  • 返回值:是一个int类型的参数,具体的在后面本喵会介绍,但是如果打开失败就会返回-1。

执行我们写的代码后,log.txt文件是创建了,但是它是红色的,说明它有错误。可以看到它前面的权限是乱的,因为我们没有指定创建文件时的权限。

但是权限并不是我们设定的0666,而是0664,这是因为有默认权限掩码(umask)的影响。

此外还有close函数,write函数,使用 man + 函数名指令查看相应参数

二、文件描述符fd

在使用系统调用open时,返回的那个整数就是文件描述符。

将文件名使用宏的方式打开多个文件。

现在我们见到了文件描述符,发现它就是几个数字。

当一个文件被打开后,操作系统会创建一个对应的结构体对象,类型是struct file。

struct file
{//文件大小//文件类型......//文件的各种属性
}
  • 每打开一个文件,操作系统就会创建这样的一个结构体对象将被打开的文件描述出来。
  • 将多个这样的结构体对象采用一定的方式组织起来,比如链表的方式,以方便操作系统管理这些被打开的文件。

在描述进程的结构体task_struct中,有一个指针,struct files_struct* files,这个指针指向一个结构体对象,该对象类型如下:

struct files_struct
{//......struct file* array[];
}
  • struct files_struct结构体中存在一个指针数组array,该数组中的指针指向的是一个个struct file类型的结构体对象。
  • 换言之,该数组中放的是被打开文件结构体对象的地址。
  • 每一个被指向的struct file结构体对象都描述着一个被打开的文件。

在前面我们看到,打印出来的fd值是连续的小整数,这些小整数就是struct files_struct 结构体中指针数组struct file* array[]的下标。

文件描述符的本质,就是数组的下标。

  • 当一个程序被加载到内存中,操作系统会创建一个结构体struct task_struct对象,在该结构体中有一个指针struct files_struct* files,指向一个struct files_struct结构体对象。
  • 这个结构体也被叫做进程描述符表,该结构体中有一个数组struct file* array[],数组中存放的是被打开文件的结构体对象的地址。如上图中,下标为3,也就是fd的是3的时候,访问到的是struct file* array[3]。
  • 通过数组中访问到的地址,可以找到对应打开文件的结构体对象,如上图中的struct file log.txt。

只有被打开的文件才会在内存中创建struct file结构体对象,没有被打开的文件就静静的躺在磁盘上。

不是该进程打开的文件,该进程执行的文件描述符表中也没有这个文件的地址。

2.1 文件描述符fd=0/1/2

在上面打开多个文件的时候,我们将打开文件的fd值打印出来,发现它是从3开始的。

那么fd = 0/1/2是什么呢?

C默认会打开三个输入输出流,分别是stdin,stdout,stderr。

可以看到,这三个流是FILE*类型的指针,暂时不用管FILE是什么,只需要知道它是一个结构体。

使用C语言的文件操作结构打开一个文件,再使用系统调用去向文件中写内容。

我们此时已经确定的知道了,FILE结构体中是有文件描述符的。

文件描述符0 1 2出现了。

  • fd = 0:标准输入流(stdin)
  • fd = 1:标准输出流(stdout)
  • fd = 2:标准错误(stderr)

此时我们便清楚了为什么我们打开的文件,文件描述符是从3开始的,因为012被默认打开的三个流占据了。

2.2 文件描述符的分配规则

为什么我们打开的文件,fd是从3开始的?不是从5或者6开始的呢?

我们将fd=0的标准输入流关闭掉,再打开文件,并且打印fd值。

我们发现此时的fd成了0,而不是3了。

同样的,将fd=2的流关闭,在打开文件。

根据这个现象,可以得出结论:文件描述符fd的分别规则是:从小到大,按顺序查找,将没有被占用的数组下标作为被打开文件的文件描述符fd值。

三、重定向

3.1 输出重定向

前面我们只关闭过0和2,没有关闭过1,现在我们关闭一下1来看看。

将标准输出关闭,然后打开文件,并且打印出打开文件的文件描述符fd。

  • 因为将标准输出关闭了,所以无法显示。

根据前面分析的文件描述符分配规则,可以推断出,将标准输出关闭以后,再打开一个文件,此时这个文件的文件描述符fd等于1。

  • 在将fd=1关闭后,再打开一个文件,从小到大按顺序查找,发现数组下标为1的位置没有被占用,所以新打开文件的fd就等于1。
  • printf函数原本是要输出到标准输出的,也就是fd为1的数组中指向的struct file对象的地址。
  • 此时下标为1的数组中不再是标准输出了,而变成了我们新打开文件的地址。
  • 但是printf已经写死了,它仍然会写入到fd为1的文件中,所以原本打印在显示器上的内容此时会写入到新打开的文件中。

3.2 输入重定向

使用只读方式打开文件log.txt该文件原本就存在。

  • 将原本struct file* array[]数组中下标0的内容改成下标为fd的内容,也就是dup2(fd,0)的作用。
  • 使用标准输入函数fgets,从标准输入流也就是键盘中读取字符串。
  • 屏幕上打印读取到的内容。

运行时直接输出log.txt中的内容,没有从键盘获取数据。也就是说,fgets函数是从文件中获取到内容,而不是标准输入。

这种从标准输入到文件的重定向叫做输入重定向。

3.3 进程独立性

子进程重定向了以后,会影响父进程吗?根据进程独立性我们可以知道,肯定是不会影响到。

在子进程中进行输出重定向,父进程同样在标准输出打印。

  • 有两个进程,一个父进程,一个子进程,操作系统维护着两个task_struct结构体,如上图红色框所示。
  • 每个进程的PCB中都有一个struct files_struct*的指针files。它们各自指向的struct files_struct结构体中都有一个文件描述符表。
  • 两个文件描述符表中的内容在子进程刚创建时是一样的,所以它们都指向相同的被打开的文件。
  • 当子进程将自己文件描述符表中下标为1的文件关闭以后,并不影响父进程文件描述符表中下标为1的数组中的内容。

每个进程都会维护自己的文件描述符表,所以多个进程就会存在多个文件描述符表,但是这些表中的指针指向的被打开文件只有一套。

某个进程进行文件的打开与关闭操作时,只需要修改自己的文件描述符表就可以,不会对其他进程造成任何影响。

一切皆文件是指:在操作系统中一切都是结构体。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

长虹智能电视ZLS59GiD机芯刷机方法及刷机固件,附进维修模式方法

适用机芯:ZLS59GiD 适配电视机型:55D2000i、65D2000i、50D2000i、50D2000i(LM4L)、55D2060G、50S1、50D2060G、55S1、32S1、39S1、43S1、50S1 刷机说明: 1.先确认电视机芯是否是表中所列,电视刷机机芯必须是此贴中所介绍的机芯…

交换机03_基本配置

一、思科设备的命令行基础 1、进入设备的命令行界面 设备支持命令行 去查看设备上的接口,是否有console口需要有console线 右击此电脑设备管理器需要通过超级终端软件进行连接,如putt、secret CRT、xshell等软件 (1)思科模拟器…

Python从入门到精通之元类

系列 Python从入门到精通之安装与快速入门-CSDN博客 Python从入门到精通之基本数据类型和变量-CSDN博客 Python从入门到精通之集合(List列表、Tuple元组、Dict字典、Set)-CSDN博客 Python从入门到精通之条件语句、循环语句和函数-CSDN博客 Python从…

02、Kafka ------ 配置 Kafka 集群

目录 配置 Kafka 集群配置步骤启动各Kafka节点 配置 Kafka 集群 启动命令: 1、启动 zookeeper 服务器端 小黑窗输入命令: zkServer 2、启动 zookeeper 的命令行客户端工具 (这个只是用来看连接的节点信息,不启动也没关系&#…

Python从入门到网络爬虫(面向对象详解)

前言 Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。本章节我们将详细介绍Python的面向对象编程。如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些面向对象语言的一些基本…

LeetCode第71题 - 简化路径

题目 以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。 在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (…) 表…

Microsoft Word中一些固定表格,文本框,图表固定到固定的位置

固定图片 插入图片: 首先,在文档中插入你想要的图片。 选择图片: 点击图片,选择它。 设置文本环绕方式: 在“格式”标签下(或者在图片工具栏上),选择“文本环绕”选项。选择“四周型”或“紧密型”等选项。这允许你将…

HTML5+CSS3⑥——CSS三大特性、表格、列表

CSS特性 继承性 层叠性 优先级 叠加计算规则 表格 表格结构标签 合并单元格 列表 无序列表 有序列表 定义列表

基于Java驾校预约管理系统

基于Java的驾校预约管理系统是一个为驾校提供在线预约服务的系统。该系统利用Java编程语言,采用SSM框架,并使用MySQL数据库进行开发。 这个系统主要有三个角色:用户、教练和管理员。 用户可以注册和登录系统,查看驾校的公告信息…

Spring中基于注解的IOC配置项目举例详解

文章目录 Spring中基于注解的IOC配置项目举例详解1、创建如下结构的Spring项目pom.xmldao层service层application.xmllog4j.properties 2、用于创建对象的常用注解2.1、Controller或Controller("user")声明bean,且id"user"2.2、Service或用Service("u…

leetcode04-元素符号积

题目链接: https://leetcode.cn/problems/sign-of-the-product-of-an-array/description/?envTypestudy-plan-v2&envIdprogramming-skills 思路: 设置一个符号位,初始值为1,遍历整个初始数组: 若碰到数组元素为0&…

大根堆小根堆

偷学的罒ω罒&#xff0c;非常好用的模版&#xff0c;分享一下。学过堆排应该很容易就看懂了&#xff0c;看不懂学一下堆排&#xff0c;不好懂的地方我也写了注释 小根堆 template<typename T> class smallest_heap { private://建堆T heap[10001];int len; public:sma…

2024年的诸多跨年演讲,为什么觉得像是鸡汤?

时光如白驹过隙匆匆而已&#xff0c;转瞬间已来到2024年。伴随着新的一年的到来&#xff0c;一些互联网大佬如罗振宇、吴晓波等纷纷直播演讲&#xff0c;分享各自的思考和感悟。值不值得听呢&#xff1f;为什么有时候觉得是鸡汤&#xff1f;这里分析下可能的原因。 罗振宇的“做…

基于EMD-SpEn(样本熵)联合小波阈值去噪

代码原理 基于 EMD-SpEn&#xff08;样本熵&#xff09;联合小波阈值去噪方法是一种用于信号降噪的信号处理方法&#xff0c;它结合了经验模态分解 (EMD)、样本熵 (SpEn) 和小波阈值处理技术。 首先&#xff0c;使用 EMD 将原始信号分解为一组称为经验模态函数 (IMFs) 的信号…

linux USB 设备基础知识

一个 USB 设备是一个非常复杂的事物, 如同在官方的 USB 文档(可从 http://www.usb.org 中得到)中描述的. 幸运的是, Linux 提供了一个子系统称为 USB 核, 来处理大部分复杂的工作. 这一章描述驱动和 USB 核之间的交互. 图 USB 设备概览 显示了 USB 设备如何包含配置, 接口, 和端…

VUE3跳转页面时 定时器未清除解决

一,问题 1、在vue中使用setTimeout定时器的时候&#xff0c;可能会遇到关不掉的情况&#xff0c;会存在明明已经在beforeDestroy和destroyed中设置了定时器清除了&#xff0c;但是有时候没生效&#xff0c;定时器还会继续执行。 2、在这里需要说一下setTimeout的使用场景&…

软件体系结构与风格复习一

总结了软件体系结构风格的经典部分。 从软件架构风格的定义&#xff0c;到软件架构模型&#xff0c;到一些经典的软件架构风格。然后是敏捷开发中的软件架构&#xff0c;之后是软件架构风格的设计和实现&#xff0c;最后是软件架构风格的质量和评估。 这是整个课程的开展顺序…

YoloV8改进策略:基于频域多轴表示学习模块|全网首发|高效涨点|代码注释详解

摘要 涨点效果:在我自己的数据集上,改进一的mAP50 由0.986涨到了0.99,mAP50-95由0.737涨到0.749,涨点明显! 本文尝试使用频域多轴表示学习模块改进YoloV8,尝试了4种改进方式,均有不同的涨点。 论文:《医学图像分割中的频域多轴表示学习》 https://arxiv.org/pdf/231…

新能源汽车@2023/24:卷价格、拼智能与生态战

【潮汐商业评论/原创】 2023年末尾&#xff0c;受到大众广泛热议的小米汽车发布会“姗姗来迟”&#xff0c;也为“乱战”中的2023新能源汽车市场画上了一个句号。 然而&#xff0c;在雷军整整三个小时看似平和的演讲与技术讲解中&#xff0c;实则在电机、智驾、智舱等核心技术…

正则表达式速查

正则表达式速查 1、正则表达式2、常用正则表达式速查手册 1、正则表达式 正则表达式是一种强大的文本匹配工具&#xff0c;可以用于在文本中查找特定的模式 正则表达式由不同类型的字符组成&#xff0c;包括普通字符、元字符、分隔符和转义字符等。其中&#xff1a; 普通字符&…