Linux:文件描述符与重定向

目录

 一、文件描述符

1.文件内核对象 

2.文件描述符分配原则

二、文件重定向

1.重定向的现象

输出重定向

输入重定向 

dup2

2.重定向的使用

三、标准输出和标准错误 


继上篇文章中,我们了解了fd打印的值为文件描述符,那么它还有什么作用呢? 

 一、文件描述符

1.文件内核对象 

我们学习了open函数,知道了文件描述符就是一个整数。Linux 进程默认情况下会有 3 个缺省打开的文件描述符,分别是标准输入 0, 标准输出 1, 标准错误 2。0、1、2 对应的物理设备一般是:键盘显示器显示器

ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

输入输出程序: 

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>int main()
{char buf[1024];// 定义一个1024字节的缓冲区ssize_t s = read(0, buf, sizeof(buf));//从标准输入(stdin)读取数据到缓冲区bufif(s > 0)// 检查是否成功读取到数据{buf[s] = 0; // 手动添加字符串终止符'\0'write(1, buf, strlen(buf));// 将数据写入标准输出write(2, buf, strlen(buf));// 将数据写入标准错误//write() 是直接写入内核,无需手动刷新缓冲区。}return 0;
}

运行后,输出一个yes,就会再往显示屏打印两个yes 

 

write是如何通过fd找到要打印的文件的呢?  

重新理解文件描述符表:


站在用户的角度,对于文件的操作有这些诸如read、write这些系统调用,也有这些系统调用进行了一定的封装后诞生的C语言库函数,这些系统调用的内部会在内存中进行一系列操作,当程序运行的时候,进程里有个叫PCB的结构体,进程的PCB中有这么一块专门的区域名字叫作fd_array[],它的本质是一个指针数组,它会指向一个一个结构体,而每一个结构体中存储的是关于这个文件的信息,这个结构体叫做struct file,而前面所说的文件描述符fd,本质上就是这个指针数组的下标。系统默认会打开三个文件:标准输入、输出和错误。比如你调用read或write的时候,系统会根据fd找到对应的file结构体,然后操作对应文件。不过数据读写不是直接和硬盘打交道的——因为CPU只能和内存玩,所以读写前得把数据先从硬盘加载到内存里的文件缓冲区里,进程才能从中读写。不管读还是写,都是在内核缓冲区和用户空间之间倒腾数据,这也就是为什么系统调用看起来像是在内存里搞事情的原因啦。 结论:在应用层对于数据的读写,本质上是将内核缓冲区的数据进行来回拷贝。

2.文件描述符分配原则

我们知道因为系统会默认打开三个文件,分别占用0,1,2这三个位置, 那么把前面这几个关掉呢?

