【Linux笔记】文件描述符与重定向

news2024/9/21 18:42:27

一、Linux关于文件操作的一些系统调用

1、open和close

我们在C语言阶段已经学过很多文件操作的函数,今天我们要来看看操作系统中对于文件是怎么操作的。

1.1、open与close的用法

C语言的库函数中有很多关于文件操作的接口,包括fopen、fclose、fprintf、fput……

但是系统中对于文件操作的系统调用接口就就没有这多多了,今天我们最主要要看的是四个接口:open和close、read和write。

这两个函数顾名思义一个是打开文件的接口一个是关闭文件的接口,他俩的功能和
C语言的fopen和fclose的功能类似。但也有些不同的地方,第一个不同的地方是返回值,C语言的ffopen和flose的返回值是一个IFLE*的结构体指针,但是open和close的返回值是一个整型,这个返回值就是我们后面要细聊的“文件描述符”。

对于open接口,它的三个参数需要解释一下,第一个参数就是我们要打开的文件的路径和文件名,这个其实和C语言中的fopen一样,第二个参数是以什么方式打开,第三个参数是打开文件时的权限。

我们先来看看他俩的基础用法:

这open的第二个参数的传法有点儿奇怪,这其实是一个类似于位图标记的传参方式,我们后面再来简单的解释一下。上面的代码中的O_CREAT | O_RDONLY表示的是文件不存在就创建,并且是以只读方式打开。

而close只需要传一个文件描述符,所以我们现在虽然还没有细讲文件描述符是什么,但是我们可以肯定的是文件描述符一定能定位一个文件。

上面的代码运行后我们当前目录中就创建出来了一个test.txt文件:

1.2、关于open函数第二个参数flags的理解

open接口的第二个参数flags其实就是利用“位图”的思想,以一个参数来达到类似C语言中可变参数列表的传参方式。

我们可以以一个小例子来理解一下:

运行结果:

从上面可以看出,flags传参其实是通过位运算来达到的,因为这里的Print1~4都只有一个比特位为1,而且位置都是互不相同的。所以按位或之后会将阐述flags的对应比特位都置为1,所以Print函数里面在匹配的时候只要对应位置为1的宏都会被匹配上,也就达到了类似传递多个参数的目的啦。

1、read和write

有了open和close我们可以打开和关闭一个文件,那接下来就是要对被打开的文件的进行操作了。

今天主要介绍两个对文件读和写的接口read和write:

先来看看write接口,write的第一个参数表示要将信息写入到哪一个文件描述符中(文件描述符定位一个文件),第二个参数表示信息从哪一个缓冲区中读取,第三个参数表示的是缓冲区的大小。

首先如果要向文件中写入,要先将文件的打开方式添加上O_WRONLY,表示以只写方式打开。

运行之后我们就可以看见文件中就有了我们写入的内容了:

而如果我们修改写入的内容我们会发现一个与C语言的文件操作不太一样的现象:

我们会发现第二次写入的时候,并没有将文件内容清空,而是从文件起始位置开始写,并覆盖原来的内容。不像C语言的fopen以写方式打开每次都会将文件内容清空。

而如果我们将打开文件的方式再加上O_TRUNC,那么在每次写入的时候就会将文件先清空了:

如果我们将O_TRUNC改成O_APPEND,那么写入的方式就变成了“追加”:

既然我们可以向文件中写入,那肯定就能向文件中读取了,这就要用到read接口了:

read的参数和write的参数一样,第一个参数fd表示要从哪一个文件描述符中读取,第二个参数表示要将内容读取到哪一个缓冲区中,第三个参数表示缓冲区的大小。

首先如果要想从文件中读取数据,先要将文件的打开方式修改成O_RDONLY,表示易只读方式打开。

运行后我们就可以在命令行中打印出来文件中的内容了:

二、文件描述符的本质

1、操作系统怎么管理被打开的文件

在了解文件描述符的本质之前,我们先打印出文件描述符看看,它是个什么数字:

我们看到当前的文件描述符是3,是一个小整数。

不急,既然进程可以打开一个文件,那么进程就一定能打开多个文件,我们打开多个文件试试,看看多个文件的文件描述符是怎样的:

从结果中我们可以看出,文件描述符都是一些小整数,并且是按顺序的,先打开的文件的文件描述符就小,后打开的就大。

文件描述符的本质:

既然操作系统是管理硬件资源和软件资源的系统软件,而我们打开的文件在没被使用时存住在磁盘中,属于硬件资源,被打开时我们知道先将文件内容加载到内存,也属于硬件资源。所以操作系统肯定也要将被打开的文件进行管理。

那怎么管理呢?我们知道操作系统也还是C语言写的,所以操作系统管理资源其实也就是通过C语言编程管理资源,那就“先描述,再组织”咯。

所以操作系统会为每个被打开的文件创建一个类型为struct file的结构体对象,这个file对象里存储着许多该文件的信息,大小、创建时间、位置(磁盘位置)、修改时间等等。并且它们之间是以双向链表的形式连接起来的:

那这是操作系统做的事,我们的进程又怎么找到它打开的文件所对应的struct file结构体呢?

我们知道操作系统会为每个运行的进程创建一个构造一个testk_struct的结构体对象,这个对象中存着很多管理进程相关的信息,而其中一个与我们今天所讲的文件相关的就是一个类型为struct flies_strcut* 类型的结构体指针,该结构体指针指向的是一个类型为struct file_struct类型的结构体对象,这个结构体对象就是一个“进程描述符表”:

而这个文件描述符表里面最重要的一个成员就是一个类型为struct file* fd_array[]指针数组,这个指针数组中存的就是一个一个指向该进程打开文件的struct file对象的指针:

有数组就有下标。

所以,文件描述符的本质其实就是一个数组的下标,这个数组就是文件描述符表中的struct file* fd_array[]数组。

所以进程在访问对应的文件的时候,其实是先拿着文件描述符找到文件描述符数组中对应下标中的地址,再通过地址找到对应的struct file对象,再通过这个对象访问到磁盘中的文件。

三、重定向

1、先看看重定向的现象

Linux操作系统中有一些指令,可以将原本要打印到命令行中的内容打印到一个文件中,这个其实就是我们今天要讲的重定向——输出重定向:

上面的结果就是直接将,ls的内容打印到了test.txt文件中,而如果我们只输入一个> test.txt指令,会发现原来的文件内容被清空了:

所以,虽然我们现在还没讲重定向的原理,但是我们可能能推断出,输出重定向也是一定是要先打开文件,并且打开文件的方式是O_WRONLY | O_TRUNC。

而除了输出重定向外,我们还有一个指令叫做“追加重定向”:

很明显,这个追加重定向在打开文件的时候一定是以O_WRONLY | O_APPEND的方式打开的。

而除了输出重定向>,我们还有一个指令叫做输入重定向,他可以将原本要从键盘中读入并打印到显示器上的操作指令修改成从某一个文件中读取并打印到显示器上。

例如我们有一个指令cat,它单独写的时候其实是从键盘中读入内容再打印到显示器上的:

而如果加上<,它就会先从指定的文件中读取内容,然后再打印到显示器上:

所以我们现在就明白了,我们以前使用的cat指令查看文件内容的时候,其实是执行了一个输入重定向,只是cat后面的<操作符可以省略罢了

2、操作系统会为每个进程默认打开的三个文件

那上面这些重定向的原理是怎么样的?不急,我们还先了解一些一下,操作系统为每一个进程默认打开的三个文件。

不知道大家是否会有一个疑问,就是我们上面打印出来的文件描述符都是从3开始的,而上面说过文件描述符的本质其实就是数组下标,但是数组下标都是从0开始的,那么0、1、2下标到哪去了呢?

可能大家在C语言阶段都知道了,我们C语言程序在运行时候,系统都会默认为我们打开三个文件:标准输入、标准输出、标准错误。也就是stdin、stdout、stderr。

