重定向(dup、dup2、dup3)--Linux

article/2025/10/1 13:22:27

文章目录

  • 🚩重定向是什么?
  • 🚩dup系列函数实现重定向
    • 🍁dup
    • 🍁dup2
    • 🍁dup3
  • 🚩总结

🚩重定向是什么?

在上篇博客[文件描述符]中我曾提到了一个有意思的证明:进程在最开始运行的时候,首先打开了三个文件,分别是标准输入流、标准输出流、标准错误输出流。证明的时候我是把标准输出留给关闭了,然后紧接着创建的文件就会占用已关闭的标准输出流,使得本该流向显示器的数据流向了新创建的文件。先不谈底层的原理,就只看表象,就像是使数据流的方向从一个方向,指向了另一个方向,完成了数据流的方向重定向。现在再次理解重定向就好理解得多了:重新锚定方向

🚩dup系列函数实现重定向

就如我上面提到的证明过程,虽说最后也实现了重定向的操作,但是这都是我们手动一步一步设计的环节,先关闭再创建,并且是重定向哪个,哪个就要关闭,关闭和创建之间,不能有其他文件的创建,否则就会把关闭的文件给占用掉了,从而导致定向到了错误的地方。

晕😵~,感觉好麻烦。但是别担心,操作系统给我们提供了函数接口,帮我们在文件管理的层面直接解决这个问题。

image-20221106210452858

上图是dup系列的重定向函数,同样的,也都是系统提供给我们的函数,属于直接对内核数据进行修改。

由于看着实在是太多而且还是英文,这里就由我一步一步从dup开始解析吧。

🍁dup

image-20221106230337023

头文件:unistd.h

参数:oldfd–旧的文件描述符(意味着最终要指向的文件,用old来描述确实很奇怪,但是没办法,将就着理解叭)

返回值:在成功的情况下,返回新文件描述符。如果出现错误,则返回-1,并适当设置errno。

返回的文件描述符重新指向了oldfd指向的文件,这个新的文件描述符是没有被使用的最小的文件描述符。

⌨整点代码测试:

#include <iostream>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PATH_NAME "log.txt"
using namespace std;
int main()
{umask(0);int oldfd = open(PATH_NAME, O_CREAT | O_RDWR | O_TRUNC, 0600);if (oldfd < 0){cerr << strerror(errno) << endl;exit(2);}cout << "oldfd: " << oldfd << endl;int newfd = dup(oldfd);cout << "newfd: " << newfd << endl;const char *str = "Hello, Kangkang, this is Michael\n";char output[1024];write(newfd, str, strlen(str));lseek(oldfd, 0, SEEK_SET);ssize_t size = read(oldfd, output, sizeof(output) - 1);if (size){output[size] = '\0';cout << output;}else{cout << "do nothing" << endl;}close(oldfd);close(newfd);return 0;
}

💻:

image-20221106223158740

首先以读写的方式打开一个文件log.txt,返回一个文件描述符oldfd,接着调用dup返回一个新的文件描述符,这个文件描述符按道理讲也是指向log.txt的,接着我们以新的文件描述符去从log.txt中读取,事实证明确实可以,dup的功能得到证实。

🍁dup2

dup虽然可以完成重定向,但是使用起来也不是那么方便,因为它只能重定向未被使用的最小的文件描述符,对已有的文件不能完成重定向。因此为了解决这个问题,又提供了dup的进阶版–dup2,可以实现指定的文件描述符newfd的重定向。

image-20221106230452314

头文件:unistd.h

参数:

oldfd–最终指向的文件的 文件描述符

newfd–需要被重定向的文件描述符

newfd与oldfd一样的时候,什么都不做,直接返回newfd

返回值:在成功的情况下,返回新文件描述符。如果出现错误,则返回-1,并适当设置errno。

这里的newfd就是我们用户指定的需要被重定向的文件描述符。

⌨整点代码测试:

#include <iostream>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PATH_NAME "log.txt"
using namespace std;
int main()
{umask(0);int oldfd = open(PATH_NAME, O_CREAT | O_RDWR | O_TRUNC, 0600);if (oldfd < 0){cerr << strerror(errno) << endl;exit(2);}cout << "oldfd: " << oldfd << endl;int newfd = dup2(oldfd, 1);        //使得标准输出流指向log.txtif (newfd == -1){cerr << strerror(errno) << endl;exit(2);}//以下的cout全部都是向log.txt中输出数据cout << "newfd: " << newfd << endl; const char *str = "Hello, Kangkang, this is Michael\n";cout << str;//使得标准输入流指向log.txtnewfd = dup2(oldfd,0);if (newfd == -1){cerr << strerror(errno) << endl;exit(2);}//重新设置文件偏移量,从文件的起始位置开始lseek(oldfd,0,SEEK_SET);char output[1024];cin >> output;  //仅仅从文件log.txt中读取一次数据cout << output << endl;  //再次将其放入文件中close(oldfd);close(newfd);return 0;
}

💻:

image-20221106234417970

之所以只有一个"newfd:"被读取到了,是因为cin提取流默认读到空格’ ‘或者换行符’\n’都会停止读取。并且我们发现上面的代码我们使用了lseek来改变文件的偏移量,改变了oldfd的也会导致newfd的文件偏移量发生同步变化,这也就是说新的文件描述符不仅仅只指向了oldfd,并且还共享文件偏移量和文件的状态。

讲真的,一般情况下,我们用到最多的就是指定文件的重定向dup2,毕竟重定向一般都是有需求了才会重定向的叭🤔。

🍁dup3

image-20221107105459533

dup3和dup2功能相似,只是多了一个参数:flags,并且要宏定义_GNU_SOURCE,这样就可以设置flags为库里宏定义好的O_CLOEXEC,那这样做的意义是什么呢?

🔺首先我们得清楚O_CLOEXEC的作用是干嘛的:

由于在调用exec系列函数([进程替换]((1条消息) 进程控制–Linux_皮皮蜥的博客-CSDN博客))时,由于进程里的程序被替换,文件描述符就会被释放,但是文件表项却没被释放,有点像内存泄漏,相当于栈上的指针被释放了,但是指针指向的堆上的空间并没有被释放。也就是说,假如我们知道了替换程序前的文件描述符,就可以在替换后的程序中继续对之前打开的文件进行操作,这意味着新的程序会继承被替换的程序的文件表项(文件指针数组)。

但是我们替换程序的时候一般的需求都是使用新的程序,很少需要用到之前已打开的文件,这就意味着我们需要去关闭那些我们在新的程序中用不到的文件(闲置文件)。更严重的情况是如果不关闭某些文件,由于替换之后文件描述符的丢失,会造成一些文件始终无法关闭的情况,造成不可预测的结果。但是如果已经打开了好多文件,手动去一个一个的关闭肯定很麻烦,于是我们可以在open这些文件的时候在open的flags里也加上O_CLOEXEC使得在进程替换时能够直接识别到文件描述符中有O_CLOEXEC开启的标志,在进程替换之前就把各个相应的文件全部自动关闭,省心省力。

那么绕了一圈,在dup3中设置flags的用途又是干嘛的呢?

由于给newfd加了O_CLOEXEC标志,进程替换前会将newfd会被自动关闭,也就是把重定向给关闭了!然后替换的新的程序继承的文件描述符就没有相关的重定向了。

⌨整点代码测试一下:

#include <iostream>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PATH_NAME "log.txt"
using namespace std;
int main()
{umask(0);int fd =open(PATH_NAME,O_CREAT | O_RDWR | O_TRUNC,0600);if(fd<0){cerr << strerror(errno) << endl;exit(2);}const char *str = "Hello, Kangkang, this is Michael";//一旦发生进程替换就把对应的重定向newwfd关闭,相当于cout无法被正常使用了(1所对应的文件指针就变成nullptr)。dup3(fd,1,O_CLOEXEC);  cout<<str<<endl;execlp("ls","ls","-al",nullptr);cerr<<"something wrong"<<endl;close(fd);return 0;
}

💻:

在这里插入图片描述

观察到发生了写入错误,并提示错误的文件描述符,这不就是因为fd:1被关闭了吗?并且由于文件异常关闭,导致log.txt中并没有数据写入。这里我是为了方便观察才使用的标准输出重定向做的例子,实际使用的时候基本都不会出现这种进程异常结束的现象,只会单纯地把重定向取消罢了。

