【Linux手册】重定向是如何实现的?Linux下为什么一切皆文件?

文章目录

前言

在Linux下,我们通常会使用到重定向功能,来将一个程序的输出内容不直接打印到屏幕上,而是写到文件中,比如:

复制代码
echo test.exe > test.txt  //将test.exe程序输出的结果写入到test.txt文件中
echo test.exe >> test.txt  //将test.exe程序输出的结果追加到test.txt文件中

我们都知道 > 能够将数据覆盖的写入到文本中,而 >> 可以将数据追加到文本末尾。那么这是怎么做到的呢???

本文将围绕这两个问题展开介绍操作系统是如何做到的重定向的,以及为什么Linux下一切皆文件,是如何做到一切皆文件的

重定向

回顾:在上一篇【Linux手册】从接口到管理:Linux文件系统的核心操作指南中我们已经对操作系统如何管理文件的有了大致的理解:

  1. 操作系统对于文件的管理也是先描述,再组织的;在操作系统中使用struct files_struct来对进程打开的文件进行管理的,其中有一个成员struct file* fd_array[]数组 ,该数组存放了struct file结构体指针,数组的下标就是文件描述符。操作系统是不认文件的名称的,操作系统都是通过文件描述符来确定文件的。
  2. C语言默认会打开三个流:stdin,stdout,stderr其文件描述符默认是0,1,2。

输入重定向

我们在使用C语言的时候,一些接口默认是向显示器打印的比如printf,printf底层调用的是write系统调用接口,那么printf在进行封装的时候一定默认调用write的时候第一个参数是1。

那么如果将1号文件描述符用其他文件替换会怎么样???printf还是向显示器打印吗???

通过close将一号文件描述符对应的文件关闭,再打开一个文件,因为文件描述符分配的规则就是从小到大进行分配的,所以新打开的文件会占据一号描述符的位置,从而实现对一号文件描述符的替换。

运行结果如下:

根据上面的运行结果我们发现,将一号文件进行替换后再运行程序后:显示器没有再输出内容,而运行完后多了一个test.txt文件,其内部存储了输出的内容。

这好像就是重定向呀!!!

是的,操作系统进行重定向的原理与上面类似:输入重定向就是将文件描述符表中的一号文件描述符进行替换,使得一些库函数的接口在进行输出的时候不再向显示器文件打印,而是向替换后的文件中打印,在此期间库函数并不关注一号文件是什么,其只是机械的向一号文件进行输出。

在上述模拟实现既要关闭文件还要打开文件太复杂了,所以系统专门提供了进行文件描述符替换的接口dup2接口:

int dup2(int oldfd ,int newfd)

  1. 第一个参数oldfd:要进行覆盖的文件描述符,即用oldfd进行覆盖;
  2. 第二个参数newfd:要被覆盖的文件描述符,即newfd被覆盖;
  3. 返回值:如果覆盖成功返回覆盖后的文件描述符,覆盖失败返回-1;
    所以上面的模拟代码就可以改写为:

输出重定向

输出重定向还是一个道理,就是将0号文件描述符替换成其他文件:

如上图所示,将0号文件用其他文件进行替换,使得在输入的时候不再不再使用键盘进行输入。

模拟实现Shell中的重定向

我们已经知道了重定向的原理,那么能不能对Shell重定向的操作进行简单的实现。

下面编写一段简单的代码对Shell重定向的原理进行简单的实现,详细代码见下图:

补充:我们在进行重定向之后,重定向的结果会不会影响到进程替换和子进程????

答案:会,对于进程替换来说,替换的只是进程的代码和数据,并没有替换进程的PCB结构体对象,所以进程的文件描述符表并没有被替换。对于子进程来说:子进程除了代码与父进程共享,其他的数据都是以写实拷贝的方式与父进程的一样。

stderr是什么

在前面我们说stdout和stderr是标准输入,输出,而stderr是标准错误。那么stderr到底是什么作用???stderr占据文件描述符表的第2号位置,stderr与stdout的作用类似,也是输出型文件,你也可以通过stderr对文件的内容进行打印,比如:

向2号文件描述符对应的文件中进行打印,最终会打印到显示器上,那这好像和stdout一样呀。

使得stdout和stderr的实现方式类似,但是功能是不一样的,stdout专门用来打印进程的输出信息,而stderr专门用来打印进程的错误信息,这样就可以实现将文件的输出信息与错误信息分流处理。

通过以下具体操作实现信息的风流处理。

如果希望将两个文件的数据打印到一个文件中可以使用以下操作:
./test.eve 1>all.txt 2>&1


如何理解Linux下一切皆文件

谈到Linux下一切皆文件就不得不说以下操作系统具体是如何对外设计进行管理的。

操作系统的管理方式一律都是先描述,再组织。在前一篇博客中我们了解到进程是通过struct files_struct来对文件进行管理和组织的,通过一个数组来指向不同的struct file结构体,到那时这和硬件驱动又有什么关系呢???

操作系统是如何对外设进行管理的,进程又是如何操作文件的???*

在struct file结构体中有一个指针struct file_operations *f_op该指针指向一个struct file_operation结构体,该结构体是对硬件操作接口的汇总:

根据上图也可以看到有read,write等接口,这些接口就实现了将系统调用与驱动程序接口关联起来了。

  • 此后进程如果要对硬件进行操作直接调用相应的接口就行了,不用关心硬件是如何实现的,操作系统通过文件的类型来调用不同驱动函数,==这就使得进程可以以一种文件的形式看待硬件设备,可以直接向文件(硬件)中写入和读取。 ==这就是为什么Linux中一切皆文件的原因
  • 该结构体也使得所有不同的外设最终都可以用一个相同的结构体file_operations来进行描述,这样就实现了抽象管理,即将底层硬件之间的差异屏蔽了,操作系统不需要大量的结构体对五花八门的硬件进行分类管理。

那么底层的硬件中必定是有差异的,操作系统是如何将其全部抽象成file_operation的???

不论是什么硬件,在冯诺依曼体系中都是输入输出设备,所以都可能有输入输出的请求,所以对于file_operation中的接口,如果外设有就直接指向对应的外设接口,如果没有就直接将指针置为空即可。
在上层都是file_operation,在下层又分出各种不同的外设,就好像是同一张皮,而其内部却相差甚远,就不就是多态嘛

以下通过一个示意图来展示Linux是如何做到虚拟文件系统的:

相关推荐
绝无仅有12 分钟前
OSS文件上传解析失败,错误:文件下载失败的排查与解决
后端·面试·架构
程序员JerrySUN21 分钟前
Linux 文件系统实现层详解:原理、结构与驱动衔接
android·linux·运维·数据库·redis·嵌入式硬件
SAP龙哥1 小时前
日常运维问题汇总-58
运维
J_Xiong01171 小时前
【工程篇】07:如何打包conda环境并拷贝到另一台服务器上
运维·服务器·conda
LUCIAZZZ1 小时前
高性能网络模式-Reactor和Preactor
java·服务器·开发语言·网络·操作系统·计算机系统
k *1 小时前
网络编程-tcp连接:服务器与客户端
服务器·网络·tcp/ip
Wezzer2 小时前
haproxy负载均衡
运维·服务器·haproxy·keepalvied
HainesFreeman2 小时前
Linux、Ubuntu和CentOS的关系与区别
linux·ubuntu·centos
倔强青铜三2 小时前
苦练Python第22天:11个必学的列表方法
人工智能·python·面试
倔强青铜三2 小时前
苦练Python第21天:列表创建、访问与修改三板斧
人工智能·python·面试