Linux——文件基础IO的文件描述符和重定向理解

news2024/11/17 5:37:10

 目录

  前言:

首先来回顾一下open函数,即在进程中同时打开多个文件:

Linux底层进程与文件的关系 :

2.1关闭stdin:

运行结果: 

​编辑由结果知:fd1指向文本文件cyq.txt,原本fd1是默认被fd_array[2]指向的,现在fd1却被fd_array[0]指向了。

2.2关闭stderr

 运行结果:

2.3关闭stdout :

查看该文件cyq.txt:

 运行结果:​编辑

总结:

2.4重定向函数dup2()的学习

2.4.1函数作用:

2.4.2.参数

2.4.3返回值问题: 

2.4.4案例——输出重定向:一般用于写内容到指定的地方

运行结果:

 运行结果:

        一次运行:

        二次运行:

        2.4.6输入重定向案例:一般用于从指定的地方读取内容。

 运行结果:​编辑


  前言:

        之前,我讲解了Linux下文件的特性、讲解了关于Linux关于文件的系统调用函数open、write、read、close函数,讲解了它们与C语言库中fopen、fclose、fgets、fputs等函数的区别,其实fopen、fclose、fgets、fputs这些函数是在Linux系统调用函数的底层原理下封装形成的优化C库函数。

 Linux文件基础IO的理解1   

接下来,我将继续深入讲解Linux中文件的一些底层知识。

首先来回顾一下open函数,即在进程中同时打开多个文件:

   #include<errno.h>
   #include<sys/types.h>
   #include<sys/stat.h>
   #include<fcntl.h>
   #include<stdlib.h>
   #include<unistd.h>
   
   #define Filename1 "big1.txt"
  #define Filename2 "big2.txt"
  #define Filename3 "big3.txt"
  #define Filename4 "big4.txt"
  
  
  int main(){
        //同时打开多个文件
      int f1=open(Filename1,O_CREAT| O_WRONLY |O_TRUNC,0664);
      int f2=open(Filename2,O_CREAT| O_WRONLY |O_TRUNC,0664);
      int f3=open(Filename3,O_CREAT| O_WRONLY |O_TRUNC,0664);
      int f4=open(Filename4,O_CREAT| O_WRONLY |O_TRUNC,0664);
 
    //打印这些文件描述符的值
      printf("f1: %d\n",f1);
      printf("f2: %d\n",f2);
      printf("f3: %d\n",f3);
      printf("f4: %d\n",f4);                                                          
  
      close(f1);
      close(f2);
      close(f3);
      close(f4);
      return 0;
    }

运行结果:

        从结果中,我们看到这4个文件描述符的值分别是3-6,那么有人会想为什么是从3开始往后呢?为什么不是从1开始,或者从0开始呢?

        其实这就要说到标准流了! 我们在学C语言的过程中,相必从书中就已经了解到了多种流,它们分别是标准输入流stdin、标准输出流stdout、标准错误流stderr,这三种流分别对应键盘、显示器、显示器。stdin标准输入流就是通过键盘向计算机中写入数据的;stdout标准输出流是通过读取计算机中的正常运行得出的数据到显示器中,方便查看的;stderr标准错误流是读取计算机在运行过程中产生的错误信息到显示器中。

        Linux下一切皆文件,那么这三个流也不例外,它们也是文件,而且它们三个是处于一直被打开的状态,因为每个进程的运行都离不开它们几个,于是stdin、stdout、stderr所代表的也是文件描述符,每个进程都有一个文件描述符表,它相当于一个数组,专门用于存放该进程打开的各个文件。这三个文件描述符就占据了数组的榜一榜二榜三位置,导致后面被进程打开的文件只能从数组索引位置3开始!

求证一下:

 结果运行:

Linux底层进程与文件的关系 :

 

       每个进程都有一个task_struct的结构体,该结构体中存放着该进程的各个属性,既然一个进程打开了多个文件,那么这几个被该进程打开的文件就必须要被管理和维护,所以task_struct的进程属性中就又多了一个属性:file_struct* files指针,该指针指向记录着该进程所打开的所有文件的结构体:files_struct。

        Linux下一切皆文件的本质和 “先描述,再组织 ” 的核心管理理念,使得我们能够清楚的了解到这个结构体中包含着每个被打开文件的各个属性,其中的成员file* fd_array[ ]指针数组便是存放着文件的各个文件描述符。所以本质上,文件描述符就是指针数组的下标。所以进程和被打开文件的关系就是通过文件描述符表files_struct映射的。

二.

        既然stdin,stdout,stderr这三个流的类型也是文件,那么意味着它们可以被close,假如我们关闭了file* fd_array[ ]数组中的前3个标准流,会发生什么?

