• 【基础IO⑨】:重定向实现原理 &&“Linux下一切皆文件“


    一.重定向

    在这里插入图片描述
    我们首先关闭2号文件描述符,然后再打开一个文件log.txt。并且打印这个文件的文件描述符。最后我们将发现这个文件的文件描述符是2。
    这是为什么呢?很简单,因为文件描述符的填充是从上往下,从0开始填没有被占用的位置,因为2号位置被关闭了,所以普通文件log.txt就被放进2号里面了。

    我们知道三个标准输入输出流stdin,stdout,stderr分别代表这键盘文件,显示器文件,显示器文件。它们默认是被打开的,也就分别占用着0,1,2文件描述符。
    当我们再打开一个文件时,就默认占用的是3号文件描述符。
    不过如果我们利用上面的原理,先关闭一个文件,那么这个文件描述符位置上就空缺了,然后再打开一个文件,那么这个文件就会被填到原来的文件的位置里去。
    在这里插入图片描述
    在这里插入图片描述
    比如先将1号描述符位置上的文件关闭,即将显示器文件关闭。然后再打开普通文件,这时,1号描述符位置上的文件地址就变成普通文件了。
    然后我们当向1号描述符输出时,就会重定向输出到普通文件里。

    1.实现原理

    实现重定向的原理其实就是覆盖文件描述符里文件地址。因为文件描述符是不变的,只能改变文件描述符里面的文件地址。原来1号描述符指向的显示器文件,如果1号描述符里的显示器文件地址被覆盖成普通文件地址,那么1号描述符就指向普通文件。当我们往1号描述符输出时,就会输出到文件里。

    所以重定向的本质就是:文件描述符数组里的内容拷贝

    不过这样写有点low,先关闭一个文件,再打开一个文件。因为重定向的本质就是文件描述符数组里面文件地址的拷贝,所以系统提供一些系统调用接口,可以帮助我们直接拷贝:
    在这里插入图片描述
    在这里插入图片描述
    dup2(int oldfd,int newfd)接口的功能就是让oldfd作为拷贝对象拷贝给newfd。其实就是让oldfd文件描述符里的文件对象拷贝给newfd文件描述符里的文件对象,最后newfd文件描述符就指向了oldfd文件描述符指向的文件了。

    在这里插入图片描述

    2.输出重定向

    在这里插入图片描述
    当我们往1号描述符里输出时,就会输出到普通文件里,而不是输出到显示器上。这就是输出重定向。

    3.输入重定向

    在这里插入图片描述
    当我们从0号文件符里获取输入时,本来是从键盘上获取输入,但是却从文件里获取到了输入内容,这就是输入重定向。

    注意:
    1.printf里封装的是1号描述符,它只知道往1号描述符里的文件对象输出,但它并不知道1号描述符里的文件对象是谁,也不关心是谁。
    2.在这里插入图片描述

    4.补充:简易shell中实现重定向

    在这里插入图片描述
    在重定向之前需要先打开文件,这样才可以获取对应的文件描述符,才可以利用文件描述符重定向

    并且在进行重定向过程中,进程如果发生程序替换是不会受影响的!
    在这里插入图片描述

    二.“Linux下一切皆文件”

    Linux下一切皆文件,这句话你是不是在学习Linux中不断的听过,但是你理解它吗?为什么在Linux下一切皆文件呢?

    首先我们先理解:

    1.所有的操作计算机的动作都是以进程的形式进行的。
    2.所有访问文件操作都是直接通过进程方式访问的。
    3.每一种外设硬件都有自己的方式去读写。虽然方法都不一样,但方法种类却类似,都是读和写。

    Linux在上层不关心底层访问的是什么设备,不管是键盘,还是显示器,还是网卡等都看成文件。以文件的方式读写访问设备。
    Linux中是如何做到这样的呢?

    1.虚拟文件系统(VFS)

    我们知道当进程访问文件时,操作系统会给这个文件创建一个文件对象。这个文件对象的地址会被放进文件描述表里。
    操作系统创建的文件对象会以链表的形式链接起来,而这软件层
    被称为虚拟文件系统VFS。

    因为硬件都有自己的方法读写,虽然方法不同,但却有共性。那么我们就可以将这个共性组成一个结构体,里面存储着函数指针,分别是读函数指针和写函数指针。当调用具体函数指针时,就会去调用对应的方法。
    而这个结构体称为 操作方法指针集。里面的指针分别指向对应的外设自己的读取方法。

    所以每打开一个文件,操作系统还会创建一个对应的读取方法集对象。这个结构体对象里存着许多函数指针,分别对应着底层外设的读写方法。
    当你要打开键盘时,操作系统就将键盘的读写方法初始化这个方法集对象,这样调用这个方法集对象里的具体函数指针就是键盘的访问操作。
    当打开的是显示器,操作系统就会将显示器的读写方法初始化给方法集对象,调用方法集对象里的函数指针就能正确的使用显示的读取方法。

    而在文件对象里存储着方法集对象的指针,可以通过该指针找到方法集对象,继而可以调用对应的外设访问方法。

    在这里插入图片描述

    所以Linux是如何不需要管底层设备是什么,都可以按照文件的方式进行访问呢?
    1 . 因为在打开的每个文件对象里都有对应的指针指向方法集结构,里面的函数指针可以找到对应访问外设的正确方法。
    2 .所以在进程看来,它可能不知道调用什么方法访问外设,但只要创建了文件对象,那么这个文件对象里就有指针可以找到对应的方法集。所以进程想要访问谁,只需要打开对应的文件。

    所以哪和设备想被打开,想被访问,也就是要将自己的设备读取方法给文件对象。文件对象里会存储一个指针,指向方法集合。

    不管是什么设备,只要操作系统创建出来对应的struct file对象。就可以正确访问该设备。
    因为操作系统会将该设备的读取方法初始化给方法集对象。而文件对象里有有一个指针可以指向方法集对象。
    在这里插入图片描述
    总结:
    操作系统启动时会创建一个软件层VFS,当外设要打开时,就会创建对应的文件对象,就会创建对应的函数指针对象,将函数指针初始化为底层设备具体的方法操作。
    每种设备都有对应的struct file对象,都有函数指针对象。函数指针指向不同的方法。所以进程不管是什么设备,直接通过文件对象里函数指针对象里函数指针,调用读_函数指针获取读操作,写函数指针获取写操作。所以在上层直接调用read和write就可以访问底层的读写方法,里面肯定是这样封装的:在这里插入图片描述
    所以软件层VFS包裹着底层硬件的差异,给上层提供统一的文件操作接口,用统一的文件接口对不同的设备进行访问。这就是:一切皆文件!
    在这里插入图片描述

  • 相关阅读:
    AI创作音乐引发的深思
    【K210+ESP8266图传上位机开发】TCP server + JPEG图像解析上位机开发
    在BERT与ElasticSearch进行词向量搜索中遇到的各种坑
    Spring微服务无法注册到指定的Spring cloud Eureka
    论文理解与代码解析 :VoxelNet pytorch版本
    网络安全(一):信息收集之玩转nmap(理论篇)
    Linux 搭建NFS
    Java版分布式微服务云开发架构 Spring Cloud+Spring Boot+Mybatis 电子招标采购系统功能清单
    `VUE`的介绍
    华为低端路由器配置脚本
  • 原文地址:https://blog.csdn.net/Extreme_wei/article/details/134250195