初识Linux · 重定向和缓冲区

目录

前言:

预备知识

缓冲区 + 重定向


前言:

其实有了文件2的预备知识,我们已经初步了解了文件描述符fd是什么,底层是如何运作的了,那么本文,我们通过文件描述符对重定向和缓冲区有一个更深层次的理解,对于重定向,我们最开始只是知道系统将我们本该输出到A的内容输出到了B,但是我们并不知道是如何运作的,所以本文的第一个目标:理解重定向是如何实现的?那么对于第二个目标,就是加深对缓冲区的理解

以上是本文的概念,那么进入主题吧。


预备知识

我们介绍重定向从一个函数开始:

我们从close函数开始,close函数的参数是fd,也就是文件描述符,结合Linux中万物皆文件的思想,如果我们我们往显示器这个文件输出东西,把该文件关了是不是就打印不出来了?

加上默认打开了三个流,stdin stdout stderr,分别对应的就是0 1 2,我们一个一个尝试:

int main()
{close(0);int fd = open("log.txt",O_WRONLY | O_CREAT | O_APPEND, 0666);printf("fd:%d\n", fd);    return 0;
}

当我们关闭了0这个,结果照样可以正常打印:

但是不同的是为什么打印出来的是0?

我们再把2关了试试:

int main()
{// close(0);close(2);int fd = open("log.txt",O_WRONLY | O_CREAT | O_APPEND, 0666);printf("fd:%d\n", fd);    return 0;
}

此时打印出来的居然是2?我们试试1:

int main()
{// close(0);// close(2);close(1);int fd = open("log.txt",O_WRONLY | O_CREAT | O_APPEND, 0666);printf("fd:%d\n", fd);    return 0;
}

相信现象我们也能猜出来,因为0 1 2分别对应的是stdin stdout stderr,我们将默认的输出流关了,所以显示器上没有东西。

而,文件描述符fd,对于我们新创建的文件来说,文件描述符既然是我们close掉的?

所以,这里可以的出来一个结论是,文件描述符的匹配规则实际上是从files_struct里面找没有使用的最小的文件描述符分配给新开的文件

我们既然使用的printf函数,没有使用文件函数,我们不妨试试文件函数fprintf:

int main()
{// close(0);// close(2);close(1);int fd = open("log.txt",O_WRONLY | O_CREAT | O_APPEND, 0666);printf("printf,fd:%d\n", fd);    fprintf(stdout,"fprintf,fd:%d\n",fd);return 0;
}

现象自然是不会在显示屏上打印东西,毕竟1已经关闭了,可是我们是知道的,1这个文件描述符是给的新开的文件log.txt,那么你说,我们打印的东西会不会出现在log.txt呢?

还真的会。

那么这个现象奇怪吗?其实并不算奇怪,因为我们知道文件描述符1虽然被关闭了,但是实际上只是没给stdout而已,给了新开的文件log.txt,那么,这是不是一种重定向呢?

答案:是!通过改变文件描述符,改变我们要输出的内容。但是光这样我们的理解并不是很深刻,我们再使用函数fflush看看:

int main()
{// close(0);// close(2);close(1);int fd = open("log.txt",O_WRONLY | O_CREAT | O_APPEND, 0666);printf("printf,fd:%d\n", fd);    fprintf(stdout,"fprintf,fd:%d\n",fd);//fflush(stdout);close(fd);return 0;
}

我们将1文件描述符关闭之后,往stdout里面打印东西,但是1因为已经被关闭了,所以自然不会在stdout上打印东西,但是根据上面的描述,打印的内容会打印到log.txt文件里面,与上文代码不同的是,在代码的最后,我们close了fd,结果如何呢?

发现log.txt的内容大小为0,打印出来看看:

也确实什么都没有。

那么如果我们加上fflush呢?

int main()
{// close(0);// close(2);close(1);int fd = open("log.txt",O_WRONLY | O_CREAT | O_APPEND, 0666);printf("printf,fd:%d\n", fd);    fprintf(stdout,"fprintf,fd:%d\n",fd);fflush(stdout);close(fd);return 0;
}

结果居然是打印出来了,难道是因为我们没有把缓冲区刷新干净吗?

那么我们带着这个问题引出缓冲区 + 重定向的概念。


缓冲区 + 重定向

不知道各位同学是否还记得,进程终止章节的exit和_exit,我们通过实验,知道了exit实际上是调用的_exit,因为库函数是没有资格调用系统层面的东西的,并且,我们调用_exit之后,我们确定了我们使用exit刷新的缓冲区一定不是在系统层面的,那么在那篇文章,我们知道了exit刷新的是上层的缓冲区,和我们上文所说的缓冲区是否是同一个呢?