2.1关闭stdin:

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

int main(){
    close(0);     //关闭stdin流
    int fd1=open("cyq.txt",O CREAT O WRONLY O TRUNC,0666);
        if(fd1<0){
            perror("open");
            exit(-1);
            }
    printf("open fd1: %d\n", fd1);
    close(fd1);
    return 0;
    }

        由上面代码可知:关闭了文件描述符表中数组的首元素,意味着fd_array[0]不再指向stdin——键盘,那么该数组的首元素为空。 

运行结果: 



由结果知:fd1指向文本文件cyq.txt,原本fd1是默认被fd_array[2]指向的,现在fd1却被fd_array[0]指向了。

 


2.2关闭stderr

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

int main(){
    close(2);     //关闭stderr流
    int fd1=open("cyq.txt",O CREAT O WRONLY O TRUNC,0666);
        if(fd1<0){
            perror("open");
            exit(-1);
            }
    printf("open fd1: %d\n", fd1);
    close(fd1);
    return 0;
    }

 运行结果:

 

        根据这个案例的结果,我们可以证出了一些东西:假如当fd_array[ ]数组中有某个指向文件的描述符下标2被关闭了,变成空闲的了,那么后面的文件描述符指向新的文件时,就会由描述符下标2去指向新文件。

   

 

       一般来说,不关闭这3个默认的流时,fd默认是在指针数组fd_array[ ]的下标3处开始分配,一旦关闭了这3个的某一个,那么fd就会被分配到数组中空闲的那一个下标位置处。


2.3关闭stdout :

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(){

    close(1);     //关闭stdout流
    int fd1=open("cyq.txt",O CREAT O WRONLY O TRUNC,0666);
        if(fd1<0){
            perror("open");
            exit(-1);
            }

    printf("open fd1: %d\n", fd1);
    fprintf(stdout, "open fd:%d\n",fd1);

    close(fd1);
    return 0;
    }

运行结果: 

         从结果可知:我们发现执行该程序时,没有给出任何结果,什么都没有显示!这是为什么?其实很好解释。因为我们关闭了stdout输出流,那么printf函数的内容也就不再从显示屏中输出了,且czq.txt的文件描述符放在了fd_array[1]中,所以printf的输出内容都打印到了cyq.txt文件中去,所以当我们执行该程序时才什么都不会显示。

查看该文件cyq.txt:

 

使用cat指令查看该文件的内容时,也是什么都没有显示出来,这又是为什么?

        其实,printf的内容先是传输到了系统的缓冲区中,需要使用fflush(stdout);才会强制刷新缓冲区,将内容传到文件中! 

 

 

 运行结果:

 

总结:

 fd的分配规则:

        从小到大,自顶向下的,且该处数组下标处没有被使用的位置为其分配fd地址。

         这就是所谓的“重定向”!改变数据的流向,让原本写入A文件的数据不再写入A中,而是写入指定的B文件中。而上面的几个案例是:让原本写入流中的数据,写入指定的文本文件中。

 

        但是这几个案例的方式有些低效率!是需要先关闭数组的某个下标处的元素,然后重定向新的文件描述符到该下标处。

2.4重定向函数dup2()的学习

dup2();

2.4.1函数作用:

        该函数的作用就是直接改变指针数组中下标处元素的数据流向。 

2.4.2.参数

        最常用的是dup2函数接口,参数oldfd是当前新建文件的文件描述符下标处(源目的地),newfd是要拷贝到的指定的文件数组下标处(最终目的地)。

2.4.3返回值问题: 

 

        若调用成功,则成功改变数组的下标处数据指向;若失败,则返回-1

 

2.4.4案例——输出重定向:一般用于写内容到指定的地方

#include<std1o.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<fcntl.h>
#include<errno.h>
#include<unistd.h>
#include<stdlib.h>
int main(){
	int fd2=open("cyq.txt",0_CREAT  O WRONLY  O TRUNC ,0666);
	if(fd2<0){
	perror("open file");
	exit(-1);
	}
	//方法2:
	dup2(fd2,1);                     //将old fd,拷贝到new fd中

	printf("open fd2:%d\n",fd2);	    //打印到文件中了
	fprintf(stdout,"open fd:%d\n",fd2);
	int cnt=3;
	while(cnt){
		printf("hello my girl,%d\n",cnt--);
		}	

	//因为内容在缓冲区中,必须强制刷新出来
	fflush(stdout);
	close(fd2);
	return 0;
   	}

运行结果:

       结果解析:虽然dup2函数改变了fd_array[1]的数据指向到了文本文件中,但fd_array[3]的数组元素也是指向该文本文件的,所以fd2的值为3是正确的。

        使用dup2(fd,1); 函数造成的结果和使用close(1); 造成的结果是一样的,都是切断了从显示屏中输出的内容转而到了指定文件中。

 


