Linux文件描述符fd详解重定向原理

news2024/11/13 9:28:52

文章目录

  • 什么是文件描述符fd?
  • 文件描述符的分配规则
  • 重定向的原理

什么是文件描述符fd?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE_NAME(n) "tag.txt"#n
int main()
{
    int fd1 = open(FILE_NAME(1), O_RDWR | O_CREAT | O_TRUNC, 0666);
    int fd2 = open(FILE_NAME(2), O_RDWR | O_CREAT | O_TRUNC, 0666);
    int fd3 = open(FILE_NAME(3), O_RDWR | O_CREAT | O_TRUNC, 0666);
    int fd4 = open(FILE_NAME(4), O_RDWR | O_CREAT | O_TRUNC, 0666);
    printf("fd1=%d\n", fd1);
    printf("fd2=%d\n", fd2);
    printf("fd3=%d\n", fd3);
    printf("fd4=%d\n", fd4);
    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    return 0;
}

image-20230104173356557

可以看到,创建了四个文件,文件描述符分别是3、4、5、6,那么前面的数字去哪里了呢?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE_NAME(n) "tag.txt"#n
int main()
{
	printf("fd1=%d\n", stdin->_fileno);
    printf("fd2=%d\n", stdout->_fileno);
    printf("fd3=%d\n", stderr->_fileno);
    return 0;
}

image-20230104173743020

其实文件描述符的0、1、2被标准输入、标准输出和标准错误给占用了。

一个系统中,会打开非常非常多的文件,这些文件OS也是需要进行管理的,而管理的方法就是先描述再组织,将所有文件共有的属性描述成结构体struct file __rcu,而在进程的PCB里有一个指针struct files_struct *files 指向结构体struct files_struct,该结构体中有一个指针数组struct file __rcu * fd_array[NR_OPEN_DEFAULT];属于当前进程的所有文件的结构体指针都在该数组中,而该数组的下表就是文件描述符fd!

image-20230104181210078

stdin、stdout、stderr默认占了该数组的前三个位置,而后面打开的文件则会依次填入该数组,所以fd会以0、1、2、3、4……的方式给出。

文件描述符的分配规则

我们每次用open打开文件,总是会用close来关闭文件。open会给我们返回fd,也就是新打开的文件的结构体指针已经填入到了那个数组中,那么close来关闭文件也就是将该数组对应下标的指针给释放。那么stdin、stdout、stderr是否可以关闭呢?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE_NAME(n) "tag.txt"#n
int main()
{
    close(0);
    close(2);
    int fd1 = open(FILE_NAME(1), O_RDWR | O_CREAT | O_TRUNC, 0666);
    int fd2 = open(FILE_NAME(2), O_RDWR | O_CREAT | O_TRUNC, 0666);
    printf("fd1=%d\n", fd1);
    printf("fd2=%d\n", fd2);
    close(fd1);
    close(fd2);
    return 0;
}

image-20230104181951228

可以发现,当关闭了stdin、stderr对应的0和2后,我们新打开的文件分配的fd就变成了0和2,这也就说明了,fd的分配规则是从数组的0下标开始依次遍历,该位置上无文件则分配,有文件则跳过。

重定向的原理

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE_NAME(n) "tag.txt"#n
int main()
{
    close(1);
    int fd = open(FILE_NAME(1), O_WRONLY | O_CREAT | O_TRUNC, 0666);
    const char *s = "hello world!\n";
    write(1, s, strlen(s));
    close(fd);
    return 0;
}

image-20230104183516508

该程序如果未关闭文件描述符1的话,也就是未关闭stdout的话,正常是应该往显示器上输出hello world!,但是关闭之后,hello world!就写入到了文件tag.txt1中!

原因是:关闭文件描述符1也就是将那个数组下标为1的指针关闭,而此时又open了一个文件,所以该文件分配的文件描述符就是1,但是write函数就是往1里面写,也就将本来写入到stdout里的内容写入到了tag.txt1中。

所以,输出重定向的原理就是关闭1,再让新打开的文件的文件描述符分配上1;

输入重定向的原理就是关闭0,再让新打开的文件的文件描述符分配上0。

但是这样的操作太过繁琐,于是就有一个函数,一步到位:dup2。

#include <unistd.h>