答案:是的!

我们在这里使用的fllush,exit刷新的其实都是语言层面的缓冲区。

大致的样子就是这样,那么,与之前不同的是我们知道了缓冲区不只有一个,好像有多个?

在语言层面来说,我们写下的所有代码,都是给多个语言层面的缓冲区,所以,当我们关闭了1,此时1给了我们新开的文件,文件对应的就是该缓冲区,注意,我这里描述的是该文件对应的缓冲区是1所对应的。也就是原来stdout的缓冲区被用了,可是,为什么我们刷新了之后,我们想要的内容就打印出来了?

这是因为,我们没有fflush之前,所有的内容都是放在的语言层面的缓冲区,当我们fflush,将里面的内容刷新出去,到了内核层面的缓冲区,就不关我们的事儿了,那是OS的工作了,那么OS自然是会将内容刷新到对应的磁盘部分。

那么,这,是不是一种重定向呢?是!因为改变了文件描述符!!

这里我们得到一个重要结论,也就是缓冲区有许多个,用户层面将内容写入到缓冲区里面,由库里面的函数进行操作,将语言缓冲区写入到内核里面,再由OS将内容写入到磁盘。

但是问题来了,为什么我们不能直接将内容写到OS,或者说直接和OS进行交互呢?这是因为OS忙!!OS忙着调度,忙着回收呢。所以系统调用往往都是比高级语言的调用慢的,成本有点高的。具体的后面一点点介绍。

那么我们现在已经理解了重定向,再加深一点印象,我们介绍一下,dup2这个函数:

对于dup2函数来说,参数只有两个,oldfd,newfd,那么,当我们改变文件描述符,比如上面的1从stdout给到了log.txt,我们应该dup2(fd,1) 还是 dup2(1,fd)呢?

我们结合fprintf,fprintf的参数为:

原本printf是将内容打印到1上,fprintf的第一个参数改变了1,改成了对应的文件对象,所以结合fprintf,dup2函数的参数应该是dup2(fd,1)。我们也可以通过文档描述看看:

dup2将让newfd成为oldfd的副本,本质上就是让原本文件描述符的指向改变了。

我们不妨来使用试试:

int main()
{int fd = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);if(fd < 0){perror("open fail!\n");}dup2(fd,1);printf("Hello linux!\n");fprintf(stdout,"Hello world!\n");return 0;
}

结果也是不出我们所料,成功写入到了log.txt文件里面:

就是因为我们将文件进行了重定向。

那么,对于重定向来说,我们已经有了一个较深的理解,现在,我们来引入一段较为奇怪的代码,通过结果来引出缓冲区的概念:

int main()
{int fd = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);if(fd < 0){perror("open fail!\n");}dup2(fd,1);printf("Hello linux!\n");fprintf(stdout,"Hello world!\n");char* message = "Hello C++!\n";write(1,message,strlen(message));fork();return 0;
}

注意那个fork,我们往1里面写入了三个字符串,但是为什么往一个文件里面,printf和fprintf都打印了两行,但是write函数只打印了一个呢?

这里我们就需要再次用到当时那个图片了:

我们将数据写到了语言层面的缓冲区是printf fprintf,write因为是系统调用,所以直接写到了内核层面的缓冲区,而我们创建了子进程之后,因为子进程是要继承父进程的代码和数据的,系统层面的肯定是继承不了的。而创建进程之后,进程结束之后是会刷新缓冲区的,也就是将语言层面的刷新了两次,从而导致,log.txt里面,有两份一样的代码。

所以,我们从这个现象,引出三段论:

缓冲区是什么? 缓冲区为什么存在? 缓冲区怎么做的?

缓冲区是一块空间,但是本质上,缓冲区实际上是结构体,为什么我这么说呢?因为缓冲区的源码如下:

FILE实际上是_IO_FILE的typedef,stdout实际上就是文件指针,那么前文所提及的,1占据了stdout对应的缓冲区哦!!缓冲区不止一个,每个打开的文件都有对应的缓冲区,每个打开的文件都有自己对应的_fileno!!这和我们之前所认为的缓冲区的差别是非常大的。

缓冲区在语言层面有,在系统内核里面也有,在任何一个文件都有,这是我们本篇文章所得出来的一个重要结论。

那么对应的空间在哪里呢?我们不妨看看这个结构体内容,可以发现基本上都是区域的命名,所以我们所谓的写入数据,刷新数据,其实都是从这些开辟的空间里面写入,刷新,读取。