2.4.5案例——追加重定向(是输出重定向的一种):

        该案例在输出重定向的基础下只改变了open函数的O_APPEND属性。其他不变。

 运行结果:

        一次运行:

        二次运行:

 


2.4.6输入重定向案例:一般用于从指定的地方读取内容。

      int fd2=open("cyq.txt",O_RDONLY);                       
      if(fd2<0){
          perror("open file");
          exit(-1);
      }
  
      dup2(fd2,0);    //输入重定向
  
      char str[64];
      while(1){
          printf(">: ");
          if(fgets(str,sizeof(str),stdin)==NULL){
            break;
             }
          printf("%s",str);
      }

解析:输入重定向是从改变数组元素指向的stdin流,转而指向文本文件,之后我们便可以读取该文件的内容。

 

 运行结果:

 

        以上就是Linux系统中重定向的含义和两种做法。

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

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

相关文章

机器学习多步时间序列预测解决方案

近年来&#xff0c;随着机器学习与深度学习的发展机器学习平台的成熟&#xff0c;数据科学家们不再需要关心底层的基础设施及构建复杂的训练与推理环境&#xff0c;从而可以把主要的时间与精力放在数据与算法本身。在机器学习变得更容易的今天&#xff0c;越来越多的传统行业已…

HAL库——STM32CubeMX中断相关配置(中断反转LED状态)

STM32CubeMX中断相关配置 文章目录 STM32CubeMX中断相关配置1. 选择你要用的芯片(双击打开)2. 设置串口写入3. 配置时钟树&#xff0c;外部时钟为系统时钟&#xff08;PLL倍频时钟&#xff09;4. 查看原理图&#xff0c;找到可以中断控制的器件&#xff0c;或者外接小灯来达到中…

中国版chatGPT【文心一言】

文心一言是一款基于人工智能技术的中文自然语言处理工具&#xff0c;它可以用于文本生成、情感分析、关键词提取等多种应用场景。相比于GPT等其他自然语言处理模型&#xff0c;文心一言有着更多的优势。 首先&#xff0c;文心一言具有更高的准确率和可靠性。它采用了最新的深度…

Redis之RDB和AOF持久化原理解析

Redis之RDB和AOF持久化原理解析 一 RDB持久化原理及缺点 说明&#xff1a; 主进程是没法读取物理内存的&#xff0c;所以会在主进程有一个页表来读取物理内存中的数据子进程共享主进程的数据&#xff0c;会复制页表&#xff0c;写入磁盘中写操作是会拷贝一份在进行写操作 二…

【MySql】MySql事务隔离级别与一致性

文章目录 理解隔离性隔离级别查看与设置隔离性读未提交Read Uncommitted读提交Read Committed可重复读 Repeatable Read串行化serializable一致性(Consistency) 理解隔离性 MySQL服务可能会同时被多个客户端进程(线程)访问&#xff0c;访问的方式以事务方式进行 一个事务可能由…

计算机网络——自顶向下方法(第四章学习记录)

本章学习网络层&#xff1a;数据平面 前一章中我们学习了运输层依赖于网络层的主机到主机的通信服务&#xff0c;提供了各种形式的进程到进程的通信。在本章中我们将看到与运输层和应用层不同的是&#xff0c;在网络中的每一台主机和路由器中都有一个网络层部分。 网络层能够…

【FreeRTOS】FreeRTOS学习笔记 ---- 堆和栈,第1个FreeRTOS程序,创建任务函数及任务管理

&#x1f340;作者&#xff1a;阿润菜菜 目录 一、通过故事介绍FreeRTOS1.什么是FreeRTOS&#xff1f;2.FreeRTOS能做什么&#xff1f; 二、如何使用FreeRTOS&#xff1f; --- 第1个FreeRTOS程序三、FreeRTOS的堆和栈1.堆和栈的概念2.堆和栈的分配方式3.堆和栈的溢出检测 四、创…

【创建一个螺旋状的相机轨迹并可视化该轨迹以及每个点的姿态】

文章目录 焦点的作用static const Eigen::Vector3d b_cam_z(0, 0, 1);static const Eigen::Matrix3d R_w_base = (Eigen::Matrix3d() << 0, 0, -1, 1, 0, 0, 0, -1, 0).finished()import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import …

canvas详解03-绘制图像和视频

canvas 更有意思的一项特性就是图像操作能力。可以用于动态的图像合成或者作为图形的背景,以及游戏界面(Sprites)等等。浏览器支持的任意格式的外部图片都可以使用,比如 PNG、GIF 或者 JPEG。你甚至可以将同一个页面中其他 canvas 元素生成的图片作为图片源。 引入图像到 …

