Linux重定向和缓冲区理解

news2024/11/16 15:44:01

重定向和缓冲区理解

本文已收录至《Linux知识与编程》专栏!
作者:ARMCSKGT
演示环境:CentOS 7

在这里插入图片描述


重定向和缓冲区理解

  • 前言
  • 正文
    • 文件描述符
    • 重定向
      • 重定向原理
      • 重定向命令
      • 重定向函数
    • 缓冲区
      • 缓冲区是什么?
      • 缓冲区刷新策略
      • 内核缓冲区与普通缓冲区
  • 最后


前言

前面我们介绍了文件描述符这个概念,关于文件描述符需要介绍的还有另一个知识,那就是重定向,重定向可以让我们指定程序向某一个文件流中输出数据或读取数据,操作系统只需要关心文件描述符即可;当我们在IO时,操作系统也并不是将数据直接写入文件,而是先写入缓冲区以提高效率;关于以上的概念,我们接下来将会逐一介绍!
重定向与缓冲区


正文

文件描述符


本章与文件描述符的知识联系紧密,如有疑问,请先移步:文件描述符介绍
文件描述符


重定向


我们在学习C/C++语言时,涉及三个文件流:stdinstdoutstderr

他们的作用分别是:

  • stdin:标准输入流,从键盘等输入设备中读取数据
  • stdout:标准输出流,向显示器等输出设备输出数据
  • stderr:标准错误流,向显示器等输出设备输出错误和异常问题

对应在C/C++语言中的:

  • stdin标准输入流:scanf / cin
  • stdout标准输出流:printf / cout
  • stderr标准错误流:perror / cerr

可能会有小伙伴疑问到:标准输出和标准错误都是输出,为什么不把标准输出和标准错误合并起来?

注意: 标准错误与我们普通输出不同,我们平时程序在运行时,可能会形成日志,如果把日志信息和程序输出一起打印会非常杂乱,将他们分开输出可以让我们更好的管理数据!


重定向原理

我们知道,操作系统在管理被打开文件时会使用文件描述符,所以操作系统作为上层并不关心关心底层中具体操作的是哪一个文件(fd_array[] 中存储的对象),操作系统只知道从0号文件描述符下的文件读取数据,1号下输出数据,2号输出错误信息等等;所以我们可以调换这些文件描述符下的文件,让操作系统进程向我们指定的文件中输入和输出信息即可,这就是重定向!
重定向图解


重定向命令

在Linux系统中可以使用以下命令重定向

> #重定向输出流
< #重定向输入流
>> #重定向输出流(覆盖写入)

# 例如
$ ./exe > txt #将程序exe的stdout(1文件描述符文件流)重定向到txt文件
$ ./exe < file #将程序exe的stdin(0文件描述符文件流)重定向到file文件
$ ./exe >> txt #将程序exe的stdout重定向到txt文件并清空txt文件原有的内容

输出重定向演示:
输出重定向

输入重定向演示:

#include <iostream>
#include <string>
using namespace std;

int main()
{
   string str;
   while(cin>>str)
   {
       cout<<str<<endl;
   }
   return 0;
}

输入重定向
这里我们先向txt文本中输入一条由三个空格分割的字符串,然后用我们自己写的程序输出!

总结三个命令的功能:

  • < : 输入重定向,将0号文件描述符下的文件流替换为命令右边的文件
  • >:输出重定向,将1号文件描述符下的文件流替换为命令右边的文件
  • >>:与 > 功能相同,但是写入文件前会清空文件中的所有数据

    如果想要清空某一个文件,只需要 > file 即可,这个指令默认打开一次file文件并清空文件内存,但什么都不做!

文件描述符重定向:
如果不指定文件描述符,则重定向默认对0和1号文件描述符做修改,如果我们想让其他文件流也重定向,那么需要使用文件描述符重定向!

#include <iostream>
using namespace std;

int main() //cout和cerr交替打印
{
   cout<<"stdout标准输出:1"<<endl;
   cerr<<"stderr标准错误:1"<<endl;
   cout<<"stdout标准输出:2"<<endl;
   cerr<<"stderr标准错误:2"<<endl;
   cout<<"stdout标准输出:3"<<endl;
   cerr<<"stderr标准错误:3"<<endl;
   return 0;
}