缓冲区是什么我们就说清楚了。

那么为什么存在缓冲区

答案非常简单,是为了提高上一层的使用体验。

你想,如果我们直接和系统交互,就像我们翻山越岭一样,只为了给好朋友一件礼物,十分的浪费人力和物力,但是如果我们使用顺丰,一次性,能运输几百件快递,岂不美哉?

所以在系统层面来看,它是为了提高高级语言层面的使用舒适度,而高级语言层面的缓冲区就是为了提高用户层面的使用舒适感。

那么缓冲区如何操作的

缓冲区最重要的肯定就是刷新操作,所以我们要讨论的是刷新策略的问题:

1 立即刷新  2 行刷新 3 全缓冲刷新 4 特殊情况刷新

对于1来说,就是我们上面说的,翻山越岭,只为了给好朋友的plus版本,不过是多了一个中间站,我们给中间站一个东西,中间站给好朋友一个东西,效率还是蛮低的。

对于2来说,行刷新的策略目前碰到的有显示器,显示器就是行刷新,这实际上是为了更符合人眼的观看,如果一次性全部刷新出来,人眼也看不过来,如果是1个字符一个字符的打印,那体验就非常差了,所以显示器为了用户体验,使用的是行刷新。

对于3来说,全缓冲刷新就是等缓冲区塞不下了,这个时候才刷新出去。

对于4来说,比如进程碰到了exit,意外终止了,终止之后就会刷新缓冲区。

这是缓冲区的怎么做。

以上是对重定向和缓冲区的一个简单理解。


感谢阅读!

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

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

相关文章

JVM(HotSpot):GC之垃圾标记阶段

文章目录 前言一、标记阶段算法1、引用计数法2、可达性分析算法&#xff08;JVM使用&#xff09; 二、4种引用1、 强引用2、软引用(SoftReference)3、弱引用(WeakHashMap)4、虚引用(PhantomReference) 三、代码案例1、 强引用2、软引用(SoftReference)3、弱引用(WeakHashMap) 前…

AI-Talk开发板之shell_xtts

一、说明 运行duomotai_ap sdk下的shell_xtts例程&#xff0c;测试语音合成以及SPK功能。 操作说明&#xff1a;开发指南 | 聆思文档中心 (listenai.com) 与处理器的信号连接&#xff1a; 二、工程 1、设备树 由于AI-Talk开发板与CSK6-MIX开发板有些管脚不一样&#xff0c;所…

主机加固是什么?又该如何实施呢?

MCK主机加固&#xff1a;企业数据安全的守护神 内核级安全加固&#xff1a;MCK主机加固采用基于操作系统内核级的安全加固技术&#xff0c;从根本上阻断了病毒和恶意软件的入侵路径&#xff0c;确保企业核心数据的安全。 智能防御机制&#xff1a;智能识别并预警潜在的安全威胁…

纯血鸿蒙!

纯血鸿蒙&#xff0c;这是哪个营销大师给起的名字啊&#xff01; 纯血&#xff01;象征着高贵、自信、自主、血性、英雄气概&#xff0c;都融入这纯血鸿蒙了&#xff01; 鸿蒙本就是开天辟地&#xff0c;加上纯血&#xff0c;真是荡气回肠&#xff01; 鸿蒙的推出背景 我们前…

数据结构(JAVA)包装类泛型

文章目录 包装类基本数据类型和对应的包装类装箱和拆箱面试题 泛型什么是泛型泛型的语法泛型类的使用泛型的使用裸类型(Raw Type) &#xff08;仅需了解&#xff09;擦除机制泛型的上界泛型方法 包装类 基本数据类型和对应的包装类 注意&#xff0c;除了int基本数据类型的包装…

机器学习核心:监督学习与无监督学习

个人主页&#xff1a;chian-ocean 文章专栏 监督学习与无监督学习&#xff1a;深度解析 机器学习是现代人工智能的核心支柱&#xff0c;已广泛应用于从数据挖掘到计算机视觉再到自然语言处理的诸多领域。作为机器学习最主要的两大类型&#xff0c;监督学习&#xff08;Super…

LLM - 配置 ModelScope SWIFT 测试 Qwen2-VL 视频微调(LoRA) 教程(3)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/142882496 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 SWIFT …

【Neo4j】- 轻松入门图数据库

文章目录 前言-场景一、Neo4j概述二、软件安装部署1.软件下载2.软件部署3.软件使用4.语法学习 总结 前言-场景 这里用大家都了解的关系数据与图数据据库对比着说,更加方便大家理解图数据库的作用 图形数据库和关系数据库均存储信息并表示数据之间的关系。但是&#xff0c;关系…