canvas详解04-绘制文字

绘制文本 canvas 提供了两种方法来渲染文本: fillText(text, x, y [, maxWidth]) 在指定的 (x,y) 位置填充指定的文本,绘制的最大宽度是可选的。 strokeText(text, x, y [, maxWidth]) 在指定的 (x,y) 位置绘制文本边框,绘制的最大宽度是可选的。 #一个填充文本的示例…

Vue电商项目--购物车操作

购物车动态展示数据 但是这个数据的格式不完美 一层套一层 重新对vuex进行存储处理 这里接口写错 这一块&#xff0c;我们通过计算属性加工一下&#xff0c;重新渲染到页面上 在这里我们考虑一个问题&#xff0c;那就是将计算出来的总值计算到页面上 这里还有一个问题&#x…

qt读写xml文件

Qt使用XML模块&#xff0c;在.pro文件中添加 QT xml Qt 提供了三种读写 XML 文档的方法&#xff1a; QXmlStreamReader/QXmlStreamWriter&#xff1a; 一种快速的基于流的方式访问良格式 XML 文档&#xff0c;特别适合于实现一次解析器&#xff08;所谓“一次解析器”&…

前端vue入门(纯代码)13

【13.Vue的消息订阅与发布】 备注&#xff1a;全局事件总线用的更多些&#xff0c;消息订阅与发布只需了解即可。【注意点1】&#xff1a;由于“消息订阅与发布”可依赖的第三方库太多了&#xff0c;这里使用pubsub-js 问题&#xff1a;“全局事件总线”和“消息订阅与发布”都…

看完这篇 教你玩转渗透测试靶机vulnhub—Emplre: Breakout

Vulnhub靶机Emplre: Breakout渗透测试详解 Vulnhub靶机介绍&#xff1a;Vulnhub靶机下载&#xff1a;Vulnhub靶机安装&#xff1a;Vulnhub靶机漏洞详解&#xff1a;①&#xff1a;信息收集&#xff1a;②&#xff1a;登入后台&#xff1a;③&#xff1a;GetShell&#xff1a;④…

oracle操作xml格式数据

新建一张用来测试的表 -- Create table create table XMLTEST (id NUMBER,content VARCHAR2(4000) );往表中插入数据 insert into XMLTEST (id, content) values (1, <root><app><id>1</id><name>张三</name><age>18</age…

《网络安全0-100》经典访问控制策略

1经典访问控制策略 1.1自主访问控制 允许用户自己对客体将已有的权限赋予给其他主体&#xff0c;也可以撤销自己赋予给其他主体的权限。 矩阵结构分为三个主要的表&#xff1a; 访问控制矩阵 访问控制列表 权能表 矩阵的局限性&#xff1a; 大小为主体数量客体数量&…

【FPGA】Verilog:时序电路设计 | 自循环移位寄存器 | 环形计数 | 扭环计数 | 约翰逊计数器

前言&#xff1a;本章内容主要是演示Vivado下利用Verilog语言进行电路设计、仿真、综合和下载 示例&#xff1a;计数器 ​​ 功能特性&#xff1a; 采用 Xilinx Artix-7 XC7A35T芯片 配置方式&#xff1a;USB-JTAG/SPI Flash 高达100MHz 的内部时钟速度 存储器&#xff1a;2Mb…

Bresenham直线算法

文章目录 1.Bresenham直线算法1.1 算法流程1.2 Bresenham算法实现1.3matlab中应用1.4 算法优势1.5 对比以往方法的改进和优化1.6 算法改进和缺陷 2.国内外研究现状3.个人感想及算法改进 1.Bresenham直线算法 Bresenham直线算法是一种用于将两点之间的线段绘制在屏幕上的算法。…

什么是MLOps?为什么要使用MLOps进行机器学习实践

随着数字化和计算能力的发展&#xff0c;机器学习&#xff08;Machine Learning&#xff09;技术在提高企业生产力方面所涌现的潜力越来越被大家所重视&#xff0c;然而很多机器学习的模型及应用在实际的生产环境并未达到预期&#xff0c;大量的ML项目被证明是失败的。从机器学…

【Red Hat7.9安装Oracle11g】---调用图形化界面的几种方式

【Red Hat7.9安装Oracle11g】---调用图形化界面的几种方式 &#x1f53b; 一、续上一篇[【Red Hat 7.9---详细安装Oracle 11g---图形化界面方式】](https://blog.csdn.net/qq_41840843/article/details/131198718?spm1001.2014.3001.5501)⛳ 1.1 前言⛳ 1.2 方式一、使用Xmana…