【Linux】重定向|重新理解Linux下一切皆文件


文章目录

  • 一、什么是重定向
    • 输出重定向的原理
    • 认识一下输出重定向的系统调用
    • 输出重定向的另外写法
  • 二、浅谈输入重定向
  • 三、重定向和进程替换有冲突吗
  • 四、Linux下一切皆文件
  • 总结



一、什么是重定向

理解重定向之前:先理解一个叫做文件描述符的具体操作。

文件描述符,也是在上篇文章提到的,
在描述进程属性的PCB对象中存在着一个struct file*的指针,该指针指向一个指针数组,指针数组的每一个指针又指向对应的文件。

这个文件描述符,就是该指针数组的下标!

Linux的内核是这样实现的:

打开一个文件时,会在指针数组中找最近的一个空位置,存储该文件的地址。返回的文件描述符就是这个位置的下标。

在这里插入图片描述

先看下面的例子:

 8 #define filename "log.txt"
  9 int main()
 10 {
 11     int fd = open(filename, O_CREAT|O_WRONLY|O_TRUNC, 0666);
 12                                                                                                                           
 13     if(fd < 0)
 14     {
 15         perror("open fail");
 16         return 1;
 17     }
 18     const char* msg = "hello linux\n";
 19     int cnt = 5;
 20     while(cnt)
 21     {
 22         write(1,msg,strlen(msg));
 23         cnt--;
 24     }
 25     printf("%d\n",fd);
 26 }

从上面的代码不难看出,这里是打开一个log.txt的文件,并循环打印hello linux到显示器中。

运行结果也是在我们意料之中的。

当我在开头加上:

在main函数内的第一行加上:
close(1);

这段代码时:
在这里插入图片描述
会看到两个现象:

1.由于已经把1号文件,即标准输出流关闭了,所以fd文件描述符没有在屏幕上打印,是可以理解的。
2.在重新运行该程序前,我已经把log.txt文件删除,运行程序后,重新出现了log.txt文件并且在该文件中出现了本来应该打印在显示器文件的内容!!!

而这个现象,就是所谓的输出重定向!!!

输出重定向的原理

原理很简单:

在执行了close(1)后,1下标的文件描述符对应的指针就被置空了,同时维护stdout的引用计数也减减了,此时1号下标就是空的!!!
在这里插入图片描述

而在此之后,创建的新文件,会先从指针数组中查找最近的第一个空位置,并重新将该位置的指针指向新的文件,返回该下标!!!

在这里插入图片描述

也就是说,新打开的文件占用的是1号下标,返回的文件描述符是1!!!

这就解释了为什么本应该向显示器打印的内容,打印到了log.txt文件中,因为是该log.txt文件占用了1号下标的空间!!!

总结:所谓的输出重定向,就是将原来的文件向对应的文件数组中对象的地址做一次地址的拷贝!

认识一下输出重定向的系统调用

在这里插入图片描述

把oldfd拷贝到newfd中,最终两个文件fd都是oldfd。

举个简单的例子:

假设1号文件描述符存储的是text.txt file*

dup2(3,1);

在这里插入图片描述

就是将3号这个文件描述符拷贝到1号文件中覆盖,拷贝完成后,1位置下标和3位置下标对应的文件描述符都是3。

最终两个文件描述符都是3。

输出重定向的另外写法

假设test.c文件有如下内容

int main()
{
      fprintf(stdout,"hello stdout\n");                                                                                                       
      fprintf(stdout,"hello stdout\n");      
      fprintf(stdout,"hello stdout\n");      
      fprintf(stdout,"hello stdout\n");      
                                   
      fprintf(stderr,"hello stderror\n");      
      fprintf(stderr,"hello stderror\n");      
      fprintf(stderr,"hello stderror\n");      
      fprintf(stderr,"hello stderror\n");     
	return 0;
}

编译test.c文件生成test文件后。

执行如下命令:

./test 1>log.txt 2>&1

