当前位置: 首页 > article >正文

【Linux】Linux 操作系统 - 18 , 重谈文件(二) ~ 文件描述符和重定向原理 , 手把手带你彻底理解 !!!

文章目录

  • 文件描述符
    • 一 、Linux 系统对文件的管理(要知道)
    • 二 、什么是文件描述符 fd ?
    • 三 、再探文件被管理过程(重要)
    • 四 、文件描述符 0 、1、2
      • 1. 文件描述符的分配原则
      • 2. 提前认识三个默认打开的文件
  • 重定向原理(重要)
    • 一 、重定向现象
    • 二 、深入剖析重定向现象(重要)
      • 1 . 重定向的本质是什么 ???
      • 2 . 各种重定向实现原理
        • 2.1 dup2 系统调用(理解 , 掌握)
        • 2.2 输出重定向
        • 2.3 输入重定向
        • 2.4 追加重定向
      • 3 . 重定向的原理(面试考)
  • 正式谈三个默认打开的文件
    • 一 、stderr
    • 二 、重定向的使用(重要)(重点讲解标准错误)
      • 1 . 重定向的完整写法
      • 2 . 将错误信息和正常信息隔离(重要)
      • 3 . 将错误信息和正常信息打印在一起
  • 总结


文件描述符


  篇章一中笔者提到了文件描述符的概念 , 本节就详细对其进行介绍 !

  对以下系统调用中会涉及文件描述符的概念 :

write(int fd, ....) - write to a file descriptor - 文件描述符read( int fd, .... ) - read from a file descriptor - 文件描述符 close(int fd) - close a file descriptor  - 文件描述符 

一 、Linux 系统对文件的管理(要知道)


  之前第一篇章提到操作文件 , 就是进程操作文件 ! 那么考虑一个问题 ???
  在系统中一切皆文件 , 那么文件多了 , 要不要被管理呢 ???

答 : 文件要被管理 ! 那怎么管理呢 ??? 先描述 , 再组织 !
  既然要描述 , 那么就会有数据结构吧 !

以下便是具体描述 :

在这里插入图片描述


二 、什么是文件描述符 fd ?


  上面也提到了文件被管理是通过文件描述符表管理的 , 那这个文件描述符表是个什么呢 ???

  • 文件描述符表是一个数组 !
  • 文件描述符的本质就是数组的下标 ! , 即 : fd 的本质就是数组的下标 !
  • flies_struct 这个数据结构中包含一个结构体指针数组 , 这个数组就是文件描述符表 !
  • 内核源代码中的 files_struct
struct files_struct {
....
....
....
....
....
....//其中包含一个结构体指针数组
struct file __rcu * fd_array[NR_OPEN_DEFAULT];  // 文件描述符表 
};

在这里插入图片描述

  • 每打开一个文件 , 就会有一个文件描述符来描述该文件 , 即 : 就会有一个指针指向该文件 !

在这里插入图片描述


三 、再探文件被管理过程(重要)

在这里插入图片描述


  总结文件被管理的过程(面试可能考) :

在这里插入图片描述
记住一个图 :
在这里插入图片描述


  所以 , 还可以得出一个结论 :

对文件内容的任何操作 , 必须先把文件的内容加载到内核对应的文件缓冲区内 , 避免频繁 I/O , 提高效率 !


四 、文件描述符 0 、1、2


  有了以上的理解 , 现在便好介绍 0 , 1 , 2 了 .

  笔者之前提过 , 系统会为每个文件分配一个文件描述符 , 每一个描述符对应一个文件 !

1. 文件描述符的分配原则


  因为文件描述符是数组的下标 , 所以会有以下原则 :

  • 找到 files_struct 数组中没有被使用的最小的下标 , 作为当前文件的描述符 ! 注意 : 是最小的下标 !!!
/ fd 分配原则#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{//close(0);//int fd = open("log.txt" , O_CREAT | O_WRONLY | O_TRUNC , 0666);	//printf("fd : %d\n",fd);   **********  打印 0//close(2);//int fd = open("log.txt" , O_CREAT | O_WRONLY | O_TRUNC , 0666);	//printf("fd : %d\n",fd);   **********  打印 2int fd = open("log.txt" , O_CREAT | O_WRONLY | O_TRUNC , 0666);	printf("fd : %d\n",fd);    **********  打印 3return 0;
}

2. 提前认识三个默认打开的文件


  之前笔者也讲过 , 系统会自动打开三个默认文件 !

  这三个默认文件 , 分别对应 :

  • 键盘 --- 文件描述符 0
  • 显示器 --- 文件描述符 1
  • 显示器 --- 文件描述符 2

观察一个现象

#include <stdio.h>
#include <unistd.h>int main()
{	printf("进入第一次输入 :\n");int a = 0;scanf("%d",&a);printf("%d\n",a);close(0);printf("进入第二次输入 :\n");int b = 10;scanf("%d",&b);printf("%d\n",b);return 0;
}

在这里插入图片描述