那怎么证明呢?

我们运行程序的时候什么也不做,就打印出这三个文件文件描述符,操作系统其实是支持我们直接打印出这三个文件的文件描述符的:

我们在学习C语言的时候就知道,stdin这些文件其实就是一个结构体指针,而这个结构体指针执行的结构体之中就有一个_fileno的成员表示的就是改文件的文件描述符。

既然操作系统都帮我们直接打开了,那我们肯定就可以直接使用了,而我们的read和write是可以直接从文件描述符中读取的,所以我们就可以看到下面这样奇怪的现象:

当然我们也可以直接标准输入的文件描述符0配合read接口,读取内容再打印到显示器stdout上:

3、怎么在自己的代码中实现重定向

如果想要在我们写的代码中实现一个重定向,我们先要来看看文件描述符的分配规则,我们已经知道,在一个C语言程序中新打开一个文件,它别分配到的文件描述符是3:

那我们知道,stdin、stdout、stderr是程序启动时候就已经加载了的,并且文件描述符是0、1、2,我们也可以直接使用它们,那我们当然也可以直接关闭它们了!

就那标准输入来说,如果我们直接关闭了标准输入,那么新的开的文件被分配到的文件描述符又是多少呢?

 结果是0,那我们现在直接关闭标准错误,那是不是分配到的文件描述符是2呢?

结果显然是的。

所以现在我们就可以输出一个结论:文件描述符的分配规则是选择最低位置的没有被使用的文件描述符分配!

那如果我们直接关闭标准输出呢?文件描述符是不是1呢?

但是我们看结果怎么什么东西都没有打印呢?

这是因为我们已经把标准输出已经关了,也就是把显示器直接关了!信息当然没有打印到显示器上面了,那信息打印到哪里了呢?

其实信息打印到了文件中了,但是如果我们就以上的代码运行后,查看test.txt文件是看不到任何内容的:

如果想要看到,就必须先要刷新一下“用户级缓冲区”:

其实我们在将内容写到stdout中的时候是并没有直接写到stdout之中的,而是在中间经过了一个C语言提供的用户及缓冲区,只有缓冲区遇到刷新条件的时候才会将内容刷新到stdout中。这个刷新条件其中一个就是程序结束,但是我们这里在程序结束之前其实就将stdout关闭了,所以信息也就没能刷新到stdout中了。(这里的stdout其实是我们打开的test.txt文件)。

所以至此,我们可以再输出一个结论,文件描述符的本质其实就是修改文件描述符表中特定下标内的内容!

如下图:

如上图,我们程序开始运行的时候就已经打开的这三个文件,但是我们一开始就将stdout给关闭了,所以根据文件描述符的分配规则,我们新打开的test.txt文件分配到的文件描述符就是1。

而这个系统对于printf,printf是不知道的,因为printf里面执封装了stdout,或者说pritnf只封装了文件描述符1,所以printf只认文件描述符1。你下层的操作printf都不管,它只知道调用的时候就去文件描述符1里面去读取,所以读到的就是文件test.tx里面的内容。

所以根据上面的结论我们可以将输入重定向给实现出来:

scanf默认是从键盘也就是stdin中读取数据,但现在我们文件描述符1的内容已经变成了test.txt文件的地址了,所以scanf也就去test.txt中读取数据了。

但是每次实现重定向如果都要我们先关闭,对应的标准输入出入中的一个未免有点太麻烦,所以系统也给我们提供了一个文件描述符表级别的数字内容交换接口:

它有两个参数:oldfd和newfd,两个参数都是文件描述符。

而dup2接口的作用就是:让oldfd去覆盖newfd,也就是最后只剩下oldfd。

所以如果我们想要用dup2来实现,输出重定向的话就应该这样写:

同样的,输入重定向我们也可以一键实现了:

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

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

相关文章

《Docker技术革命:从虚拟机到容器化,全面解析Docker的原理与应用-上篇》