该代码的意思是:
./test可执行程序本应该向1号文件描述符对应的文件,即显示器文件中打印内容的,可是被重定向到了log.txt文件中,也就是log.txt文件对应文件描述符的内容拷贝到了1号文件的下标中。
所以./test 运行起来的四条printf语句会打印到log.txt文件中。

2>&1的意思是:将1号文件描述符中的指针内容拷贝给2好文件描述符中,本来2号文件描述符也是指向显示器文件的,至此也同样指向了log.txt文件。

所以log.txt文件会同时出现stdout和stderror两份打印内容。

./test >log.txt 2>&1

上面的命令还能这样写。也就是把1省略。因为默认就是打印到stdout上的。

二、浅谈输入重定向

输入重定向与输出重定向相反,默认从某一个文件中读取。

最简单的例子是:

cat < log.txt

从log.txt文件中读取到stdout显示器文件中。

三、重定向和进程替换有冲突吗

进程替换的本质只是将进程在物理内存中的代码和数据提换成磁盘文件中特定可执行程序的代码和数据。

替换前后并未创建新的进程,且替换后,只是修改了一下页表的映射关系,修改一下进程的虚拟地址空间中的几个参数值。

而重定向的本质是对文件描述符表中的指针进行拷贝,修改的文件描述符表的内容,与进程替换没有任何关系。

两者各司其职,产生了解耦关系。

未来该找进程的文件描述符表中的某一个下标文件写入时就写入,内存管理想向特定文件写入就写入,与文件描述符表的管理没有任何关系。

在这里插入图片描述

四、Linux下一切皆文件

我们平时按的键盘,看的显示器,网卡,声卡,磁盘等等,这些在Linux下都是文件,也就是我们平时所说的外设。

这些外设也是文件。
所以也有文件所有的读方法,写方法,其他的文件属性等等。

当我们想从键盘中输入一些东西时,首先要打开键盘文件。

打开前操作系统会做好一系列准备工作。

创建进程的PCB结构体对象task_struct,该结构体对象中能找到管理文件的文件结构对象(files_struct)

同事,这些外设本来就提供有属于它们独有的访问文件的读写方法等。

并且,每一个文件在被打开时,都会创建属于自己的文件对象struct file,这个文件对象中保存该文件的各种属性信息,其中就有两个函数指针,分别是读指针和写指针,指向该文件的读写方法。

在文件结构对象表files_struct中有一个文件描述符数组,该数组存储的是各个文件的文件描述符。

通过这些文件描述符即可找到对应的每一个被打开的文件。

而这一系列准备工作完成后,假设进程开始调度键盘文件。
该进程就是read系统调用。
read这个进程被运行起来,其内部有这样一条核心代码:

ssize_t read(int fd)
{
	task_struct->files_struct->fd_arr[fd]->f_opes->write();
}

首先进程去到自己的task_structPCB对象中找到文件对象表,在该表中获取到文件描述符,通过文件描述符找到了对应的文件对象,再通过文件对象中的一个指针找到描述对应文件的读写方法结构体,然后通过该结构体内的读写方法指针获取到对应的读写方法,再由该方法调用到对应的外设!

在这里插入图片描述

所以这就是我们之前经常所说的Linux下一切皆文件的原因!!!

这整个过程,就非常像C++中的多态!!!

所以可以肯定的是,C++的多态是抄C语言内核的,必定是这样!

任何一门语言要支持面向对象,它的底层一定会支持这样的思想结构。


总结

Linux下一切皆文件的本质就是面向对象的过程,对每个对象分层,串联起来就能实现调用不同的外设,就能实现不同的功能!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

信创之路数据库人大金仓篇

概要 信创大势所趋&#xff0c;吾等上下求索 参考文档 Linux&#xff1a;人大金仓数据库-KingBaseES V8与 php7的连接配置 laravel9适配人大金仓&#xff08;kingbase&#xff09;数据库 thinkphp6适配人大金仓&#xff08;Kingbase&#xff09;数据库 数据库选型 目前比较…