所以 , 上面完全可以印证文件描述符 0 对应的就是键盘文件(C语言 — stdin ) !


  其余 , 学者可以自行验证 !


重定向原理(重要)


  我们之前经常用 > , 这个就是重定向 , 那么其原理真的了解吗 ?

  重定向分类 :

  • 输出重定向
  • 输入重定向
  • 追加重定向

一 、重定向现象


  给出以下代码 : 你会发现什么现象 ??

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{	// 演示输出重定向现象 , 即 1 -- 对于 stdout// 关闭 1 对于的文件 , 即 : 关闭显示器文件close(1);int fd = open("redir.txt" , O_CREAT|O_WRONLY | O_APPEND , 0666);//打印 , 默认打印到显示器 , 即 : 只会向 1 设备文件中打印 ; 但当关闭 stdout 文件后 , 会打印到 redir.txt 中 printf("I am file , defalut printf to stdout\n");return 0;
}*************** 关闭 1 , 不会在显示器上打印了 , 而是打印在了 redir.txt 中 **************

二 、深入剖析重定向现象(重要)


  对以上的现象 , 为什么关闭了 1 对应的文件 , 不会打印在显示器 , 却打印到了其它文件了呢 ??

在这里插入图片描述

在这里插入图片描述

1 . 重定向的本质是什么 ???


  通过以上讲解的 , 现象的剖析 , 便可得到 :

重定向的本质 : 改变文件描述符的指针指向 !!!!


2 . 各种重定向实现原理

2.1 dup2 系统调用(理解 , 掌握)


  这里介绍一个系统调用 :

 dup, dup2, dup3 - duplicate a file descriptor , 复制一个文件描述符int dup2(int oldfd, int newfd);描述 : 使用新的 newfd 这个文件描述符 , 形成 oldfd 这个文件描述符的拷贝 !简单理解 : 它会将 oldfd 所指向的文件复制到 newfd,新的会指向旧的内容 !

在这里插入图片描述


2.2 输出重定向
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>// ls > xxx , 往 xxx 写入
int main()
{int fd = open("dir.txt" , O_CREAT | O_WRONLY | O_TRUNC , 0666);// old , new , 新的指向旧的 dup2(fd , 1); // 1 本身指向的是显示器文件 , 在 C语言中为 : stdout , 现在指向 fd printf("Hello newfile\n");const char* str = "dir success!\n";write(1 , str , strlen(str));close(fd); return 0;
}

在这里插入图片描述

  通过我们手动编写的原理 , 这里就可以知道为什么 > 符号可以用来新建文件了 . 因为 open 文件时会 O_CREAT , 文件不存在 , 就创建 !


2.3 输入重定向
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>// ls < xxx , 从 xxx 读取内容
int main()
{int fd = open("dir.txt" , O_RDONLY);dup2(fd , 0); // 0 原来指向键盘文件 , 默认从键盘上读取内容, 现在指向 dir.txt char buff[200];memset(buff , 0 , sizeof(buff));//读 fd 文件	while(1)	{	// 从 0 指向的设备文件中读 ssize_t red = read(0 , buff , sizeof(buff)-1);	// 不读 \0 l if(red > 0){buff[red] = 0;printf("%s\n",buff);	}		if(red == 0 ){break;}}return 0;
}


  以上就做到了 , 本来是从键盘读取 , 现在是在新的文件中读取 !


2.4 追加重定向


  这里实现一个追加输出重定向 .

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>/ 追加输出重定向
// ls >> xxx , 往 xxx 写入
int main()
{int fd = open("dir.txt" , O_CREAT | O_WRONLY | O_APPEND , 0666);// old , new , 新的指向旧的 dup2(fd , 1); // 1 本身指向的是显示器文件 , 在 C语言中为 : stdout , 现在指向 fd printf("Hello newfile\n");const char* str = "dir success!\n";write(1 , str , strlen(str));close(fd); return 0;
}


  这里就可以发现 , 可以追加写入到 dir.txt 这个文件中了 !


3 . 重定向的原理(面试考)


  通过以上各个重定向的编写 , 我们可以发现一个共同点 , 那就是每个重定向都是 : 打开方式 + dup2 来进行的 !

面试总结 :
在这里插入图片描述


正式谈三个默认打开的文件


  在之前的篇章笔者一直在铺垫三个默认打开的文件 , 但是没有正式讲解 , 这里便给出 !

在这里插入图片描述

  通过文件描述符的学习 , 我们这里就可以知道了 , 以下 :

  • 键盘文件对应文件描述符 0
  • 显示器文件(stdout) 对应文件描述符 1
  • 显示器文件(stderr) 对应文件描述符 2


  所以 , 默认情况下 , 0 , 1 , 2 是被占用的 , 我们之后打开的文件默认是从文件描述符 3 开始的 !


  那对于 stderr 这个显示器文件一直没有介绍 , 以下对其详细解释 !