文章目录 Docker为什么会出现总结 Docker的思想Docker历史总结 Docker能干嘛虚拟机技术虚拟机技术的缺点 容器化技术Docker和虚拟机技术的区别 Docker概念Docker的基本组成镜像&#xff08;image)容器&#xff08;container&#xff09;仓科&#xff08;repository&#xff09;…

基于大数据的B站数据分析系统的设计与实现

摘要&#xff1a;随着B站&#xff08;哔哩哔哩网&#xff09;在国内视频分享平台的崛起&#xff0c;用户规模和数据量不断增加。为了更好地理解和利用这些海量的B站数据&#xff0c;设计并实现了一套基于Python的B站数据分析系统。该系统采用了layui作为前端框架、Flask作为后端…

防火墙综合拓扑(NAT、双机热备)

实验需求 拓扑 实验注意点&#xff1a; 先配置双机热备&#xff0c;再来配置安全策略和NAT两台双机热备的防火墙的接口号必须一致如果其中一台防火墙有过配置&#xff0c;最好清空或重启&#xff0c;不然配置会同步失败两台防火墙同步完成后&#xff0c;可以直接在主状态防火墙…

逻辑回归与感知机详解

一逻辑回归 采用log函数作为代价函数 1 用于二分类问题 2 cost成本函数定义 3 求最小值&#xff0c;链式求导法则 4 梯度下降法 5 结构图表示 二 感知机 样本点到超平面距离法 1 线性二分类问题 2 点到直线距离 3 更新w 和b 参数 4 算法流程 5 例子

真机调试,微信小程序,uniapp项目在微信开发者工具中真机调试,手机和电脑要连同一个wifi,先清空缓存,页面从登录页进入,再点真机调试,这样就不会报错了

微信小程序如何本地进行真机调试&#xff1f;_unity生成的微信小程序怎么在电脑上真机测试-CSDN博客 微信小程序 真机调试 注意事项 uniapp项目在微信开发者工具中真机调试&#xff0c;手机和电脑要连同一个wifi&#xff0c;先清空缓存&#xff0c;页面从登录页进入&#xf…

mac系统内存越来越大怎么清理?CleanMyMac X轻松解决

MacBook作为一款优秀的笔记本电脑&#xff0c;自带的macOS操作系统更加稳定、安全、易用。但是&#xff0c;随着安装的应用越来越多&#xff0c;应用的功能越来越强大&#xff0c;占用的内存也越来越多&#xff0c;从而导致电脑变慢。那么&#xff0c;MacBook如何清理内存呢&am…

【Node.js基础】Node.js的介绍与安装

文章目录 前言一、什么是Node.js&#xff1f;二、安装Node.js2.1 Windows系统2.2 macOS系统2.3 Linux系统 三、运行js代码总结 前言 随着互联网技术的不断发展&#xff0c;构建高性能、实时应用的需求日益增长。Node.js作为一种服务器端运行时环境&#xff0c;以其事件驱动、非…

CentOS gui 图形界面显示文字乱码

一、现象 CentOS&#xff08;CentOS 7.5&#xff09;控制台下显示中文乱码&#xff1a; 或者通过X11 Forwarding远程显示CentOS的图形化程序文字乱码&#xff1a; 二、解决方法 安装中文语言包&#xff1a; yum install kde-l10n-Chinese 注&#xff1a;网上有些文章会推荐安…

k8s中调整Pod数量限制的方法

一、介绍 Kubernetes节点每个默认允许最多创建110个pod&#xff0c;有时可能由于主机配置扩容的问题&#xff0c;从而需要修改节点pod运行数量的限制。 即&#xff1a;需要调整Node节点的最大可运行Pod数量。 一般来说&#xff0c;只需要在kubelet启动命令中增加–max-pods参数…

代码随想录 Leetcode222.完全二叉树的节点个数

题目&#xff1a; 代码&#xff08;首刷自解 2024年1月30日&#xff09;&#xff1a; class Solution { public:int countNodes(TreeNode* root) {int res 0;if (root nullptr) return res;queue<TreeNode*> deque;TreeNode* cur root;deque.push(cur);int size 0;w…

