【Linux】文件描述符与重定向操作

news2024/12/29 10:34:21

系列文章

收录于【Linux】文件系统 专栏

对于Linux下文件的写入与读取,以及文件原理还有疑惑的可以看看上一篇文章浅谈文件原理与操作。


目录

系列文章

再谈文件描述符

​编辑

IO函数的本质

一切皆文件

文件重定向

原理

系统接口


再谈文件描述符

🍧上一篇文章中,我们就提到了 open 的返回值即 fd,又称为文件描述符,之后我们进行读写操作时就需要用到 fd。

🍧而 fd 究竟是什么来头,下面我们来一起揭秘。

🍧我们首先先打开一个文件,再打印返回的 fd ,观察一下输出的结果。

#define myfile "log.txt"

int main()
{
    int fd = open(myfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
    cout << fd << endl;
    return 0;
}

🍧输出的是 3,这个 3 有什么特殊的含义吗?是意味着文件是打开的第三个文件吗?

🍧其实,任何一个进程在启动的时候,默认会打开当前进程的三个文件

标准输入标准输出标准错误
C语言stdinstdoutstderr
CPPcincoutcerr
文件类型键盘文件显示器文件显示器文件
fd012

🍧因为一开始打开了三个文件,从而我们第一次打开文件时 fd 便从 3 开始了!讲到这里,你应该能大概猜到 fd 是什么了吧。

🍧没错,就是数组下标

🍧之前我们讲过,文件是用户通过调用进程打开的,于是在内存之中就会存在一个个包含文件大部分属性的 struct file。当我们找到 struct file 时就意味着我们找到了被打开的文件,而用户能访问到的只有进程的 task_struct。

🍧因此,系统需要将该进程打开的文件管理起来,就有了 file_struct,其中的 fd_array 就负责存储 struct file 的指针。最后 tack_struct 再使用指针与 file_struct 关联起来即可。其中fd就是 fd_array 数组的下标。

🍧因此,只要知道 fd 就能在 fd_arry 中的位置就能找到对应 struct file 的指针,进而找到 struct file。

🍧这样的结构设计同时还实现了进程管理与文件系统解耦合,只用指针进行轻度的连接。

IO函数的本质

🍧不仅如此,每一个 struct file 都有属于自己的缓冲区,本质上我们调用 wirte 函数就是将数据拷贝到这个缓冲区之中,至于什么时候刷新到磁盘中的指定位置,则有 OS 自主决定。

🍧而 read 前便会将磁盘的数据拷贝到缓冲区里,之后找到缓冲区读取数据。

即 IO 函数的本质就是拷贝函数,在用户空间和内核空间之间来回拷贝。

一切皆文件

🍧我们常说:“Linux 下一切皆文件。” 这句话又该如何理解呢?而其中最困扰我们的就是如何将外设也看成文件。

🍧由于每种外设读取与写入的方式都不相同,因此需要安装特定的驱动程序才能使用。

🍧而有些外设只读不写或是只写不读就没有对应的驱动程序。 

🍧这时候我们再回过头来看struct file这个结构。

struct file
{
    //文件权限
    //文件大小
    //缓冲区
    ...
    int (*readp)(int fd,char* buffer,int size);
    int (*writep)(int fd,char* buffer,int size);
};

🍧其中两个函数指针则分别指向了驱动程序的读写函数。 

🍧即:操作系统的文件操作只是将数据拷贝到文件的缓冲区中,之后再调用目标文件的自身的读写操作,完成读写。 

🍧每个设备都可以将其驱动程序填入文件的结构体,因此在进程看来外设也是文件。同时,我们在访问 OS 时都是通过进程代为执行,而进程认为一切皆 struct file 但其中究竟是设备还是文件,进程不知道也不关心。由此在用户看来最终就是一切皆文件的现象。

文件重定向

🍧之前我们就在命令行使用过文件重定向的操作,将原本要输出到显示器的信息重定向到文件之中

echo hello log.txt > log.txt   //将语句写入到log.txt文件中

这种操作是如何用代码的实现的呢?下面一起来看看吧。

原理

🍧首先,我们得先了解一下文件描述符的分配规则: 选文件描述表中最小的、没有被使用的数组元素,分配给新文件

🍧我们可以用一段代码来验证一下。

int main()
{
    int fd1 = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC, 0666);
    cout<<fd1<<endl;
    close(fd1);
    int fd2 = open("out.txt",O_WRONLY | O_CREAT | O_TRUNC, 0666);
    cout<<fd2<<endl;
    close(fd2);
    return 0;
}

 

🍧可以看到,两次 fd 都是 3,这是因为一开始的 fd 就是从 3 开始的,打印 fd 后关闭文件再打开一个新文件时,文件描述表中还是只有三个文件,于是 fd 还是从 3 开始分配。

🍧这时候,我们便想:前面三个文件能不能关闭呢?我们再次打开文件会发生什么事呢?

#define myfile "log.txt"