C语言--统计一行字符串的单词个数, 单词用非字母分割.例如“ab235adg 456ad“被认为是3个单词.

一.题目描述 统计一行字符串的单词个数, 单词用非字母分割. 例如"ab235adg 456ad"被认为是3个单词. 二.思路分析 本题的主要难点在于如何判断有一个单词呢&#xff0c;当然遍历字符串是必须的。下面给出两种不同的思路&#xff1a; 一.当前是字母&#xff0c;下一个…

openRPA开源项目源码编译

最近接触到了一个新的领域——RPA&#xff0c;RPA全称Robotic Process Automation&#xff0c;中文名为机器人流程自动化。RPA可以视作一个数字机器人&#xff0c;它可以通过程序来模拟人与软件系统的交互过程&#xff0c;代替人工将大量重复、有规则的计算机操作自动化&#x…

Vite -静态资源处理 - SVG格式的图片

特点 Vite 对静态资源是开箱即用的。 无需做特殊的配置。项目案例 项目结构 study-vite| -- src| -- assets| -- bbb.svg # 静态的svg图片资源| -- index.html # 主页面| -- main.js # 引入静态资源| -- package.json # 脚本配置| -- vite.co…

3GPP TS38.201 NR; Physical layer; General description (Release 18)

TS38.201是介绍性的标准&#xff0c;简单介绍了RAN的信道组成和PHY层承担的功能&#xff0c;下图是PHY层相关标准的关系。 文章目录 结构信道类型调制方式PHY层支持的过程物理层测量其他标准TS 38.202: Physical layer services provided by the physical layerTS 38.211: Ph…

【Mac开发环境搭建】Docker安装Redis、Nacos

文章目录 Dokcer安装Redis拉取镜像创建配置文件创建容器连接测试Redis连接工具[Quick Redis]设置Redis自启动 Docker安装Nacos Dokcer安装Redis 拉取镜像 docker pull redis创建配置文件 # bind 127.0.0.1 -::1 bind 0.0.0.0 # 是否启用保护模式 protected-mode no# redis端口…

python+pytest接口自动化测试之接口测试基础

一、接口测试的基本信息 1、常用的两种接口&#xff1a;webservice接口和http api接口   webService接口是走soap协议通过http传输&#xff0c;请求报文和返回报文都是xml格式的&#xff0c;可以用soupui、jmeter等工具进行测试。   http api接口是走http协议&#xff0c;…

数据结构与算法之美学习笔记:20 | 散列表(下):为什么散列表和链表经常会一起使用?

目录 前言LRU 缓存淘汰算法Redis 有序集合Java LinkedHashMap解答开篇 & 内容小结 前言 本节课程思维导图&#xff1a; 今天&#xff0c;我们就来看看&#xff0c;在这几个问题中&#xff0c;散列表和链表都是如何组合起来使用的&#xff0c;以及为什么散列表和链表会经常…

【咖啡品牌分析】Google Maps数据采集咖啡市场数据分析区域分析热度分布分析数据抓取瑞幸星巴克

引言 咖啡作为一种受欢迎的饮品&#xff0c;已经成为我们生活中不可或缺的一部分。随着国内外咖啡品牌的涌入&#xff0c;新加坡咖啡市场愈加多元化和竞争激烈。 本文对新加坡咖啡市场进行了全面的品牌门店数占比分析&#xff0c;聚焦于热门品牌的地理分布、投资价值等。通过…

系列四、GC垃圾回收【四大垃圾算法-引用计数法】

一、概述 Java中&#xff0c;引用和对象是有关联的&#xff0c;如果要操作对象则必须要用引用进行。因此判断一个对象是否可以被回收&#xff0c;很显然一个简单的办法就是通过引用计数来判断一个对象是否可以被回收。简单来讲就是给对象中添加一个引用计数器&#xff0c;每当一…