如果我们重定向输出到txt文件中,则只能重定向cout,cerr全部打印到屏幕上了
重定向
此时我们需要使用文件描述符重定向:

$ ./exe 1>txt 2>log #将1文件描述符重定向为txt文件,2文件描述符重定向为log文件
$ ./exe >txt 2>log #当然也可以这样写,因为输出重定向默认是1,可以省略,但是不好看

重定向
重定向后,程序没有任何输出,而cout(标准输出)和cerr(标准错误)分别输出到对应的文件!

当然,文件描述符之间也是可以重定向的:

#将文件描述符2中的文件流重定向为1文件描述符的文件流
$ ./exe >txt 2>&1 #两者效果相同 但一般使用本行这种写法
$ ./exe >txt 2<&1

以上述代码为例:

文件描述符重定向
2>&1原理
所以最终cout和cerr全部打印到了txt文件,因为重定向后全部都输出到了文件中!


重定向函数

我们在写程序时,常用的重定向函数是:

#include <unistd.h>
int dup2(int oldfd, int newfd)

函数解析:

  • 参数
    –oldfd:新的文件描述符
    –newfd:旧文件描述符
    功能是将newfd文件描述符下的文件流重定向为oldfd下文件描述符的文件流,最终重定向完,文件描述符只剩下oldfd,因为newfd已经被oldfd覆盖了 !
  • 返回值:重定向成功返回newfd,失败返回-1
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cassert>
using namespace std;

int main()
{
   int txtfd = open("./txt",O_WRONLY|O_TRUNC|O_CREAT);
   int logfd = open("./log",O_WRONLY|O_TRUNC|O_CREAT);
   int d1 = dup2(txtfd,1); //将txt文件重定向到stdout
   int d2 = dup2(logfd,2); //将log文件重定向到stderr
   assert(d1>0 && d2>0);
   cout<<"stdout标准输出:1"<<endl;
   cerr<<"stderr标准错误:1"<<endl;
   cout<<"stdout标准输出:2"<<endl;
   cerr<<"stderr标准错误:2"<<endl;
   cout<<"stdout标准输出:3"<<endl;
   cerr<<"stderr标准错误:3"<<endl;

   close(txtfd);
   close(logfd);
   return 0;
}

dup2重定向
我们可以发现,重定向后,运行exe程序没有任何现象,而txt和log文件中存放了cout和cerr的输出信息!

在开发大型项目时,将错误信息单独剥离出来是一件很重要的事,学会重定向很重要!


缓冲区


缓冲区是什么?

操作系统的IO少不了缓冲区这个buffer,缓冲区相当于一个buffer数组,是内存上的一小部分空间,配合不同的刷新策略,起到提高 IO 效率的作用!

缓冲区理解:
缓冲区就好比快递站,当我们寄快递时,快递站不会立即将我们的快递装车运输,而是等快递数量达到一定时才进行一次运输!
这是因为快递车一次运输的时间非常长,如果每一个快递就运输则时效性会大大降低!
操作系统中的IO工作也存在这样的问题,所以需要缓冲区先存储数据,然后一次性刷新到磁盘!
快递
所以,对于CPU来说,磁盘非常慢,如果频繁刷新数据,则在磁盘IO上会浪费大量时间,不如先存放到内存上再一次性刷新到磁盘上,毕竟内存上的速度还是CPU相对可以接受的!

我们对比一下IO和不IO下对一个数++的区别:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
   int num = 1;

   clock_t t = clock();
   for (int i = 0; i < 100000; ++i) cout << num++ << endl;
   cout << "IO时间:" << clock() - t << endl;

   t = clock();
   for (int i = 0; i < 100000; ++i) num++;
   cout << "非IO时间:" << clock() - t << endl;
   return 0;
}

IO与非IO
对一个数++十万次,需要IO下接近10秒才结束,而不需要IO则不足1秒!

这里说明,在频繁IO的场景下,我们可以借助缓冲区来暂时储存数据,然后一次性刷新到磁盘!

缓冲区示例:

#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cassert>
using namespace std;

int main()
{
   int fd = open("./file",O_WRONLY|O_CREAT|O_TRUNC,0664);
   assert(fd!=-1); //检查文件是否打开成功

   //例如像这样分多个模块需要写入的,如果直接刷新则需要分三次进行写入,而写入缓冲区则一次刷新即可
   char str[100] = {0};
   snprintf(str,sizeof(str),"%s:%d-","Linux file buffer write: ",sizeof(str)," bit");

   write(fd,str,sizeof(str)); //一次刷新
   close(fd);
   return 0;
}