一 、stderr


  stderr 是显示器文件 , 那么和 stdout 的显示器文件到底有什么区别呢 ??

  • stdout 是显示器文件 , 一般显示我们正常的程序信息 .
  • stderr 是显示器文件 , 一般显示我们错误的程序信息 .
  • 二者本质都是指向同一个硬件 , 但是二者所对应的文件描述符不同 !

描述符不同 , 目的是让我们用重定向进行正常信息和错误信息的隔离 !!!


二 、重定向的使用(重要)(重点讲解标准错误)

1 . 重定向的完整写法

假如这里需要把可执行程序 a.out 里面的内容重定向到文件 log.txt 中 . 我们平时写的 x > log.txt 的完整写法是 :a.out 1 > log.txt // 解释
意思就是 : 把 a.out 里面的 1 文件描述符对应的内容重定向到 log.txt 中 .


  这里看一段代码理解 :

#include <iostream>
#include <cstdio>int main()
{	//输出 , 默认是在显示器输出 , 1 对应的文件 ,stdout(C语言) , cout(C++)printf("Hello C!\n");std::cout << "Hello C++!" << std::endl;//输出错误信息 , 默认也是在显示器输出 , 1 对应文件 ,stderr , cerrconst char* str = "Hello C err!\n";fprintf(stderr , "%s" , str);std::cerr << "Hello C++ Err!" << std::endl;return 0;
}
  • 编译后执行 , 不进行重定向 !

在这里插入图片描述

  • 编译后执行 , 进行重定向 ! (不用完整写法)
    在这里插入图片描述

  • 编译后执行 , 进行重定向 ! (用完整写法)
    在这里插入图片描述


  所以 , 重定向时 , 可以指定文件描述符 , 这是最完整的写法 !!!


2 . 将错误信息和正常信息隔离(重要)


  重定向完整写法的最大应用就是将二者进行隔离 , 这样方便程序员去查看错误信息 !

./mydir 1>log.normal  2>log.err

在这里插入图片描述

  所以 , 要清楚

  • 文件描述符 1 - > 对应的是显示器文件 , stdout / cout , 即 : 一般打印我们程序程序信息 !
  • 文件描述符 2 - > 对于的是显示器文件 , stderr / cerr , 即 : 一般打印我们程序的错误信息 !
  • 通过 重定向 可以将不同的信息打印到不同文件 ! 这是常做的 !!!!

3 . 将错误信息和正常信息打印在一起


  当我们需要将二者信息同时都重定向到一个文件该怎么做 ???
  可能有的人会这样做 :

.mydir  1>log.normal  2>log.normal  // 这个有问题吗 ????????

  上面的做法是有问题的 , 因为 > 底层是用的系统调用 open 呀 , open 的打开文件方式是用了 O_TRUNC 的 , 也就意味着会先清空在写入 , 所以 , 非常错误 !!!

  • 正确做法
./mydir 1>log.normal 2>&1 


2 > &1 的意思就是 : 再把 2 里面的内容添加到 1 里面 , 不清空 !

在这里插入图片描述

总结

在这里插入图片描述

http://www.lryc.cn/news/2386790.html

相关文章:

  • 第五十三节:综合项目实践-车牌识别系统
  • AI时代新词-AI伦理(AI Ethics)
  • 湖北理元理律师事务所债务优化服务中的“四维平衡“之道
  • Git Push 失败:HTTP 413 Request Entity Too Large
  • 第10章 网络与信息安全基础知识
  • GO语言学习(九)
  • go 访问 sftp 服务 github.com/pkg/sftp 的使用踩坑,连接未关闭(含 sftp 服务测试环境搭建)
  • Linux多线程(二)之进程vs线程
  • 【MogDB】测试 ubuntu server 22.04 LTS 安装mogdb 5.0.11
  • AI时代新词-数字孪生(Digital Twin)
  • 【HW系列】—web常规漏洞(文件上传漏洞)
  • 如何实现 C/C++ 与 Python 的通信
  • python炸鱼船
  • 使用AutoKeras2.0的AutoModel进行结构化数据回归预测
  • 好用但不常用的Git配置
  • ULVAC VWR-400M/ERH 真空蒸发器 Compact Vacuum Evaporator DEPOX (VWR-400M/ERH)
  • P1068 [NOIP 2009 普及组] 分数线划定
  • PPT连同备注页(演讲者模式)一块转为PDF
  • 第三十二天打卡
  • 项目三 - 任务8:实现词频统计功能
  • MongoDB 快速整合 SpringBoot 示例
  • 2025.05.22-得物春招机考真题解析-第二题
  • ollama list模型列表获取 接口代码
  • OPC Client第5讲(wxwidgets):初始界面的事件处理;按照配置文件初始化界面的内容
  • 什么是BFC,如何触发BFC,BFC有什么特性?
  • python做题日记(9)
  • Leetcode 3557. Find Maximum Number of Non Intersecting Substrings
  • 【C++进阶篇】初识哈希
  • Spring Boot——自动配置
  • 免费轻量便携截图 录屏 OCR 翻译四合一!提升办公效率