echarts 实现双y轴折线图示例

该示例有如下几个特点&#xff1a; ①实现tooltip自定义样式&#xff08;echarts 实现tooltip提示框样式自定义-CSDN博客&#xff09; ②legend左右区分展示 ③双y轴折线展示 代码如下&#xff1a; this.options {grid: {left: "3%",right: "3%",top: &…

目标检测—YOLO系列(二 ) 全面解读论文与复现代码YOLOv1 PyTorch

精读论文 前言 从这篇开始&#xff0c;我们将进入YOLO的学习。YOLO是目前比较流行的目标检测算法&#xff0c;速度快且结构简单&#xff0c;其他的目标检测算法如RCNN系列&#xff0c;以后有时间的话再介绍。 本文主要介绍的是YOLOV1&#xff0c;这是由以Joseph Redmon为首的…

测试开发环境下centos7.9下安装docker的minio

按照以下方法进行 1、安装docker&#xff0c;要是生产等还是要按照docker-ce yum install docker 2、启动docker service docker start 3、 查看docker信息 docker info 4、加到启动里 systemctl enable docker.service 5、开始docker pull minio/minio 但报错&#x…

【2023春李宏毅机器学习】快速了解机器学习基本原理

文章目录 机器学习约等于机器自动找一个函数 机器学习分类 regression&#xff1a;输出为连续值classification&#xff1a;输出为一个类别structured learning&#xff1a;又叫生成式学习generative learning 生成有结构的物件&#xff08;如&#xff1a;影像、句子&#xf…

Facebook内容的类型

随着人们日益依赖的社交媒体来进行信息获取与交流&#xff0c;Facebook作为全球最大的社交媒体平台之一&#xff0c;那么Facebook的内容都有哪些类型呢&#xff1f;下面小编来讲讲吧&#xff01; 1、实时发生的事 我们需要实时了解时事动态&#xff0c;这样可以使用户对品牌发…

CAN总线负载及CANoe查看总线负载率

文章目录 一、什么是CAN总线的负载率&#xff1f;二、负载率计算三、CANoe查看总线负载率 一、什么是CAN总线的负载率&#xff1f; 一般业内对负载率的定义为&#xff1a;实际数据传输速率和理论上能达到的数据传输速率的比值。 传输速率一般是按秒来计算&#xff0c;数据传输…

Shopee买家通系统是怎么操作自动下单的

Shopee买家通系统可以自动下单虾皮平台的产品&#xff0c;具体操作流程是先准备好能下单的账号&#xff08;没有账号可以直接准备资料后用软件进行注册&#xff09;&#xff0c;然后设置关键词及产品编号进行自动搜索、点击、浏览后进行添加购物车&#xff0c;最后再进行自动结…

自学人工智能难吗?

在人工智能风靡全球的时代&#xff0c;越来越多的人对学习人工智能产生了浓厚的兴趣。那么&#xff0c;自学人工智能难吗&#xff1f;今天&#xff0c;我们将为你揭开这个谜团&#xff0c;让你轻松开启智能未来之旅&#xff01; 一、自学人工智能——不再是难题 过去&#xf…

DS二叉树的存储

前言 我们上一期已经介绍了树相关的基础知识&#xff0c;了解了树相关的概念和结构、二叉树的概念和结构以及性质、也介绍了他的存储方式&#xff01;本期我们来根据上期介绍的对二叉树的顺序存储和链式存储分别进行实现&#xff01; 本期内容介绍 二叉树的顺序结构 堆的概念…

Unity优化(1)——合并Mesh的两种方法

在某些移动端项目中&#xff0c;对于DrawCall的要求是很严格的&#xff0c;我们一般查看DrawCall可以通过Statistics里面的Batches进行查看&#xff0c;一般对于移动设备的Batches要控制在200左右比较合适&#xff0c;所以降低Batches是很重要的。 我们常常会遇到一个物体下挂载…