缓冲区刷新


缓冲区刷新策略

缓冲区有多种刷新策略,比如 C语言 中 scanf 的缓冲区刷新策略为:遇到空白字符或换行就截断,因此在输入时需要按一下回车,缓冲区中的数据才能刷新至内核缓冲区中,而 printf 的刷新策略为行缓冲,即遇到 \n 才会进行刷新!

刷新策略:

  • 行缓冲:遇到 \n 才进行刷新,一次刷新一行
  • 全缓冲:缓冲区存满了才进行刷新
  • 无缓冲:没有缓冲区

在Linux中,一般而言,显示器的刷新策略为行缓冲,而普通文件的刷新策略为全缓冲

全缓冲:

#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
   while(true)
   {
       cout<<"Hello Word";
       usleep(10000);
   }
   return 0;
}

全缓冲
可以发现,全缓冲下,每一次打印都是一块一块的打印,一次打印一堆!

行缓冲:

#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
   while(true)
   {
       cout<<"Hello Word"<<endl; //endl换行,相当于\n
       usleep(10000);
   }
   return 0;
}

行缓冲
行缓冲下,一行一行进行打印!


内核缓冲区与普通缓冲区

我们使用C/C++语言上打开一个文件,C/C++库会为我们配置一个文件缓冲区,而这个只是语言层级上的缓冲区,我们现在知道文件打开是通过系统调用,而在系统中也会为打开的文件配置缓冲区!

对于库中的缓冲区我们称为普通缓冲区,而对于系统调用打开的文件缓冲区是内核缓冲区!

所以我们真正的IO流程是:数据先写入普通缓冲区(库缓冲区),然后通过库中的刷新策略刷新到内核缓冲区,最终遵循操作系统的刷新策略,刷新到磁盘上!
内核缓冲区与普通缓冲区
图片出自:Linux 实现原理 — I/O 处理流程与优化手段

理解缓冲区:
我们用代码理解一下缓冲区冲刷机制!

#include <iostream>
#include <cstdio>
#include <cstring>
#include <unistd.h>
using namespace std;

int main()
{
   const char* s1 = "Hello Word!\n";
   const char* s2 = "Hello Linux!\n";
   fprintf(stdout,"%s",s1); //向stdout中写入
   write(1,s2,strlen(s2));
   fork(); //子进程
   return 0;
}

我们直接运行程序:
正常运行
这里并没有什么问题,我们重定向输出到其他文件流中:
重定向
重定向运行后发现,相当于直接运行,file文件流中多了一句 Hello Word! 而且文件中的字符串输出顺序与直接运行的结果相反,这里就涉及内核缓冲区和普通缓冲区的知识了!

注意:当我们直接运行程序时,程序是向屏幕打印,此时使用的fprintf是C/C++库提供的函数和普通缓冲区,屏幕文件默认行缓冲,所以一旦我们写入的字符串有 \n 就会立刻输出;而write是系统调用。系统调用函数本身没有缓冲区,使用文件内核缓冲区,当我们写入后直接冲刷到文件内核缓冲区!

当我们重定向后,文件file属于普通文件,C/C++库缓冲区默认刷新策略是全缓冲,即缓冲区写满了才刷新到文件,而write则直接冲刷到文件内核缓冲区,策略随操作系统而定!

在进行fork之前,进程都不会冲刷普通缓冲区,所以fork时缓冲区的内容(Hello Word!)并没有冲刷到文件,而是在缓冲区中,此时父子进程各有一份未刷新的缓冲区!

当父子进程结束时,同时冲刷普通缓冲区写入文件内核缓冲区中,此时造成的现象就是率先写入了Hello Linux! 在文件内核缓冲区中,随后父子进程同时冲刷普通缓冲区中的Hello Word!到文件内核缓冲区中,所以我就内核缓冲区中写入数据的顺序是 Hello Linux! -> Hello Word! -> Hello Word!,最终操作系统将我就内核缓冲区中的数据冲刷到文件,就有了三条字符串!