int main()
{
    close(0);
    int fd = open(myfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
    cout << fd << endl;
    return 0;
}

 

🍧很明显,新文件的 fd 便是刚才关闭的 0。若我们关闭1号标准输出文件,然后再调用 printf 或 cout 输出流进行输出呢?

🍧这次程序运行完我们却没有看到显示器有进行输出,我们查看 log.txt 这个文件便会发现,信息被输出到了文件之中。

🍧从操作系统的角度出发,OS 只知道标准输出的 fd 是 1,当遇到需要输出的情况时就找到 fd 对应的文件进行输出。因此,OS 并不知道此时 1 已经不是对应着标准输出这件事,便直接向文件写入。

🍧即重定向的原理便是:在上层不知道的情况下,在OS内部更改进程对应的文件描述符表中特定下标的指向

系统接口

🍧若是每次要重定向时都要像上面那样先关闭文件,再打开一个新文件匹配 fd 未免有些麻烦。

🍧OS内部有一个函数便可实现重定向的功能。

🍧我们使用 dup2 便可以直接实现函数的重定向,第一个参数原来的 fd第二个参数则是想要关闭文件的 fd

int main()
{
    int fd = open(myfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
    dup2(fd, 1);
    printf("hello this is stdout\n");
    close(fd);
    return 0;
}

 🍧借助这个功能,我们可以简单的对运行结果进行一个记录,输出和错误分别记录在不同的文件中。

int main()
{
    int fd1 = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    int fd2 = open("error.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    dup2(fd1,1);
    dup2(fd2,2);
    cout << "hello cout" << endl;
    cerr << "hello cerr" << endl;
    close(fd1);
    close(fd2);
    return 0;
}


🍧好了,今天 文件描述符与重定向操作 的相关内容到这里就结束了,如果这篇文章对你有用的话还请留下你的三连加关注。

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

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

相关文章

C语言 指针(特别篇)

本篇目录 C语言 指针&#xff08;特别篇&#xff09;内存地址简要介绍C语言指针C语言的指针可以指向什么?取地址符 &&#xff08;Address-of Operator&#xff09;C语言中的 * 号运算符示例集&#xff1a;指向变量的指针指向数组的指针指向字符串的指针二级指针指针数组的…

doris docker部署 1.2.4.1版本

目录 写在前面 镜像编译 准备工作 下载编译好的包 fe be 编辑初始化文件 fe be 编辑Dockerfile fe be 构建镜像 fe be 构建结果 镜像运行 fe be 修改配置 添加udf依赖 启动be 注册be 错误分析 写在前面 以下操作语句按顺序执行即可&#xff0c;如果需要…

泛型Generic

泛型 1. 介绍1.1 使用泛型的好处 2. 自定义泛型结构&#xff1a;泛型类、泛型接口&#xff1b;泛型方法2.1 泛型类、泛型接口2.2 泛型方法 3. 其他3.1 泛型在继承方面的体现3.2 通配符使用3.2.1 有限制的通配符 1. 介绍 把元素的类型设计成一个参数&#xff0c;这个类型参数叫…

数组6大排序算法

快速排序 核心算法&#xff1a; 1.取一个基准值&#xff08;一般是数组中间的元素&#xff09;&#xff0c;遍历数组&#xff0c;比基准值大的放右边&#xff0c;小的放左边&#xff0c;相等的则不动 2.分别创建三个数组来存储元素&#xff0c;最后将三个数组拼接起来 3.循…

Java替换Jar文件中的class文件方法

备份源文件 文件不重要的话可以不需要备份&#xff0c;线上环境务必备份方便回滚 mkdir bak cp test.jar bak 查看class文件所在目录 jar -tvf test.jar | grep Time.class 标红内容就是需要替换的class文件&#xff0c;如果有多个文件需要替换依次执行2&#xff0c;3步骤…

webSocket实时通信02——基于Spring【纯后端——JAVA】

这里是基于Spring整合websoket后来实现的实时通信&#xff0c;这里只有java的代码&#xff0c;通过在线网站 http://www.websocket-test.com/测试即可 1. 导包 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-star…

网络安全合规-职业发展路线

网络安全人才一将难求&#xff0c;缺口高达 95% 在以前&#xff0c;很多政企单位在进行 IT 部门及岗位划分时&#xff0c;只有研发和运维部门&#xff0c;安全人员直接归属到基础运维部&#xff1b;而现在&#xff0c;越来越多单位为了满足国家安全法律法规的要求&#xff0c;…

终极攻略!如何彻底防止Selenium被检测!

在使用Selenium进行爬虫时&#xff0c;许多朋友都会遇到各种反爬措施。 实际上&#xff0c;在绝大多数情况下&#xff0c;网站轻而易举地能够检测出你正在使用WebDriver而非标准浏览器。 本文将详细介绍如何有效防止检测的方法。 在一篇公众号文章《别去送死了。Selenium 与…

【重磅】智能未来 —— 人工智能与城乡规划的交叉对话(CSDN深圳城市开发者社区首场线下技术交流活动)

文章目录 活动主题活动海报活动时间活动地点互动有礼加入有礼赠书简介特别说明 活动主题 智能未来-人工智能与城乡规划的交叉对话 —— 以 AI 为核心主题&#xff0c;探索 AI 在 智慧城市、智能视频、智能编程 等重点领域的技术发展和实战落地。 活动海报 活动时间 2023.06…

加密与解密 调试篇 动态调试技术 (五)-WinDbg

windbg主要厉害的地方是在他可以对内核调试 并且本身微软的产品 对windows调试适配度够高 注意 windbg给出的图形操作并不好用 主要是使用命令行来进行操作 我们省略安装 直接进入调试 file 可以打开软件 可以附加也可以分析dump文件还可以进行内核和 远程调试内核调试分为…

招商基金资深架构师教你如何搭建统一监控平台

随着数字化进程的加速和业务的高速发展&#xff0c;系统的复杂程度日益升级&#xff0c;为确保业务系统的连续性和稳定性&#xff0c;越来越多的企业想要建设统一的监控平台&#xff0c;但却不知道从哪里开始着手。比如&#xff1a; 有些企业会直接将监控系统页面集成到统一监…

SpringBoot配置文件application.yml的理解

一、存放位置分类 1.当前项目根目录下的config目录下 2.当前项目的根目录下 3.resources目录下的config目录下 4.resources目录下 按照这上面的顺序&#xff0c;4个配置文件的优先级依次降低。 二、自定义存放位置和自定义命名 自定义存放位置和自定义配置文件命令和appl…

Python从Excel读取数据并使用Matplotlib绘制成二维图像

本课程实现使用 Python 从 Excel 读取数据&#xff0c;并使用 Matplotlib 绘制成二维图像。这一过程中&#xff0c;将通过一系列操作来美化图像&#xff0c;最终得到一个可以出版级别的图像。本课程对于需要书写实验报告&#xff0c;学位论文&#xff0c;发表文章&#xff0c;做…

Vue中的虚拟Dom,diff算法,以及diff的优化

virtual dom&#xff1a; 关键词&#xff1a; 1、 template 2、渲染函数 3、 vnode(virtual dom) 4、patch(diff算法) 5、view Vue.js通过编译将template 模板转换成渲染函数(render ) &#xff0c;执行渲染函数就可以得到一个虚拟节点树 VNode 虚拟节点&#xff1a;它可以代…

C++ 中的新成员

C 中的动态内存分配 C 中通过 new 关键字进行动态内存申请 C 中的动态内存申请是基于类型进行的 delete 关键字用于内存释放 new 关键字和 malloc 函数的区别 new 关键字是 C 的一部分 malloc 是由 C 库提供的函数 new 以具体类型为单位进行内存分配 malloc 以字节为单位…

Jenkins ——pipeline入门教程

一、什么是pipeline 什么是Pipeline&#xff1f;简单来说&#xff0c;就是一套运行于Jenkins上的工作流框架&#xff0c;将原本独立运行于单个或者多个节点的任务连接起来&#xff0c;实现单个任务难以完成的复杂发布流程&#xff08;实用场景&#xff1a;将多个Jenkins构建任…

最优化方法Python计算:一元函数搜索算法——二次插值法

已知连续函数 f ( x ) f(x) f(x)在 x ∗ x^* x∗近旁存在最优解 x 0 x_0 x0​。对博文《最优化方法Python计算&#xff1a;连续函数的单峰区间计算》讨论的 f ( x ) f(x) f(x)单峰区间的包围算法稍加修改&#xff0c;可算得 f ( x ) f(x) f(x)包含 x 0 x_0 x0​的单峰区间 [ a …

pandas---删除重复行、映射、异常值检测与过滤、抽样

1. 删除重复行 使用duplicated()函数检测重复的行。 返回布尔类型的Series对象&#xff0c;每个元素对应一行&#xff0c;如果该行不是第一次出现&#xff0c;则元素为True。 def make_df(indexs, columns): data [[str(j)str(i) for j in columns] for i in indexs]df …

中国人民大学与加拿大女王大学金融硕士——用更长远的眼光,展望未来

职场中遇到瓶颈&#xff0c;大家都迫切希望改变自己所处的环境&#xff0c;但却不愿意改变自己&#xff0c;所以他们自己仍然是被束缚的。如果一个人不能够从自我拷问的状态中解脱出来&#xff0c;他就永远也不可能实现自己心中的目标。我们要用更长远的眼光去展望未来&#xf…

NAVIGATE 领航者峰会:记忆科技携手新华三,以存储创新释放数据价值

近日&#xff0c;由紫光集团和新华三集团主办的2023 NAVIGATE 领航者峰会在杭州举行。本届峰会的主题为“精耕务实&#xff0c;为时代赋智慧”&#xff0c;围绕该主题&#xff0c;国内外数千名技术领导者汇聚一堂&#xff0c;探讨数字经济的创新未来。作为IT硬件领域的重要厂商…