【关于Linux中----文件接口、描述符、重定向、系统调用和缓冲区】

news2024/11/14 15:22:48

文章目录

  • 一、C文件接口中的那些事儿
  • 二、接口介绍
  • 三、文件描述符fd
  • 四、重定向
  • 五、缓冲区


一、C文件接口中的那些事儿

众所周知,Linux是用C语言写成的,那在这篇文章的开头,自然要先对C语言中的文件操作进行一个概括!

写文件:

直接看一个例子:
在这里插入图片描述

再创建一个对应的Makefile文件:
在这里插入图片描述

运行程序之前,当前路径下文件如下:
在这里插入图片描述

运行结果如下:
在这里插入图片描述

读文件:

在这里插入图片描述

执行结果如下:
在这里插入图片描述

注意,与写操作不同,当文件不存在时,读操作将失败!

上面所演示的写操作中,实际上是覆盖式的写操作,即每次向文件中写入时,都会先清空文件。与之对应的有追加写操作,即"a",在每次向文件中写入时,不清空文件原有内容,而是在后面接着写入。这里不再演示。

除此之外,C语言中还有格式化的输入和格式化的输出(fprintf、fscanf),用法大同小异,这里不再赘述。

总结一下C语言中对文件的操作
r: Open text file for reading.
The stream is positioned at the beginning of the file.
r+: Open for reading and writing.
The stream is positioned at the beginning of the file.
w: Truncate(缩短) file to zero length or create text file for writing.
The stream is positioned at the beginning of the file.
w+: Open for reading and writing.
The file is created if it does not exist, otherwise it is truncated.
The stream is positioned at the beginning of the file.
a: Open for appending (writing at end of file).
The file is created if it does not exist.
The stream is positioned at the end of the file.
a+: Open for reading and appending (writing at end of file).
The file is created if it does not exist. The initial file position
for reading is at the beginning of the file,
but output is always appended to the end of the file.


二、接口介绍

在C语言学习阶段,相信大家都知道一个概念:
C程序默认会打开三个输入输出流,分别是stdin,stdout和stderr(标准输入,标准输出和标准错误)
在这里插入图片描述
可见,它们的类型都是FILE*,即它们都是C语言提供的文件。

而在体系结构层面看,上面三个流分别对应的硬件设备是键盘,显示器,显示器。也就是说,当向这三个文件中写入时,就是向对应的硬件设备中写入。
例:
在这里插入图片描述

结果如下:
在这里插入图片描述

也可以用输出重定向将信息写入一个具体的文件:
在这里插入图片描述

既然stdout和stderr所对应的硬件设备都是显示器,那么将上面代码中的stdout换成stderr所得到的结果是否相同呢?

答案是是的
在这里插入图片描述
在这里插入图片描述

但是,当用stderr向文件中重定向时,却不能将信息写入到文件中
在这里插入图片描述

为什么会出现这种现象?

答:因为">"叫做输出重定向,只能把stdout中的内容重定向到其他文件中。同时也证明了stdout和stderr根本就不是一个东西。

实际上,fputs可以向一切文件或者硬件设备中写入信息。这也反映出一个本质----一切皆文件,包括硬件设备。

这里需要明确一个概念:操作者对一切文件的操作最终都要访问到硬件,而硬件的管理者是操作系统,所以,所有的语言上的对文件的操作都要贯穿操作系统,所以就不得不使用操作系统的调用接口。

下面来介绍几个接口:

open:

在这里插入图片描述

先看一个没有第三个参数的例子(在此之前,现将之前创建的file.txt删掉):
在这里插入图片描述

关于O_WRONLY和O_CREAT具体是什么,后文中会给出解释。

结果如下:
在这里插入图片描述

可以看到,当不给定文件的权限属性即第三个参数时,新创建的文件的权限完全是乱的。

关于文件权限,之前的文章中已经说过,这里不再赘述,直接给一个设定了权限的例子:
在这里插入图片描述
在这里插入图片描述

由此可见,当直接调用系统接口时,我们需要给出特定的文件权限。但我们使用C语言中的函数时,却不用关心这一点。这是因为C语言中的各种函数时操作系统的调用接口经过不同的封装而来的。