这里需要说明的是:普通缓冲区的数据需要刷新到文件内核缓冲区才能被操作系统冲刷到文件中;当我们调用语言所提供的库函数时,根据语言的规定会先写入语言提供的普通缓冲区中,而普通缓冲区需要冲刷到文件内核缓冲区中最终由操作系统冲刷至文件中!当我们使用系统调用时,是直接写入文件内核缓冲区,没有写入普通缓冲区的中间环节!


最后

本节介绍了关于文件描述符的重定向知识,现在我们知道了我们不仅可以向屏幕打印字符,向键盘提取字符,也可以向其他文件输出和提取,因为Linux下一切皆文件!我们还学习了缓冲区的知识,知道了缓冲区的意义,这将对于我们以后的程序设计有极大的帮助!

本次 <Linux重定向和缓冲区理解> 就先介绍到这里啦,希望能够尽可能帮助到大家。

如果文章中有瑕疵,还请各位大佬细心点评和留言,我将立即修补错误,谢谢!
结尾

🌟其他文章阅读推荐🌟
Linux<文件理解和系统调用> -CSDN博客
Linux<进程控制> -CSDN博客
Linux<进程地址空间> -CSDN博客
Linux<环境变量> -CSDN博客
Linux<进程初识> -CSDN博客
🌹欢迎读者多多浏览多多支持!🌹

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

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

相关文章

5款超级好用的开发效率工具,建议收藏!

大家好&#xff01;高温天气切莫太累&#xff0c;注意防暑休闲开胃(&#xff5e; o &#xff5e;)~zZ 人口过剩的时代&#xff0c;劳动力也追求高性价比。好的工具&#xff0c;能够帮助我们更高效地完成工作&#xff0c;节省时间&#xff08;摸鱼时间&#xff09;和精力&#…

吐血整理,性能测试方法与步骤详细,进阶测试之路...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 总体方向&#xf…

HTML5 sessionStorage会话存储

sessionStorage 是HTML5新增的一个会话存储对象&#xff0c;用于临时保存同一窗口(或标签页)的数据&#xff0c;在关闭窗口或标签页之后将会删除这些数据。本篇主要介绍 sessionStorage(会话存储)的使用方式。包括添加、修改、删除等操作。 目录 1. 介绍 1.1 说明 1.2 特点 1…

微信小程序实现一个文字展开收起功能

1.0 需求背景 需求很常见&#xff0c;就是当一行文字过多时&#xff0c;显示省略号&#xff0c;然后显示展开两个字&#xff0c;点击&#xff0c;文字完全展示开&#xff0c;点击收起&#xff0c;回到省略形式&#xff0c;如下图 2.0 需求分析 有了上图&#xff0c;应该能更好…

2023亚马逊云科技中国峰会之Serverless

序言 Amazon Web Services&#xff0c;是Amazon.com推出的一系列云计算服务。 它提供了一系列的基础设施服务、平台服务和软件服务&#xff0c;希望可以帮助我们更轻松地构建和管理基于云的应用程序。 今天来学习一下 Serverless 本文会介绍以下六个模块&#xff1a; 为什么会…

RocketMq 同组消费者 自动设置InstanceName

RocketMq 同组消费者 自动设置InstanceName 一、背景二、处理方法三、源码分析四、总结 一、背景 同组多于1个消费者&#xff0c;如果没单独设置instanceName,默认为DEFAULT。启动时会报如下错误&#xff1a; org.apache.rocketmq.client.exception.MQClientException: The co…

物联网工业触摸屏与防火墙的安全协作

1 前言 随着物联网技术的快速发展&#xff0c;物联网HMI不仅需要提供SCADA级功能库和控件库&#xff08;点击查看物联网HMI功能库和控件库的详细介绍&#xff09;&#xff0c;还需要具备强大的安全性能。虹科物联网HMI内置防火墙功能&#xff0c;识别和阻止未经授权的访问&…

PCI Express --- LTSSM

目录 1. 链路训练和状态机 1.1 Detect 状态 1.1.1 Detect.Quiet 子状态 1.1.2 Detect.Active 子状态 1.2 Polling 状态 1.2.1 Polling.Active 子状态 1.2.2 Polling.Compliance 子状态 1.2.2 Polling.Configuration 子状态 1.2.3 Polling.Speed 子状态 1.3 Configuration 状…