关闭0: 

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>int main()
{close(0);int fd = open("log.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);close(fd);return 0;
}

运行结果(关闭2也是一样的效果):

 

由此可以推测出,文件描述符的分配规则是:寻找最小的,没有被使用的数据的位置,分配给指定的打开文件 

二、文件重定向

1.重定向的现象

刚刚其实我们已经细微的发现了重定向了,但是现在我们将要学的更加细:

输出重定向

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>// 重定向
void test()
{close(1);int fd = open("log.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);if(fd < 0){perror("open fail\n");exit(1);}printf("fd-> %d\n", fd);printf("stdout->fd: %d\n", stdout->_fileno);fflush(stdout);close(fd);
}int main()
{test();return 0;
}

 运行结果:

 

运行结果是,在屏幕上没有任何信息,但在log.txt中居然出现了输出信息。 

当程序通过关闭标准输出(fd=1)并重新打开一个文件(如log.txt)时,由于文件描述符遵循"用完即丢"的复用规则,新文件会抢占原本属于标准输出的1号索引位置。此时所有以1为默认目标的输出操作(包括printf)都会被悄悄重定向——它们不再往显示器写数据,而是像对待普通文件一样向log.txt写入内容。这个过程中,Linux内核通过VFS将设备抽象成统一的文件对象,使得无论stdout原本指向的是屏幕还是磁盘文件,最终都表现为对特定文件描述符的操作。这种设计巧妙利用了文件描述符的索引特性,实现了I/O重定向的核心逻辑:系统只认数字标号,不关心具体绑定的物理设备,真正实现了"一切皆文件"的统一管理理念总结:新打开的文件描述符取代了stdout原先的描述符,大家只认fd==1的文件。 

输入重定向 

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>int main()
{close(0);int fd = open("log.txt", O_RDONLY);if (fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);char buf[64];fgets(buf, sizeof buf, stdin);printf("%s", buf);return 0;
}

 

重定向的本质,就是修改特征文件fd的下标内容。上层的fd不变,变化的是底层fd指向的内容,也就是所谓文件描述符级别的数组内容的拷贝。那这样的写法还是太奇怪了,每次都要把一个文件关闭再打开一个新的文件,作为系统理应给操作者提供这样替换文件描述符的系统调用,事实上也确实提供了这样的系统调用。 

dup2

SYNOPSIS#include <unistd.h>int dup(int oldfd);int dup2(int oldfd, int newfd);
*  If oldfd is not a valid file descriptor, then the call fails, and newfd is not closed.
*  If oldfd is a valid file descriptor, and newfd has the same value as oldfd, then dup2() does nothing, and returns newfd.

简单来说,就是用oldfd去替换newfd,保留下来的是oldfd,那么上面的代码就可以被改良成这样:

void redir2()
{int fd = open("log.txt", O_CREAT | O_WRONLY | O_APPEND, 0666);if(fd < 0){perror("open fail\n");exit(1);}dup2(fd, 1);printf("fd-> %d\n", fd);printf("stdout->fd: %d\n", stdout->_fileno);fflush(stdout);close(fd);
}

运行结果: 

 

fd->3,fd也没有变化,但是就是没有往显示屏上打印,依旧是往文件里打印的。也就是说,不需要你去关闭一个文件秒速符,而是用一个文件描述符暂时的代替另外一个文件描述符,这样不仅更加准确,而且更加明了。

要输出的文件描述符是 1,而要重定向的目标文件描述符是 fd (echo “hello” > log.txt),dup2 应该怎么传参 —— dup2(1, fd) || dup2(fd, 1) ? 

*  If oldfd is not a valid file descriptor, then the call fails, and newfd is not closed.
*  If oldfd is a valid file descriptor, and newfd has the same value as oldfd, then dup2() does nothing, and returns newfd.

很明显依靠函数原型,我们就能认为 dup2(1, fd),因为 1 是先打开的,而 fd 是后打开的.可实际上并不是这样的,文档中说 newfd 是 oldfd 的一份拷贝,这里拷贝的是文件描述符对应数组下标的内容,所以数组内容最终应该和 oldfd 一致。换而言之,这里就是想把让 1 不要指向显示器了,而指向 log.txt,fd 也指向 log.txt。所以这里的 oldfd 对应 fd,newfd 对应 1,所以应该是 dup2(fd, 1)。

oldfd copy to newfd -> 最后要和谁一样? oldfd

假设输出重定向:显示器(1) -> log.txt(3)。应该是 dup2(1, 3);,还是dup2(3, 1); ?

dup2(3, 1);

3 的内容 copy 到 1 里面 -> 最终和 3 一致

2.重定向的使用

 重定向其实并不陌生,在之前的学习中已经用过重定向,只是那是还没有建立起来一个基础的概念,先看下面的指令演示:

[root@iZbp1157ft1ib0ydj8jqtzZ test]# echo "hello linux" > log.txt
[root@iZbp1157ft1ib0ydj8jqtzZ test]# cat log.txt
hello linux

这段指令的含义就是,把hello linux重定向到log.txt中,其实这样的操作符还有下面的这几种,一一进行介绍

>:这个操作符表示的是输出重定向,意思就是把内容输出到某个文件中,有些类似于以w的方式打开一个文件并进行写入。

>>:这个操作符表示的是追加重定向,意思就是把内容追加输出到某个文件中,有些类似于append的方式进行写入。

<:这个操作符表示的是输入重定向,表示把原来的内容输入输入到某个文件中,相当于是替换了标准输入流的文件。

三、标准输出和标准错误 

前面的知识已经足以理解为什么要有标准输入和标准输出,但是还有一个问题有待解决,标准错误的意义是什么呢?难道标准输出的信息还不够吗?

答案是确实不够,在对于大型项目的时候,会有很多的输出信息,这些输出信息有些是正常信息,有些是异常信息,而对于开发者来说他们需要的是错误信息,因此对于如何获取错误信息就显得至关重要,于是标准错误流信息就诞生了,对于正常来说可能没有太多的感觉,但是实际上,没有感觉的原因是因为标准错误和标准输出的文件对象都是显示器,而实际上这是可以被替换的,基于这个原理可以做出下面的测试代码:

int main()
{printf("this is normal message\n");perror("this is error message\n");return 0;
}

 运行和操作结果:

[root@iZbp1157ft1ib0ydj8jqtzZ test]# ./test 1>out.txt 2>error.txt
[root@iZbp1157ft1ib0ydj8jqtzZ test]# cat out.txt
this is normal message
[root@iZbp1157ft1ib0ydj8jqtzZ test]# cat error.txt
this is error message
: Success

利用上面的原理可以写出这样的测试代码,把正确信息存储到一个文件中,把错误信息存储到另外一个文件中,这样就能知道哪里是错误哪里是正确了。

1>&2意思是把标准输出重定向到标准错误。

2>&1意思是把标准错误输出重定向到标准输出。

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

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

相关文章

白盒测试(3):PCB阻抗测试方法

PCB阻抗测试是确保信号完整性的关键&#xff0c;通过测量走线的特性阻抗&#xff0c;验证其是否符合设计目标。常用方法包括时域反射法&#xff08;TDR&#xff09;、网络分析仪法和仿真软件法。TDR通过分析反射信号定位阻抗异常&#xff0c;网络分析仪通过S参数计算阻抗&#…

CentOS 7 安装Nginx-1.26.3

无论安装啥工具、首先认准了就是官网。Nginx Nginx官网下载安装包 Windows下载&#xff1a; http://nginx.org/download/nginx-1.26.3.zipLinxu下载 wget http://nginx.org/download/nginx-1.26.3.tar.gzLinux安装Nginx-1.26.3 安装之前先安装Nginx依赖包、自行选择 yum -y i…

笔记:如何使用XAML Styler以及在不同的开发环境中使用一致

一、目的&#xff1a;分享如何使用XAML Styler以及在不同的开发环境中使用一致 XAML Styler 是一个 Visual Studio 扩展&#xff0c;用于自动格式化和整理 XAML 文件。它可以帮助开发者保持一致的代码风格&#xff0c;提高代码的可读性和可维护性。以下是如何在 Visual Studio …

分布式存储学习——HBase概述

1.1 HBase概述 1.1.1 理解大数据背景 1.1.2 HBase是什么 1.1.3 HBase与Hadoop的关系 1.1.4 HBase的核心功能模块 1.1.5 HBase的应用场景和经典案例 1.1.6 小结 本文参考于学校《HBase应用于开发》教材 1.1 HBase概述 本节将介绍大数据背景和HBase的基本概念&#xff0c…

交叉编译openssl及curl

操作环境&#xff1a;Ubuntu20.04 IDE工具&#xff1a;Clion2020.2 curl下载地址&#xff1a;https://curl.se/download/ openssl下载地址&#xff1a;https://openssl-library.org/source/old/index.html 直接交叉编译curl会报错找不到openssl&#xff0c;所以需要先交叉编…

MDM 如何彻底改变医疗设备的远程管理

在现代医疗行业迅速发展的格局中&#xff0c;医院和诊所越来越依赖诸如医疗平板和移动工作站等移动设备。这些设备在提高工作效率和提供卓越的患者护理方面发挥着关键作用。然而&#xff0c;随着它们的广泛使用&#xff0c;也带来了一系列挑战&#xff0c;例如在不同地点确保数…

零基础C语言学习日志22(自定义类型:联合和枚举)

目录 联合体 联合体类型的声明 联合体的特点 相同成员联合体和结构体的对比 联合体大小的计算 例子 枚举类型 枚举类型的声明 枚举类型的优点 枚举类型的使用 联合体 联合体类型的声明 像结构体一样&#xff0c;联合体也是由一个或者多个成员构成&#xff0c;这些成…

天津大学02-深度解读DeepSeek:部署、使用、安全【文末附下载链接】

大模型风险与不当用例——价值观错位 大模型与人类价值观、期望之间的不一致而导致的安全问题&#xff0c;包含&#xff1a;• 社会偏见&#xff08;Social Bias&#xff09;LLM在生成文本时强化对特定社会群体的刻板印象&#xff0c;例如将穆斯林与恐怖主义关联&#xff0c;或…

[C语言日寄] 字符串操作函数的使用及其拓展

【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋&#xff1a;这是一个专注于C语言刷题的专栏&#xff0c;精选题目&#xff0c;搭配详细题解、拓展算法。从基础语法到复杂算法&#xff0c;题目涉及的知识点全面覆盖&#xff0c;助力你系统提升。无论你是初学者&#xff0c;还是…

Qt 进度条与多线程应用、基于 Qt 的文件复制工具开发

练习1&#xff1a;Qt 进度条与多线程应用 题目描述 开发一个基于 Qt 的应用程序&#xff0c;该应用程序包含一个水平进度条&#xff08;QSlider&#xff09;&#xff0c;并且需要通过多线程来更新进度条的值。请根据以下要求完成代码&#xff1a; 界面设计&#xff1a; 使用 QS…

Gartner:数据安全平台DSP提升数据流转及使用安全

2025 年 1 月 7 日&#xff0c;Gartner 发布“China Context&#xff1a;Market Guide for Data Security Platforms”&#xff08;《数据安全平台市场指南——中国篇》&#xff0c;以下简称指南&#xff09;&#xff0c;报告主要聚焦中国数据安全平台&#xff08;Data Securit…

道可云人工智能每日资讯|《奇遇三星堆》VR沉浸探索展(淮安站)开展

道可云元宇宙每日简报&#xff08;2025年3月5日&#xff09;讯&#xff0c;今日元宇宙新鲜事有&#xff1a; 《奇遇三星堆》VR沉浸探索展&#xff08;淮安站&#xff09;开展 近日&#xff0c;《奇遇三星堆》VR沉浸探索展&#xff08;淮安站&#xff09;开展。该展将三星堆文…

Spring AI Alibaba + Ollama:国产大模型DeepSeek LLM的低成本AI应用开发认知

写在前面 官方文档很详细&#xff0c;有开发需求可以直接看文档https://java2ai.com/docs/1.0.0-M5.1/get-started/博文内容为一个开发Demo&#xff0c;以及API简单认知理解不足小伙伴帮忙指正 &#x1f603;,生活加油 我看远山&#xff0c;远山悲悯 持续分享技术干货&#xf…

解决:Word 保存文档失败,重启电脑后,Word 在试图打开文件时遇到错误

杀千刀的微软&#xff0c;设计的 Word 是个几把&#xff0c;用 LaTex 写完公式&#xff0c;然后保存&#xff0c;卡的飞起 我看文档卡了很久&#xff0c;就关闭文档&#xff0c;然后 TMD 脑抽了重启电脑 重启之后&#xff0c;文档打不开了&#xff0c;显示 杀千刀的&#xff…

掌握高效大模型任务流搭建术(二):链式流程如何赋能 AI 处理能力提升

前言&#xff1a; 在上一篇文章中&#xff0c;我们初步探索了 LangChain 的基础链式操作——LLMChain。它巧妙地将大语言模型&#xff08;LLM&#xff09;与提示模板&#xff08;Prompt Template&#xff09;相结合&#xff0c;为模型交互逻辑的封装提供了一种简洁而高效的方式…

虚拟卡 WildCard (野卡) 保姆级开卡教程

本文首发于只抄博客&#xff0c;欢迎点击原文链接了解更多内容。 前言 本篇教程为 WildCard 的介绍以及开卡教学&#xff0c;要了解不同平台&#xff08;Grok、Talkatone 等&#xff09;的订阅方式请移步《订阅教程》分类 当我们想要充值国外平台会员时&#xff0c;一般都需要使…

计算机数据库三级刷题总结(博主89分已过,总结的内容分享)

计算机数据库三级刷题总结&#xff08;博主89分已过&#xff0c;总结的内容分享&#xff09; 文章目录 计算机数据库三级刷题总结&#xff08;博主89分已过&#xff0c;总结的内容分享&#xff09;一、 数据库设计阶段二、事务相关三、数据库设计顺序四、数据库三级模式与二层映…

记录一些面试遇到的问题

重载和重写的区别 重载是overload&#xff0c;覆盖是override 重载属于编译时多态&#xff0c;覆盖属于运行时多态 运行时多态和编译时多态 运行时多态指的是在运行的时候才知道要调用哪一个函数&#xff0c;编译时多态是指在编译的时候就知道调用哪一个函数。 运行时多态…

HBuilder X 使用 TortoiseSVN 设置快捷键方法

HBuilder X 使用 TortoiseSVN 设置快捷键方法 单文件&#xff1a;(上锁&#xff0c;解锁&#xff0c;提交&#xff0c;更新) 安装好 TortoiseSVN &#xff0c;或者 按图操作&#xff1a; 1&#xff0c;工具栏中 【自定义快捷键】 2&#xff0c;点击 默认的快捷键设置&…

JmeterHttp请求头管理出现Unsupported Media Type问题解决

JmeterHttp请求头管理出现Unsupported Media Type问题解决 大多数的app与pc端压测的时候都会出现这种情况 当我们在jemter测试当中当中遇见Unsupported Media Type&#xff0c;有一种可能就是我们请求的网页的content-Type的类型与我们测试的时候的类型不一致 解决方法 可以添…