上述代码中的O_WRONLY和O_CREAT是什么?为什么要将它们进行按位或运算?第二个参数是int类型,这个值又跟它们按位或的结果有什么关系?

答:在操作系统中规定了一些像O_WRONLY这样的值为二进制表示中只有一个比特位为1的值,而第二个参数为"flag"顾名思义就是一个标志位。而我们通常的习惯是用1表示进行某操作,而用0表示不进行某操作。这里的O_WRONLY也是这样的思想。但与我们平常使用的标志位不同的是,由于这里一个参数可能表示要进行多种操作(如上面的例子),因此也需要多种标志。所以将不同的二进制表示中只有一个比特位为1的数进行按位或运算,得到的就是有多个比特位为1的值,如此就可以用一个值表示多种操作了

O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR : 读,写打开
这三个常量,必须指定一个且只能指定一个
O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
O_APPEND: 追加写

下面来介绍open函数的返回值:

对代码稍作改动:
在这里插入图片描述
结果如下:
在这里插入图片描述

显然现在还看不出来什么,也不知道返回值为什么是3,接下来再对代码稍作改动:
在这里插入图片描述

结果如下:
在这里插入图片描述
可以看到,这些返回值是连续的。
open函数返回值为-1时,表示打开文件失败,大于0时表示打开文件成功。但是上面的结果为什么是从3开始的?0、1、2去哪儿了?

答:其实0、1、2代表的分别是stdin、stdout和stderr。这些文件像数组一样整齐地排列着,而且0、1、2这三个文件是默认打开的。
具体的细节下文中详细介绍。

下面来捋清一个思路:
所有的文件操作实际上都是进程对文件的操作,而进程要读取文件就必须要把文件内容加载到内存中。可是通常一个进程会打开多个文件,也就是说进程和打开的文件之间的数量关系是1:n。

那么操作系统如何管理多个被打开的文件呢?

答:和PCB类似,操作系统会用一个结构体来存储每一个文件的相关信息,当要对文件进行操作时,只需要对结构体进行操作即可。


三、文件描述符fd

既然操作系统要对打开的文件进行管理,当OS中有多个进程运行,每一个进程又都打开了多个文件,那么如何区分每一个进程要操作哪些文件呢?

答:在PCB中,有一个结构体----struct files_struct,这个结构体中有一个指针数组----struct files* fd_array[],里面存储的是每一个被进程打开的文件的地址,这些文件地址按照下标从小到大排列在指针数组中。而每一个文件被打开时,都会创建一个结构体----struct file,指针数组中存储的就是这些结构体的地址。而进程默认会打开三个输入输出流----stdin、stdout和stderr,它们占据了数组中0、1、2的下标的位置。这也是上面例子中open返回值从3开始的原因。

在这里插入图片描述

文件描述符fd的本质就是数组下标!!!

如何理解Linux中“一切皆文件”???

答:在系统层面,为了管理硬件,file结构体里面的IO函数根据不同的硬件调用不同的驱动层面的IO函数,实现用文件的角度来管理所有硬件。

在这里插入图片描述
linux一切都可以是文件,并不是说实体是文件,而是以文件的方式去做管理,以文件的角度去看待硬件和软件。这是一种多态思想!

前面说过,文件描述符在管理文件的结构体中是以指针数组的数组下标的形式存在的,而且下标为1和2的指针指向的文件对应的硬件设备都是显示器。那么可不可以直接向1、2对应的文件中写入数据呢?

答案是可以的,如下:
在这里插入图片描述
结果如下:
在这里插入图片描述

再将其向2对应的文件中写入,所得结果是一样的,这里不再粘贴代码和结果。

同样的,文件描述符为0的文件对应的硬件设备是键盘,那么也可以用类似的方法从键盘中读取数据:
在这里插入图片描述

结果如下:
在这里插入图片描述
上面的代码中,关闭0和2的结果是一样的,那它们的区别是什么呢?

答:标准错误不能在输出重定向时不起作用。因为顾名思义输出重定向是对stdout起作用的。关于重定向,下文中给出解释。