性能测试超细总结,如何才能做到有效压测?性能压测看这篇就够了...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 目标制定以及业务…

java SSM 游戏资讯系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM 游戏资讯系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和 数据库&#xff0c;系统主要采用B…

3ds Max - Pivot Painter Tool

很久之前的笔记&#xff0c;整理归档&#xff1b; Pivot Painter Tool是3dsMax中的插件&#xff0c;主要是辅助将Mesh中每个Element生成自己的Pivot Position&#xff0c;方便如使用World Position Offset对每个Element进行精确控制&#xff0c;导入使用Pivot Painter Tool工具…

深入理解Linux虚拟内存管理(七)

系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核 Linux 设备驱动程序 Linux设备驱动开发详解 深入理解Linux虚拟内存管理&#xff08;一&#xff09; 深入理解Linux虚拟内存管理&#xff08;二&#xff09; 深入理解Linux虚拟内存管理&#xff08;三&#xff09; 深入理…

Linux系统和Windows系统下Python2代码转换为Python3代码工具使用指南

简介 本文主要介绍Linux系统和Windows系统下Python2代码转换为Python3代码工具2to3.py或2to3指令使用指南。 项目场景及问题描述 Python2的最后一个版本是2.7&#xff0c;在2020年彻底停止支持。有些环境不方便同时安装Python2和Python3&#xff0c;或者在使用Python3的环境…

【JVM】JVM 垃圾回收算法

文章目录 前言标记清除&#xff08;Mark-Sweep&#xff09;介绍优缺点 复制&#xff08;拷贝 Copying&#xff09;介绍优缺点 标记整理&#xff08;Mark-Compact&#xff09;介绍优缺点 前言 目前JVM中有三种常见的垃圾回收算法&#xff0c;分别是&#xff1a;标记清除、标记整…

Matter实战系列-----1.软硬件开发环境搭建

一、硬件方面 我使用的是一套xG21 BRD4180B和两块xG24 BRD4187C,如下图&#xff1a; 1.1 RCP&#xff1a; 芯片型号EFR32MG21A020F1024IM32 1.2 Matter Light/Switch over Thread&#xff1a; 芯片型号EFR32MG24B220F1536IM48 1.3 蓝牙5.0 USB dongle 注意由于Linux对蓝牙…

阿里、字节、网易面试必考,黑马【爆火】微服务项目发布

最近&#xff0c;收到一位粉丝投稿&#xff0c;他说&#xff1a;“阿里三面凉凉了&#xff0c;输在了微服务上。” 在看到微服务的面试题后&#xff0c;整个人都是懵的&#xff0c;发现没有经验的自己&#xff0c;一窍不通。 如今&#xff0c;微服务已经成为Java开发者必备的…

深入篇【C++】string类的常用接口介绍:标准库中的string类 【万字总结】

深入篇【C】string类的常用接口介绍&#xff1a;标准库中的string类 Ⅰ.string类介绍Ⅱ.string类的常用接口①.string类对象的常用构造1.string()2.string(const char*ch)3.string(const string& str)4.string(size_t n,char c)5.string(const string& str,size_t pos,…

想开发测试工具,应该如何入手?

何为测试工具&#xff1f;就是能辅助测试同学来完成特定的操作的工具&#xff0c;比如常见的如postman、Fiddler、Charles、jira&#xff0c;包括jmeter等&#xff0c;当然还包括公司自己开发的用例转换工具&#xff0c;造数工具&#xff0c;Mock工具或是平台等等。一般以应用程…

测试在“鸡头”和“凤尾”间如何选择?

经常在知乎上碰到这样的问题&#xff1a;同时拿到多个offer&#xff0c;公司有大有小&#xff0c;有创业型有成熟性&#xff0c;怎么在“鸡头”和“凤尾”间做选择&#xff1f; 为什么会纠结呢&#xff1f;通常创业型公司&#xff0c;给优秀的测试员的薪酬远高于市场平均值&…

“我只想找个测试岗,你却百般刁难我!”给我们带来的思考

最近看到一篇帖子&#xff0c;讲的是一个七八年的大龄测试员被公司补偿性裁员后&#xff0c;找工作的糟心经历。 原文是酱紫的&#xff1a; ---------------------------------------- 不管怎么说&#xff0c;我做测试也有七八年了&#xff0c;一直觉得自己的技术还是可以的&…