🚩总结

重定向的知识并不简单,首先得清楚文件描述符的概念,其次得知道进程替换的具体应用细节。并且还得熟练掌握dup函数族,实际应用还是比较重要的。

我在整理这篇博客的时候也是又回头看了一下才疏通了知识脉络,希望正在学习的你能够从这篇文章中有所收获吧,有问题欢迎留言或私信,我们一起学习进步🐾


http://chatgpt.dhexx.cn/article/7tjbXyYU.shtml

相关文章

协议篇————3、DUP协议详解

一、UDP协议简介 UDP是OSI模型中一种无连接的传输层协议&#xff0c;它主要用于不要求分组顺序到达的传输中&#xff0c;分组传输顺序的检查与排序由应用层完成&#xff0c;提供面向事务的简单不可靠信息传送服务。UDP 协议基本上是 IP协议与上层协议的接口。UDP协议适用端口分…

8086汇编语言dup指令学习

dup是duplicate的缩写&#xff0c;重复的意思&#xff1b; 用来定义重复的字节、字、双字、结构等内存缓冲区&#xff1b; db x dup()&#xff0c;x是重复的次数&#xff0c;&#xff08;&#xff09;里是要重复的数&#xff0c;逗号分隔&#xff1b; db 重复的次数 dup &…

文件描述符的复制(dup)

1.dup和dup2函数的介绍 int dup(int oldfd); 返回一个文件描述符&#xff0c;参数是要复制的那个文件描述符。2.使用dup进行文件描述符的复制 &#xff08;1&#xff09;dup系统调用对fd进行复制&#xff0c;会返回一个新的文件描述符&#xff0c;&#xff08;譬如原来的是3&a…

java面试题库app

java面试题库app是一款专为正在找工作的java程序员打造的软件&#xff0c;这款软件集合了精选的Java面试笔试题目及答案&#xff0c;致力于帮助用户通过面试和笔试&#xff0c;有需要的朋友快来下载吧。 java面试题库app特色 1、java基础(全面的java基础面试题&#xff0c;jav…

java-题库

java-题库 一、hashMap的底层实现1.1、hashMap概述1.2、JDK1.7-扩容源码1.3、JDK1.7链表迁移过程1.4、JDK1.8下的扩容机制 二、java面向对象的三大特征三、JVM相关四、集合相关五、JDK1.8新特性5.1、接口内可以添加非抽象的方法实现5.2、Lambda表达式5.3、函数式接口5.4、Strea…

Java二级考试题库

1、 2、 3、 4、 5、 6、 7、 8、 9、 10、 11、 12、 13、 14、 15、 16、 17、 18、 19、 20、 21、 22、 23、 24、 25、 26、 27、 注意&#xff1a;&#xff08;1&#xff09;基本型数据变量传递的是数值&#xff0c;修改会直接影响数据&#xff1b;而引用型变量传递的是…

java基础_题库详解

删除线格式 # 1 JDK和JRE有什么区别&#xff1f; JRE&#xff1a;Java Runtime Environment&#xff08; java 运行时环境&#xff09;。即java程序的运行时环境&#xff0c;包含了 java 虚拟机&#xff0c;java基础类库。 JDK&#xff1a;Java Development Kit&#xff08; ja…

Java笔试题

随机从1到100000中间随机取出100个不同的质数&#xff0c;然后按从小到大的顺序排列 public class IsPrime { //工具类// 输入一个数,判断是否为质数,费时方法public static Integer isPrime(int num) {if (num 0 || num 1)return -1;for (int i 2; i < num - 1; i) {if …

Java期末题库

根据如下图所示数据库信息&#xff0c;完成1-3小题。 &#xff08;1&#xff09;使用JDBC数据库连接技术&#xff0c;编写JDBCUtil工具类&#xff0c;在该类中定义getCon方法用于获得与数据库的连接对象&#xff0c;定义closeAll方法用于释放连接对象等资源。 import java.sql…

2022史上最全java面试题题库大全800题含答案

2022史上最全java面试题题库大全800题含答案 1、 meta标签的作用是什么 2、 ReenTrantLock可重入锁&#xff08;和synchronized的区别&#xff09;总结 3、 Spring中的自动装配有哪些限制&#xff1f; 4、 什么是可变参数&#xff1f; 5、 什么是领域模型(domain model)&#…