文件描述符的分配规则:

前面用代码得出的现象是,进程每次打开的文件返回的fd是从3开始往后排序的,因为前面已经有三个标准输入输出流了。
那么如果把前面三个文件描述符对应的文件关掉一个,新打开的文件的文件描述符会有不同吗?

答案是会的,如下:
在这里插入图片描述
在这里插入图片描述

而当我们关掉2所对应的文件后,执行结果如下:
在这里插入图片描述
所以我们不难得出一个结论:

给新文件分配的fd是从file_array[]数组中,最小的,没有被使用的数组下标


四、重定向

在上面的代码中,我们并没有关掉1所对应的文件,因为那是标准输出,若把它关掉了,最后就不会有任何东西显示到显示器上。那接下来再看这样一段代码:
在这里插入图片描述
结果如下:
在这里插入图片描述

结果显示,执行文件后确实没有任何东西输出到显示器中,但是file.txt中却出现了本该出现在显示器中的内容,为什么呢???

答:在上面的程序中,我们使用了printf,而printf的本质就是向stdout中写入数据,而stdout是一个FILE*类型的指针。在C语言阶段相信大家也都了解过,FILE是一个结构体,而这个结构体里就包含了打开的文件对应的fd。而在上面的代码中,我们将1对应的文件关闭,这是1这个文件描述符就被file.txt占有了。所以当printf去调用系统接口时,就自然向file.txt中写入了数据。这就是重定向

常见的重定向有:>, >>, <
>(输出重定向):

在这里插入图片描述
跟上面的代码相同,可以理解为将echo这个文件对应的fd给了file.txt,所以内容自然也就输出到后者了。

>>(追加重定向):

跟追加写入类似,就是重定向到一个文件中时,并不会清空该文件原有的内容,代码实现如下:
在这里插入图片描述
在这里插入图片描述
操作符实现如下:
在这里插入图片描述

<<(输入重定向)

stdin对应的是显示器,输入重定向就是将指定文件的内容输出到显示器上。代码实现如下:
在这里插入图片描述
在这里插入图片描述

操作符实现:
在这里插入图片描述

如何证明stdin、stdout和stderr的FILE结构体中包含文件描述符0、1、2?

在typedef struct _IO_FILE FILE; 在/usr/include/stdio.h这个文件中有个struct _IO_FILE结构体:
在这里插入图片描述
可见里面确实有文件描述符。
接下来再用代码证明一下:
在这里插入图片描述

在这里插入图片描述

使用dup2系统调用:

如果我们每次进行重定向,都要先关闭一个文件,未免有点麻烦,所以介绍一下dup2这个接口:
在这里插入图片描述

代码如下:
在这里插入图片描述

结果如下:
在这里插入图片描述


五、缓冲区

在引入缓冲区概念之前,先看一段代码:
在这里插入图片描述
结果如下:
在这里插入图片描述

结果显示,最后显示器和file.txt文件中都没有任何数据。
按照前面的思路,显示器中没有数据是因为将原本应该输出到显示器中的数据重定向到了file.txt中。
可是,file.txt中也没有写入数据,为什么呢?

接下来解释缓冲区的概念:
前面说到,FILE这个结构体中包含了文件描述符_fileno。除此之外,其中还包含了C语言中的文件缓冲区。而我们向文件中写入数据时,这些数据会被拷贝到C语言中的文件缓冲区
又因为OS的层状结构,用户对硬件设备进行操作都需要经过OS的系统调用接口。
当程序接收到某种信号后,系统调用接口会将C语言中的缓冲区中的内容加载到OS的内核缓冲区,然后再加载到对应的硬件外设中。

而用户和OS进行交涉时的缓冲方法有以下三种:
①无缓冲
②行缓冲(遇到\n就进行缓冲区刷新)比如向显示器中写入
③全缓冲(当缓冲区存满或进程退出时进行缓冲区刷新)比如向文件中写入

而上面代码中,1所对应的显示器本该是行缓冲,但我们将其重定向到了fd,也就是将其变为向文件中写入,对应的缓冲方法也就变成了全缓冲。这些数据本该在进程退出后刷新到文件中,但我们在代码的最后关闭了文件对应的fd,这也就意味着OS并没有来得及将C语言中的缓冲区中的内容加载到OS的内核缓冲区中,自然也就没有刷新缓冲区,自然也就没有写入。