int dup2(int oldfd, int newfd);

参数:
    newfd是oldfd的拷贝,也就是将newfd位置上的指针变为oldfd位置上的指针。
返回值:
    成功返回newfd
    失败返回-1

于是上面的函数就可以改为:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE_NAME(n) "tag.txt"#n
int main()
{
    int fd = open(FILE_NAME(1), O_WRONLY | O_CREAT | O_TRUNC, 0666);
    dup2(fd, 1);
    const char *s = "hello world!\n";
    write(1, s, strlen(s));
    close(fd);
    return 0;
}

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

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

相关文章

AppScan 是一款web安全扫描工具

AppScan介绍&#xff1a; AppScan是IBM的一款web安全扫描工具&#xff0c;主要适用于Windows系统。该软件内置强大的扫描引擎&#xff0c;可以测试和评估Web服务和应用程序的风险检查&#xff0c;根据网站入口自动对网页链接进行安全扫描&#xff0c;扫描之后会提供扫描报告和修…

2022 中国白帽人才能力与发展状况调研报告

声明 本文是学习2022中国白帽人才能力与发展状况调研报告. 下载地址 http://github5.com/view/55039而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 中国白帽人才能力与发展状况调研报告 2022.11 补天漏洞响应平台 奇安信行业安全研究中心 概述 …

元数据管理Datahub架构讲解

目录1. 概述2. 各模块介绍2.1 Metadata Store​2.2 Metadata Models2.3 Ingestion Framework2.4 GraphQL API2.5 User Interface3. Ingestion Framework的架构3.1 Metadata Change Event(MCE)3.2 Pull-based Integration3.3 Push-based Integration​3.4 Applier(mce-consumer)…

CAN学习资料汇总

先简单做个汇总&#xff0c;细节稍后再逐一写出来 CAN&#xff0c;CANOpen与OSI七层协议的关系 CAN CAN是控制器局域网络(Controller Area Network, CAN)的简称&#xff0c;是由以研发和生产汽车电子产品著称的德国BOSCH公司开发的&#xff0c;并最终成为国际标准&#xff08;…

暴涨1000w播放!B站爆款恰饭,弹幕刷屏“上链接”

12月27日&#xff0c;在2022 AD TALK bilibili营销伙伴大会上李旎谈到B站第一批主流用户大多数已经进入28-30岁的阶段&#xff0c;近亿的用户开始对内容的需求有了新的变化。除此之外&#xff0c;B站近几年的用户增长速度亮眼&#xff0c;2022年第三季财报显示平台日活用户已经…

动态内存管理(15)

目录 1、动态内存函数的介绍 1、malloc 2、free 3、calloc 4、realloc 2、常见的动态内存错误 3、C/C程序的内存开辟 4、柔性数组 1、定义 2、柔性数组的特点&#xff1a; 3、使用 1、动态内存函数的介绍 1、malloc malloc 向堆区申请一块连续可用的空间&#xff…

【高阶数据结构】AVL树(动图详解)

&#x1f308;欢迎来到数据结构专栏~~AVL树详解 (꒪ꇴ꒪(꒪ꇴ꒪ )&#x1f423;,我是Scort目前状态&#xff1a;大三非科班啃C中&#x1f30d;博客主页&#xff1a;张小姐的猫~江湖背景快上车&#x1f698;&#xff0c;握好方向盘跟我有一起打天下嘞&#xff01;送给自己的一句…

5G NR 标准 第12章 波束管理

第12章 波束管理 第 11 章一般性地讨论了多天线传输&#xff0c;然后重点介绍了多天线预编码。 讨论多天线预编码的一般假设是可以对不同的天线元件进行详细控制&#xff0c;包括相位调整和幅度缩放。 实际上&#xff0c;这需要在数模转换之前在数字域中执行发射机侧的多天线…

path 路径模块

1、什么是 path 路径模块 path 模块是 Node.js 官方提供的、用来处理路径的模块。它提供了一系列的方法和属性&#xff0c;用来满足用户对路径的处理需求。 方法名 说明 path.join() 用来将多个路径片段拼接成一个完整的路径字符串 path.basename() 用来从路径字符串中…

Kubernetes(4)- 数据存储