【Golang】Go语言web框架Gin响应客户端有哪些方式

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Navigation2 算法流程

转自 https://zhuanlan.zhihu.com/p/405670882 此文仅作学习笔记 启动流程 在仿真环境中启动导航包的示例程序&#xff0c;执行nav2_bringup/bringup/launch/tb3_simulation_launch.py文件。ROS2的launch文件支持采用python语言来编写以支持更加复杂的功能&#xff0c;本文件…

个人用操作系统笔记(待补充)

文章目录 一、绪论二、Linux操作系统进程线程&#xff08;进程内的基本调度单位&#xff09;文件与设备文件权限文件系统文件连接&#xff08;共享&#xff09; 地址空间缺页中断 用户超级用户root&#xff08;用户标识与组标识都是0&#xff09; 重定向系统启动常用命令 OS研究…

【优选算法篇】踏入算法的深邃乐章:滑动窗口的极致探秘

文章目录 C 滑动窗口详解&#xff1a;进阶题解与思维分析前言第二章&#xff1a;进阶挑战2.1 水果成篮解法一&#xff1a;滑动窗口解法二&#xff1a;滑动窗口 数组模拟哈希表复杂度分析&#xff1a;图解分析&#xff1a;示例&#xff1a;滑动窗口执行过程图解&#xff1a; 详…

ARM嵌入式学习--第三天

ARM常用伪指令分析 ARM伪指令&#xff1a;为了方便程序员使用&#xff0c;编译器设计的指令&#xff0c;这个指令ARM核无法识别&#xff0c;需要编译器对它翻译成ARM核所识别的指令 -LDR R1&#xff0c;0xabcdef分析 总结&#xff1a; 编译器在编译的时候&#xff0c;将ldr r…

深入拆解TomcatJetty(一)

深入拆解Tomcat&Jetty&#xff08;一&#xff09; 专栏地址&#xff1a;https://time.geekbang.org/column/intro/100027701 1、Web容器是什么 早期的 Web 应用主要用于浏览新闻等静态页面&#xff0c;HTTP 服务器&#xff08;比如 Apache、Nginx&#xff09;向浏览器返…

008、相交链表

0、题目描述 相交链表 1、法1 嵌套循环&#xff0c;从listA的第一个节点开始与listB的每个节点比对&#xff0c;有相同的就返回这个节点。 时间复杂度是n^2 struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {struct ListNode* pa …

多媒体(1)

多媒体 多媒体的信息结构是【非线性的网状结构】 多媒体技术的基本特征&#xff1a;集成性、交互性、实时性、数字化、多样性 多媒体数据具有【数据量大】、【数据类型多】、【数据类型间区别小】、【输入输 出复杂】的特点 在多媒体数据库中&#xff0c;基于内容检索的关键技术…

智联云采 SRM2.0 testService SQL注入漏洞复现

0x01 产品简介 智联云采是一款针对企业供应链管理难题及智能化转型升级需求而设计的解决方案,针对企业供应链管理难题,及智能化转型升级需求,智联云采依托人工智能、物联网、大数据、云等技术,通过软硬件系统化方案,帮助企业实现供应商关系管理和采购线上化、移动化、智能…

【AI绘画】Midjourney进阶:引导线构图详解

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AI绘画 | Midjourney 文章目录 &#x1f4af;前言&#x1f4af;什么是构图为什么Midjourney要使用构图 &#x1f4af;引导线构图特点使用场景提示词书写技巧测试 &#x1f4af;小结 &#x1f4af;前言 【AI绘画】Midjourney进阶&a…

嵌入式职业规划

嵌入式职业规划 在嵌入式的软件开发中&#xff0c;可以分为&#xff1a; 嵌入式MCU软件开发工程师&#xff1b; 嵌入式Linux底层&#xff08;BSP&#xff09;软件开发工程师&#xff1b; 嵌入式Linux应用开发工程师&#xff1b; 嵌入式FPGA算法开发工程师 对于前两个阶段 …

【时间之外】IT人求职和创业应知【9】

目录 1. 云计算ETF领涨,中证云计算与大数据主题指数估值较低 2. 南向资金持有海螺创业市值减少,但仍保持高比例持股 3. 深创赛澳大利亚分站赛落幕,龙岗区与3个意向落地项目签约 认知决定你的赚钱能力。以下是今天可能影响你求职和创业的热点新闻: 今日关键字:数字战争…