Linux文件:重定向底层实现原理(输入重定向、输出重定向、追加重定向)

2024-05-16 00:36

本文主要是介绍Linux文件:重定向底层实现原理(输入重定向、输出重定向、追加重定向),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Linux文件:重定向底层实现原理(输入重定向、输出重定向、追加重定向)

  • 前言
  • 一、文件描述符fd的分配规则
  • 二、输出重定向(>)
  • 三、输出重定向底层实现原理
  • 四、追加重定向(>>)
  • 五、输入重定向(<)
  • 六、系统调用dup2
  • 七、标准错误stderr存在意义

前言

 在Linux中,操作系统会为每一个文件创建对应的描述结构体对象struct file。该结构体中一定存在3个部分:打开文件的所有属性、文件的操作集、文件缓冲区(内存)。其中由于冯诺依曼体系决定了,无论对文件进行读操作还是写操作,都需要先将数据加载到文件缓存区

 我们在应用层进行对数据读写的操作本质上是用户缓冲区和内核数据缓冲区之间的相互拷贝!!!

一、文件描述符fd的分配规则

  1. 默认情况下,进程会默认打开3个文件:标准输入、标准输出、标准错误。
  2. 文件描述符的分配规制是:从上往下遍历查找最小的、未被使用的分别个新的文件!

二、输出重定向(>)

 下面我们将标准输出文件关闭,然后创建新的文件;并向新的文件中输出一些信息。

int main()
{close(1); //将标准输出文件显示器关闭int fd = open("log.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);//新创建文件,fd分配为1    if(fd < 0)    {    perror("open");    return 1;                                                         }    printf("what will happen\n");    return 0;
}

在这里插入图片描述

  • 我们将stdout关闭后,根据文件描述符的分配规则,新创建的文件fd为1。
  • 我们观察结果发现,原本应该向显示器打印的消息直接向新创建的文件中写入。我们将这种现象称为输出重定向>!!

三、输出重定向底层实现原理

 在C中,printf函数只能向标准输出文件中输出消息,更准确的说:printf只认文件描述符为1对应的文件

 当我们关闭标准输出后,新创建的文件所分别的文件描述符fd为1。此时文件描述符表中,下标为1的数组内容由标准输出替换为新打开的文件log.txt。此时我们调用printf输出消息时,变为向log.txt文件中写入!!

所有重定向的本质就是修改特定文件fd的下标内容!!上层fd不变,下层fd指向的内容发生改变!
在这里插入图片描述

四、追加重定向(>>)

 追加重定向和输出重定向基本相同,主要在于新创建文件的打开方式从w改为a。在底层上和输出重定向一样,都是将fd为1的下标内容替换为新文件即可!!!

int main()
{close(1); //将标准输出文件显示器关闭int fd = open("log.txt", O_CREAT | O_WRONLY | O_APPEND, 0666);//新创建文件,fd分配为1    if(fd < 0)    {    perror("open");    return 1;                                                         }    printf("what will happen\n");    return 0;
}

在这里插入图片描述

五、输入重定向(<)

 输入重定向<本质就是将待重定的文件内容替换到fd为0的下标位置。此时我们熟悉的scanf、fputs等函数读取方式会从键盘转变为新文件。即直接读取新文件中的内容

int main()
{close(0);   //将显示器关闭                                                          int fd = open("log.txt", O_RDONLY); //新创建文件fd为0if(fd < 0)    {    perror("open");    return 1;    }    char buffer[1024];    fread(buffer, 1, 1024, stdin);//从文件log.txt中读写信息    printf("stdin->fd:%d, %s\n", stdin->_fileno, buffer); return 0;
}

在这里插入图片描述

  • 在上述代码执行过程中,没有出现光标等待我们从键盘上输入信息,而是直接将log.txt中的信息输出,进一步说明了fd为0的下标内容被替换。

六、系统调用dup2

 上述进行重定向时时进行的fd指向的内容替换,都需要我们手动先将对应的1、2、3号文件关闭,非常麻烦!为此,系统中提供了相关的系统调用接口:dup2

#include <unistd.h>
int dup2(int oldfd, int newfd);//These system calls create a copy of the file descriptor oldfd

【实例】:

int main()
{int fd = open("log.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);dup2(fd, 1);printf("this is log.txt test\n");  return 0;
}

在这里插入图片描述

七、标准错误stderr存在意义

 标准输出和标准错误都是向显示器上打印信息。既然都是向同一个硬件进行写入,为什么还需要标准错误的存在?

 在项目实际运行过程中,bug是不可避免的。所以在日志中会记录很多正确信息和错误信息。而stderr存在的意义是将两者信息分开,将所有的错误信息全部写入标准错误文件中,从而降低排除的成本!!

【实例】:

  • 下面我们通过输出hello stdinhello Stderr来模拟日志中的正确信息和错误错误信息!
int main()
{fprintf(stdout, "hello stdin\n");                                                       fprintf(stderr, "hello Stderr\n");}

我们情况下,两者都在显示器上直接输出

在这里插入图片描述


我们也可以通过替换fd=1fd=2的下标对应文件,来将两者信息分开,分别向两个文件中写入
在这里插入图片描述

  1. 我们也可以将两种信息全部打印到同一个文件中:1>log.txt 2>&1。 这段代码的意思是先用log.txt文件fd中的内容替换fd=1下标中的内容,然后&1获得下标fd=1中保存的数据(即指向log.txt),最后将fd=2对应的内容用fd=1中的内容替换!!即fd=1fd=2都指向了log.txt文件。
  2. 前面讲述的输入重定向、输出重定向、追加重定向都是简写的,原始版本应该和上面重定向一样,加上对应的fd。例如输出重定向./myfile > log.txt./myfile 1 > log.txt简写。

这篇关于Linux文件:重定向底层实现原理(输入重定向、输出重定向、追加重定向)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/993374

相关文章

Java实现优雅日期处理的方案详解

《Java实现优雅日期处理的方案详解》在我们的日常工作中,需要经常处理各种格式,各种类似的的日期或者时间,下面我们就来看看如何使用java处理这样的日期问题吧,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言一、日期的坑1.1 日期格式化陷阱1.2 时区转换二、优雅方案的进阶之路2.1 线程安全重构2

Android实现两台手机屏幕共享和远程控制功能

《Android实现两台手机屏幕共享和远程控制功能》在远程协助、在线教学、技术支持等多种场景下,实时获得另一部移动设备的屏幕画面,并对其进行操作,具有极高的应用价值,本项目旨在实现两台Android手... 目录一、项目概述二、相关知识2.1 MediaProjection API2.2 Socket 网络

使用Python实现图像LBP特征提取的操作方法

《使用Python实现图像LBP特征提取的操作方法》LBP特征叫做局部二值模式,常用于纹理特征提取,并在纹理分类中具有较强的区分能力,本文给大家介绍了如何使用Python实现图像LBP特征提取的操作方... 目录一、LBP特征介绍二、LBP特征描述三、一些改进版本的LBP1.圆形LBP算子2.旋转不变的LB

Redis消息队列实现异步秒杀功能

《Redis消息队列实现异步秒杀功能》在高并发场景下,为了提高秒杀业务的性能,可将部分工作交给Redis处理,并通过异步方式执行,Redis提供了多种数据结构来实现消息队列,总结三种,本文详细介绍Re... 目录1 Redis消息队列1.1 List 结构1.2 Pub/Sub 模式1.3 Stream 结

C# Where 泛型约束的实现

《C#Where泛型约束的实现》本文主要介绍了C#Where泛型约束的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录使用的对象约束分类where T : structwhere T : classwhere T : ne

将Java程序打包成EXE文件的实现方式

《将Java程序打包成EXE文件的实现方式》:本文主要介绍将Java程序打包成EXE文件的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录如何将Java程序编程打包成EXE文件1.准备Java程序2.生成JAR包3.选择并安装打包工具4.配置Launch4

Linux命令之firewalld的用法

《Linux命令之firewalld的用法》:本文主要介绍Linux命令之firewalld的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux命令之firewalld1、程序包2、启动firewalld3、配置文件4、firewalld规则定义的九大

MySQL索引的优化之LIKE模糊查询功能实现

《MySQL索引的优化之LIKE模糊查询功能实现》:本文主要介绍MySQL索引的优化之LIKE模糊查询功能实现,本文通过示例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一、前缀匹配优化二、后缀匹配优化三、中间匹配优化四、覆盖索引优化五、减少查询范围六、避免通配符开头七、使用外部搜索引擎八、分

Python实现特殊字符判断并去掉非字母和数字的特殊字符

《Python实现特殊字符判断并去掉非字母和数字的特殊字符》在Python中,可以通过多种方法来判断字符串中是否包含非字母、数字的特殊字符,并将这些特殊字符去掉,本文为大家整理了一些常用的,希望对大家... 目录1. 使用正则表达式判断字符串中是否包含特殊字符去掉字符串中的特殊字符2. 使用 str.isa

Spring Boot 集成 Quartz并使用Cron 表达式实现定时任务

《SpringBoot集成Quartz并使用Cron表达式实现定时任务》本篇文章介绍了如何在SpringBoot中集成Quartz进行定时任务调度,并通过Cron表达式控制任务... 目录前言1. 添加 Quartz 依赖2. 创建 Quartz 任务3. 配置 Quartz 任务调度4. 启动 Sprin