将最后的close去掉之后结果如下:
在这里插入图片描述

下面再看一段代码:
在这里插入图片描述
然后将其重定向到file.txt中,结果如下:
在这里插入图片描述

可以看到,C语言接口中的数据都出现了两次,但是系统接口只出现一次,为什么呢?

因为fork创建了子进程,发生了写时拷贝,导致重定向时,写入了两次数据。而系统接口与C语言接口不同,它不用将用户层面的缓冲区中的数据加载到OS的内核缓冲区,所以只出现一次。

补充:若要强制刷新缓冲区,可在代码结束之前使用fflush()函数,这里不再演示了。

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

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

相关文章

20221203英语学习

今日新词&#xff1a; lecturer n.讲演者, 讲课者, 讲授者, &#xff08;尤指英国大学的&#xff09;讲师 hypothetical adj.假设的, 假定的 rather adv.相当; 宁可, 宁愿; (常用于or后) 更确切些 ring n.戒指, 指环, 环状物; 圆圈; 敲钟, 按铃; 铃声, 钟声 yes adv.(回答…

1.2 Hadoop简介-hadoop-最全最完整的保姆级的java大数据学习资料

文章目录1.2 Hadoop简介1.2.1 什么是Hadoop1.2.2 Hadoop的起源1.2.3 Hadoop的特点1.2.4 Hadoop的发行版本1.2.5 Apache Hadoop版本更迭1.2.6 第六节 Hadoop的优缺点1.2 Hadoop简介 1.2.1 什么是Hadoop ​ Hadoop 是一个适合大数据的分布式存储和计算平台 ​ 如前所述&#x…

html5期末大作业:基于HTML+CSS技术实现——传统手工艺术雕刻网站(3页)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

深度强化学习的组合优化[1] 综述阅读笔记

文章目录前言零、组合优化问题基础1. 定义&#xff08;1&#xff09;定义&#xff08;2&#xff09;常见问题2. 方法&#xff08;1&#xff09;精确方法&#xff08;2&#xff09;近似方法&#xff08;3&#xff09;深度学习方法3. 文章架构一、概述1. 神经网络&#xff08;1&a…

【图文教程】若依前后端分离版本-菜单怎么设置

在使用若依前后端分离版本作为基础架构的时候&#xff0c;需要新建菜单怎么操作&#xff1f;本文凯哥将详细讲解怎么添加。 1&#xff1a;登录若依系统后&#xff0c;[系统管理]>>[菜单] 先新建一个一级菜单。可以模仿【系统管理】菜单来创建&#xff1a; 系统菜单&am…

226.翻转二叉树

226.翻转二叉树 题目 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1]示例 2&#xff1a; 输入&#xff1a;root [2,1,3] 输出&#xff1a;[2,3,…

神器必会!特别好使的编辑器Source Insight

已剪辑自: https://mp.weixin.qq.com/s/nA9VJeMjC4gDpDSI8r-2FA “Source Insight&#xff08;以下简称SI&#xff09;是世界上最好的编辑器”&#xff0c;说这句话不知道会不会出门被打呢&#xff1f;-_- 中国古话说得好&#xff0c;“文无第一&#xff0c;武无第二”&#x…

都2202年了,不会有人还不会发布npm包吧

背景 介绍了axios的二次封装用于支持常规请求及自定义请求&#xff0c;并对同一时间内的相同请求做拦截处理&#xff08;如果您没有阅读过这篇文章&#xff0c;建议您花费3分钟大致了解&#xff09;。恰逢最近准备写一个跨框架组件库&#xff08;工作量很大&#xff0c;前端三…

浏览器存储(webStorage)常用API以及简单使用

前言 最近正值世纪杯期间&#xff0c;不知道大家心目中的球队成绩如何&#xff0c;在此期间我学了浏览器存储(webStorage)以及API,下面就分享给大家 浏览器存储(webStorage) 存储内容不是Vue团队打造的&#xff0c;原本的js中就有&#xff0c;所以可以不适用脚手架编译&…

