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

news2025/1/11 2:47:30

文章目录

  • 一、什么是重定向
    • 输出重定向的原理
    • 认识一下输出重定向的系统调用
    • 输出重定向的另外写法
  • 二、浅谈输入重定向
  • 三、重定向和进程替换有冲突吗
  • 四、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下一切皆文件的本质就是面向对象的过程,对每个对象分层,串联起来就能实现调用不同的外设,就能实现不同的功能!!!

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

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

相关文章

C语言求0—7所能组成的奇数个数

完整代码&#xff1a; // 求0—7所能组成的奇数个数 //根据题意&#xff0c;应该是没有重复数字的&#xff0c;所以最大只能为八位数 //如果可以重复的话&#xff0c;那么位数就限制不了&#xff0c;然后奇数的个数就是无穷大了 #include <stdio.h>int main() {int coun…

万宾科技智能井盖传感器,提升市政井盖健康

市政井盖就是城市里不可或缺的基础设施之一&#xff0c;关于它的监测工作可马虎不得。它承载着保护市民的交通安全以及城市正常运转的重要使命。虽然现在城市化的速度很快&#xff0c;但是传统的市政井盖管理方式变得有些力不从心了。井盖的覆盖范围很广&#xff0c;如果单单依…

开启数据库审计 db,extended级别或os级别)并将审计文件存放到/opt/oracle/audit/下

文章目录 1、登录到数据库2、查看审计状态3、创建审计目录4、启用审计5、设置审计文件路径5、再次查看结果 1、登录到数据库 使用SQL*Plus或者其他Oracle数据库客户端登录到数据库。 sqlplus / as sysdba;2、查看审计状态 show parameter audit;目前是DB状态&#xff0c;并且…

【超好用的工具库】hutool-all工具库的基本使用

简介&#xff08;可不看&#xff09;&#xff1a; hutool-all是一个Java工具库&#xff0c;提供了许多实用的工具类和方法&#xff0c;用于简化Java开发过程中的常见任务。它包含了各种模块&#xff0c;涵盖了字符串操作、日期时间处理、加密解密、文件操作、网络通信、图片处…

计算机毕业设计 基于SpringBoot的医院档案管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

自动驾驶-BEV感知综述

BEV感知综述 随着自动驾驶传感器配置多模态化、多源化&#xff0c;将多源信息在unified View下表达变得更加关键。BEV视角下构建的local map对于多源信息融合及理解更加直观简洁&#xff0c;同时对于后续规划控制模块任务的开展也更为方便。BEV感知的核心问题是&#xff1a; …

【proverif】proverif的语法3-认证协议的验证代码-案例分析

proverif-系列文章目录 【proverif】proverif的下载安装和初使用【proverif】proverif的语法1-解决中间人攻击-代码详解【proverif】proverif的语法2-各种密码原语的编码【proverif】proverif的语法3-认证协议的验证代码-案例分析 (本文) 文章目录 proverif-系列文章目录前言一…

51单片机应用从零开始(六)·逻辑运算

51单片机应用从零开始&#xff08;一&#xff09;-CSDN博客 51单片机应用从零开始&#xff08;二&#xff09;-CSDN博客 51单片机应用从零开始&#xff08;三&#xff09;-CSDN博客 51单片机应用从零开始&#xff08;四&#xff09;-CSDN博客 51单片机应用从零开始&#xff08;…

【入门篇】1.1 redis 基础数据类型详解和示例

文章目录 1. 简介2. Redis基础数据类型2.1 String类型场景示例常用命令示例 2.2 List类型场景示例 2.3 Set类型场景示例 2.4 Hash类型场景示例 2.5 Sorted Set类型 3. 使用Redis存储数据的注意事项1. 内存管理2. 数据持久化3. 高并发下的性能考量 4. 参考资料 1. 简介 Redis概…

springMvc中的拦截器【巩固】

先实现下想要的拦截器功能 package com.hmdp.utils;import com.hmdp.entity.User; import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Ht…

【SpringMvc】SpringMvc +MyBatis整理

&#x1f384;欢迎来到边境矢梦的csdn博文&#x1f384; &#x1f384;本文主要梳理 Java 框架 中 SpringMVC的知识点和值得注意的地方 &#x1f384; &#x1f308;我是边境矢梦&#xff0c;一个正在为秋招和算法竞赛做准备的学生&#x1f308; &#x1f386;喜欢的朋友可以关…

python_主动调用其他类的成员

# 主动调用其他类的成员 # 方式一: class Base(object):def f1(self):print("5个功能") class Foo(object):def f1(self):print("3个功能")# Base.实例方法(自己传self),与继承无关Base.f1(self)obj Foo() obj.f1()print("#"*20)# 方式二:按照类…

Rockdb简介

背景 最近在使用flink的过程中&#xff0c;由于要存储的状态很大&#xff0c;所以使用到了rockdb作为flink的后端存储&#xff0c;本文就来简单看下rockdb的架构设计 Rockdb设计 Rockdb采用了LSM的结构&#xff0c;它和hbase很像&#xff0c;不过严格的说&#xff0c;基于LS…

希亦ACE和小吉内衣洗衣机选哪个?两款洗衣机对比

内衣洗衣机可以称得上是实现幸福的小家电&#xff0c;它不仅懒人的福音还是我们打工人的福音&#xff0c;在每天下班之后可以有时间休息了&#xff0c;洗完澡还有要手洗内衣裤&#xff0c;真的很痛苦&#xff0c;拥有了内衣洗衣机简直是一件非常幸福的事情&#xff0c;但现在市…

wpf devexpress Property Grid创建属性定义

WPF Property Grid控件使用属性定义定义如何做和显示 本教程示范如何绑定WP Property Grid控件到数据和创建属性定义。 执行如下步骤 第一步-创建属性定义 添加PropertyGridControl组件到项目。 打开工具箱在vs&#xff0c;定位到DX.23.1: Data 面板&#xff0c;选择Prope…

HarmonyOS 实现底部导航栏

该功能实现需要Tabs、TabsController、TabContent、Column等组件 Tabs相当于Android中的BottomNavigationView TabContent相当于Android中的fragment TabBuilder内相当于每个Item Entry Component struct Main {public tabsController : object new TabsController()State c…

后端接口性能优化分析-多线程优化

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码&#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44d;三连支持&…

微软 Gradle 强强联手,Gradle 构建服务器正式开源!

作者&#xff1a;Nick Zhu - Senior Program Manager, Developer Division At Microsoft 排版&#xff1a;Alan Wang Gradle 构建服务器 (Build Server for Gradle) 在九月份&#xff0c;我们宣布 Microsoft 和 Gradle 联手探索了一种基于 Build Server Protocol&#xff08;B…

用户运营:如何搭建用户分析体系

在运营的工作范畴中&#xff0c;用户运营是很重要的一个环节&#xff0c;甚至有公司会设置专门的“用户运营”岗位。 用户运营的价值体现在多个方面&#xff0c;不仅可以帮助引流、吸引更多用户使用产品&#xff0c;在用户正式使用产品之后的运营则更为重要。通过日常用户运营&…

思源笔记的优缺点 vs Obsidian vs Logseq vs Trilium

新用户对思源笔记的印象。&#xff08;PS&#xff1a;两年前我试用过思源笔记&#xff0c;被卡顿劝退了&#xff09; 优点 相比obsidian&#xff0c; 可在文档树拖拽 拖拽调整笔记顺序 拖拽使一个笔记成为另一个笔记的子笔记&#xff0c;树状结构 设置-文档树&#xff0c;默认…