全国计算机等级考试——二级JAVA完整大题题库【五十三道】

全国计算机等级考试二级 JAVA 题目内容 编写于2023.04.10 分为40道选择题和3道大题&#xff08;大题是程序填空类型&#xff09; 其中选择题只能进去做一次&#xff0c;一旦退出来则不可再进&#xff08;注意&#xff01;&#xff09;。大题可以重复进入&#xff0c;重复做。…

java面试题题库大全800题

1、 meta标签的作用是什么 2、 ReenTrantLock可重入锁&#xff08;和synchronized的区别&#xff09;总结 3、 Spring中的自动装配有哪些限制&#xff1f; 4、 什么是可变参数&#xff1f; 5、 什么是领域模型(domain model)&#xff1f;贫血模型(anaemic domain model)和充血模…

Java基础习题库

Java基础习题库 目录 Java基础习题库 一、Java基础之概述 1.JDK,JRE,JVM三者之间的关系&#xff0c;以及JDK、JRE包含的主要结构有哪些 2.为什么要配置path环境变量&#xff1f;如何配置&#xff1f; 3.创建如下的类&#xff0c;使得运行的话可以输出&#xff1a; 4.编译…

Javase MINA框架

MINA是一个基于TCP/IP通信的JAVA框架&#xff0c;MINA可以帮助我们快速开发高性能、高扩展的网络通信应用&#xff0c;提供了事件驱动、异步操作的编程模型&#xff0c;默认使用NIO作为底层支持 客户端实现步骤&#xff1a;

基于Java后台(Springboot框架)+前端小程序(MINA框架)+Mysql数据库的疫苗预约小程序系统设计与实现

项目背景和意义 目的&#xff1a;本课题主要目标是设计并能够实现一个基于微信小程序疫苗预约系统&#xff0c;前台用户使用小程序&#xff0c;后台管理使用基JAVAMySql技术&#xff1b;通过后台添加疫苗内容、价格、开放预约的日期和时间段&#xff0c;用户通过小程序登录&…

Spring Boot Starter之Mina框架封装

封装目的 目前是将Mina与Springboot的自动装配然后结合自身公司的业务对Mina进行统一的封装&#xff0c;目的是让代码可读性提高&#xff0c;也跟加便于使用&#xff0c;不用再写重复的代码 功能介绍 实现功能&#xff1a;可以通过注解的方式自动配置Mina服务端相关的代码 …

java mina 框架 获取字节_浅谈Java的Mina框架传递对象

接触java的Mina框架已经有很多时间了&#xff0c;在网上也读过了很多的相关文章&#xff0c;发现Mina框架的确是一个值得拿来好好研究的东西&#xff0c;前些日子写了一个山寨QQ项目&#xff0c;其中的通信部分用到了java中自带的InputStream&#xff0c;OutputStream&#xff…

Java网络编程之MINA框架(1)

1、MINA&#xff1a; 一个简洁易用的基于TCP/IP通信的JAVA框架 2、下载地址&#xff1a; http://mina.apache.org/downloads-mina_2_0.html 3、至少需要&#xff1a; (在下载的文件中找以下两个jar包导入工程中) mina-core-2.0.21.jar、slf4j-api-1.7.26.jar 4、开发一个Mina应…

浅谈Mina框架

一、简介 Apache Mina Server 是一个网络通信应用框架&#xff0c;也就是说&#xff0c; 也可以提供JAVA对象的序列化服务、虚拟机管道通信服务等&#xff09;&#xff0c;Mina可以帮助我们快速开发高性能、高扩展性的网络通信应用&#xff0c;Mina提供了事件驱动、异步&#…

java mina框架实例_Apache Mina框架实践

1.为什么要用Apache Mina框架 ApacheMina Server 是一个网络通信应用框架,Mina 可以帮助我们快速开发高性能、高扩展性的网络通信应用&#xff0c;Mina 提供了事件驱动、异步(Mina 的异步IO 默认使用的是JAVANIO 作为底层支持)操作的编程模型。 2.ApacheMina框架使用 Mina的执行…