第八章 数据存储 ​ 在前面已经提到&#xff0c;容器的生命周期可能很短&#xff0c;会被频繁地创建和销毁。那么容器在销毁时&#xff0c;保存在容器中的数据也会被清除。这种结果对用户来说&#xff0c;在某些情况下是不乐意看到的。为了持久化保存容器的数据&#xff0c;ku…

【Java 数据结构】常见排序算法(下)

目录 1、上期回顾 2、冒泡排序 3、快速排序 3.1 理解快速排序的二叉树结构 3.2 Hoare 法 3.3 三数取中 3.4 小区间优化 3.5 挖坑法 3.6 前后指针法 3.7 注意事项 4、归并排序 1、上期回顾 上期我们主要介绍了排序的基本认识&#xff0c;以及四个排序&#xff0c;分…

【Nginx】静态资源部署(上)

文章目录Nginx静态资源概述Nginx静态资源的配置指令listen指令server_name指令匹配执行顺序location指令设置请求资源的目录root / aliasindex指令error_page指令静态资源优化配置语法Nginx静态资源压缩Gzip模块配置指令Gzip压缩功能的实例配置Gzip和sendfile共存问题gzip_stat…

116.(leaflet之家)leaflet空间判断-点与多边形的空间关系

听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行完整代码包,运行如有问题,可“私信”博主。 效果如下所示: 下面献上完整代码,代码重要位置会做相应解释 <!DOCTYPE html> <html>

电脑怎么录屏?这三个简单的电脑录屏方法,不要错过

在现在的社会&#xff0c;电脑已经是我们日常工作中不可或缺的一部分。我们通常都要在电脑进行各种各样的操作&#xff0c;所以在向其他人展示电脑操作的时候&#xff0c;我们通常都会把整个操作过程录屏下来。那电脑怎么录屏&#xff0c;今天小编就给大家分享三个简单的电脑录…

分布式数据库中间件Sharding-JDBC介绍

前文中介绍了分布式数据库中间件Mycat的一些特性&#xff0c;作为对比本文简要介绍Sharding-JDBC的一些特性以及分片的实现原理&#xff0c;进行对比分析以了解。 1、ShardingSphere介绍 ShardingSphere是一套开源的分布式数据库中间件解决方案&#xff0c;目前由Sharding-JDB…

15个有用的JavaScript技巧

今天这篇文章&#xff0c;是我从网络上整理的一些常见的 JavaScript Tips。我在我的项目中使用了所有这些实用技巧&#xff0c;今天我想把它们分享给你&#xff0c;希望也能够帮助到你。 1.数字分隔符 为了提高数字的可读性&#xff0c;可以使用下划线作为分隔符。 const la…

pytorch学习记录

pytorch学习记录0.引言1.numpy 简单入门1.1.常用基础1.2.numpy其他学习2.pytorch 简单入门2.1.Pytorch的基本组成元素2.2.Pytorch构建神经网络2.3.进阶部分2.4.其他参考资料0.引言 一步一步积累吧。 一篇不错的博客 1.numpy 简单入门 1.1.常用基础 numpy中文手册 numpy官方…

【李宏毅】HW12

HW12一、作业描述1、Policy Gradient2、Actor-Critic二、实验1、simple2、medium3、strong三、代码一、作业描述 在这个HW中&#xff0c;你可以自己实现一些深度强化学习方法&#xff1a; 1、策略梯度Policy Gradient 2、Actor-Critic 这个HW的环境是OpenAI gym的月球着陆器。…

ubuntu | 重装ubuntu(VMware虚拟机环境)

前言 最近又把双系统的ubuntu搞崩了&#xff0c;在grub界面进不去&#xff0c;估计找不到启动项&#xff0c;被迫重装。 这次觉得还是windows系统好使&#xff0c;就用vmware虚拟机了。 一、安装vmware虚拟机 参考我之前的博客。 二、安装ubuntu 下载ubuntu18.04的iso文件…

15.2 浏览器中的进程

浏览器中的进程 start 上一篇文章&#xff0c;学习到了如何区分进程和线程。在这里再复习一下&#xff0c;进程类似于一个工厂&#xff0c;线程类似于工厂的工人&#xff0c;一个工厂可以有一个或多个工人。 1. 浏览器的进程 一个浏览器中有很多的进程&#xff0c;我以谷歌…