搭建WebGL开发环境

前言 本篇文章介绍如何搭建WebGL开发环境 WebGL WebGL的技术规范继承自免费和开源的OpenGL ES标准&#xff0c;从某种意义上说&#xff0c;WebGL就是Web版的OpenGL ES&#xff0c;而OpenGL ES是从OpenGL中派生出来的。他们的应用环境有区别&#xff0c;一般来说&#xff1a;…

走迷宫-bfs

package Test;import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;public class Main {static int N 110,hh 0,tt -1,n,m;static int[][] g new int[N][N]; //用来存储迷宫static int[][] d new int[N][N]; //用来存储d[i…

jenkins部署(docker)

docker部署&#xff0c;避免安装tomcat 1.拉镜像 docker pull jenkins/jenkins2.宿主机创建文件夹 mkdir -p /lzp/jenkins_home chmod 777 /lzp/jenkins_home/3.启动容器 docker run -d -p 49001:8080 -p 49000:50000 --privilegedtrue -v /lzp/jenkins_home:/var/jenkins_…

【Go 快速入门】数组 | 切片 | 映射 | 函数 | 结构体 | 方法和接收者

文章目录 数组切片append 函数copy 函数删除元素 映射delete 函数 函数init 特殊的函数defer 语句panic / recover 错误处理 类型结构体内存对齐JSON 序列化与反序列化方法和接收者 项目代码地址&#xff1a;03-ArraySliceMapFuncStruct 数组 基本格式&#xff1a;var 数组变…

从0开始搭建若依微服务项目 RuoYi-Cloud(保姆式教程完结)

文章接上一章&#xff1a; 从0开始搭建若依微服务项目 RuoYi-Cloud&#xff08;保姆式教程 一&#xff09;-CSDN博客 四. 项目配置与启动 当上面环境全部准备好之后&#xff0c;接下来就是项目配置。需要将项目相关配置修改成当前相关环境。 数据库配置 新建数据库&#xff…

ModelArts加速识别,助力新零售电商业务功能的实现

前言 如果说为客户提供最好的商品是产品眼中零售的本质&#xff0c;那么用户的思维是什么呢&#xff1f; 在用户眼中&#xff0c;极致的服务体验与优质的商品同等重要。 企业想要满足上面两项服务&#xff0c;关键在于提升效率&#xff0c;也就是需要有更高效率的零售&#…

利用操作符解题的精彩瞬间

下面是链接为了解释练习2的并且还有与操作符相关的知识。 C语言与操作符相关的经典例题-CSDN博客 操作符详解&#xff08;上&#xff09;-CSDN博客 操作符详解&#xff08;下&#xff09;-CSDN博客 目录 练习1&#xff1a;在一个整型数组中&#xff0c;只有一个数字出现一…

化工企业能源在线监测管理系统,能源管理新利器

化工企业在开展化工生产活动时&#xff0c;能源消耗量较大&#xff0c;其节能潜力空间也较大&#xff0c;因此必须控制能耗强度&#xff0c;促进能效水平的稳步提升。化工企业通过能源现状的分析&#xff0c;能够实现能源使用情况的实时反馈与监管&#xff0c;从而达到节能减排…

铸就企业品牌之道之软文发稿

企业品牌是企业在市场中的独特身份&#xff0c;通过标志、名称和视觉元素等构建&#xff0c;为企业赋予个性和识别度。品牌不仅是产品和服务的代名词&#xff0c;更是传递价值观、建立信任和塑造企业形象的关键工具。成功的品牌能够吸引目标受众&#xff0c;建立稳固的市场地位…

[AI]文心一言爆火的同时,ChatGPT带来了这么多的开源项目你了解吗

前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff1a;https://www.captainbed.cn/z ChatGPT体验地址 文章目录 前言4.5key价格泄漏ChatGPT4.0使用地址ChatGPT正确打开方式最新功能语音助手存档…