UGUI性能优化学习笔记(番外)一些零星的优化点

一、Overdraw 1.1 什么是Overdraw overdraw也就是过度绘制&#xff0c;是指在每个渲染周期内&#xff0c;屏幕上每个像素最理想只渲染一次&#xff0c;但是由于UI元素的重叠会导致像素会被渲染多次&#xff0c;每次渲染从CPU阶段到GPU阶段会消耗大量资源&#xff0c;如果这种…

[附源码]计算机毕业设计JAVA学习资源共享与在线学习系统

[附源码]计算机毕业设计JAVA学习资源共享与在线学习系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; S…

Spring统一异常处理捕获不到CompletableFuture异步编排中的异常的问题

Spring统一异常处理捕获不到CompletableFuture异步编排中的异常的问题Spring统一异常处理捕获不到CompletableFuture异步编排中的异常的问题Spring统一异常处理简单例子多线程无法捕获场合正常场合&#xff08;不使用异步编排&#xff09;代码ServiceImExcpHandlerController结…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java高校车辆租赁管理系统23qhn

要开始我们毕业设计的第一步的关键就是选好我们的课题&#xff0c;有的同学开始选题的时候想着按照传统的课题延续下去&#xff0c;在设计题目时&#xff0c;不要过于笼统广泛&#xff0c;选择题目其实并不难&#xff0c;要多从自身的角度出发&#xff0c;要结合你们当前所处的…

【java】网络编程

文章目录网络编程概述基本概念IP地址概念InetAddress端口与协议概念UDP通信编程UDP发送数据UDP接受数据UDP通信程序练习TCP通信编程TCP发送数据TCP接收数据TCP通信程序练习网络编程概述 基本概念 IP地址概念 终端检查&#xff1a; InetAddress package heima.网络编程;impor…

nginx配置文件 location语法

1&#xff1a;nginx官方文档给出location语法如下&#xff1a; location [|~|~*|^~] uri { ....... }2&#xff1a;路径匹配 开头表示精确匹配。如 A 中只匹配根目录结尾的请求&#xff0c;后面不能带任何字符串&#xff1b;^~ 开头表示uri以某个常规字符串开头&#xff0c;不是…

字符串中第二大的数字(遍历)

力扣链接&#xff1a;力扣 给你一个混合字符串 s &#xff0c;请你返回 s 中 第二大 的数字&#xff0c;如果不存在第二大的数字&#xff0c;请你返回 -1 。 混合字符串 由小写英文字母和数字组成。 示例 1&#xff1a; 输入&#xff1a;s "dfa12321afd" 输出&…

Keras生成式学习(五)

生成式深度学习 生成式学习即创造学习&#xff0c;深度学习开始创造 一、使用LSTM 生成文本 给定前面的标记&#xff08;token&#xff0c;通常是单词或字符&#xff09;&#xff0c;能够对下一个标记的概率进行建模的任何网络都叫作语言模型&#xff08;language model&…

Android Camera性能分析 - 第25讲 CameraServer LatencyHistogram简介

本讲是Android Camera性能分析专题的第25讲 ​&#xff0c;我们介绍CameraServer LatencyHistogram简介&#xff0c;包括如下内容&#xff1a; LatencyHistogram是什么CameraServer默认有哪些Latency的Histogram如何获取CameraLatencyHistogram数据CameraLatencyHistogram类详…

Java项目:SSM CRM人事管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 CRM人事管理系统&#xff0c;主要功能有&#xff1a; 用户管理&#xff1a;用户查询、添加用户、编辑、删除&#xff1b; 职位管理&#xff1a…

Java基于springboot+vue的防护用品销售购物商城系统 前后端分离

开发背景 随着近些年疫情的爆发人们对个人医疗相关防护也越来越重视了&#xff0c;尤其是在疫情开始之初&#xff0c;人们对疫情感受到非常的恐慌&#xff0c;虽然在国家和领导人的领导下疫情一次次的得到了控制&#xff0c;但是我们还是要做好个人防护&#xff0c;为了让更多…