Linux基础IO - 文件描述符、重定向

前面的文章中我们讲述了C语言中文件相关的操作与系统文件IO的接口,这篇文章中将会讲述文件描述符与重定向的知识。

运行在前文中的系统文件程序,通过观察可以看到图中的数据3非常的奇怪没头没尾的,下面我们就来从这里开始。

通过查看man手册可以看到open函数的返回值为:

发生错误返回-1, 那么正常运行不应该从0开始吗?为什么是3?0、1、2到哪里去了呢?

任何一个进程,在启动的时候默认会打开当前进程的三个文件: 

标准输入, 标准输出, 标准错误 -- 本质都是文件

stdin           stdout          stderr  -- 文件在语言层的表现

cin              cout             cerr 

图中与上面的意思相同,即文件在语言层的表现,FILE* 是一个类。

上图中的FILE* 又表示着什么内容?

一个简单的例子来看一下上述的内容:

#include <iostream>
#include <cstdio>// 使用c++风格的c语言的头文件    
using namespace std;

int main()
{
	// Linux下一切皆文件,所以,向显示器打印本质上就是向文件中写入,怎样理解?                                 
	// c
	fprintf(stdout, "helo fprintf->stdout\n");
	fprintf(stderr, "helo fprintf->stderr\n");
	
	// c++
	cout << "hello cout -> cout" << endl;
	cerr << "hello cerr -> cerr" << endl;
}

标准输入 -- 设备文件 -> 键盘文件

标准输出 -- 设备文件 -> 显示器文件

标准错误 -- 设备文件 -> 显示器文件

运行上述代码可以发现正常执行的时候标准输入与标准错误都打印在了显示器上,然而当重定向到log文本文件中就会发现只有标准错误打印到了显示器文件上。

至此,可以得到一个结论:标准输出和标准错误都会向显示器打印,但是其实不太一样。

上述我们一共提到了三个问题,接下来将逐一的解决。

fd = 3?

我们知道了任何一个进程,在启动的时候默认会打开当前进程的三个文件,恰好前文的文件描述符正好是从3开始的,可以大胆猜想标准输入, 标准输出, 标准错误三个的文件描述符分别为012,事实也确实如此。当我们打开多个文件时文件的描述符的就会因此增加:

这种数字的增加让人感觉很像数组的下标,我们得出一个结论文件描述符(open对应的返回值)本质上就是数组下标。 

我们根据之前的知识知道,运行代码时他就会变成一个进程拥有PCB,在磁盘中有大量的文件, 打开文件就会被加载到内存中,有了大量的文件就需要使用先描述再组织的方法将其管理起来。内存中就会存在很多的文件对象  --  struct file结构体,里面包含着文件的大部分属性。当进程想要访问文件的时候,只需要找到file就能找到文件的所有内容。文件是操作系统打开的,又是用户让操作系统打开的,最后就是以进程为代表打开文件;因此我们需要有一种方式能够把一个进程和自己打开的文件对应关系维护起来。进程和被打开的文件可以简单的理解为1:n的关系。在操作系统中是这样设计的:在内核汇总定义了struct file_struct结构体,其中有一个数组struct file* fd array[]里面有指向内存中文件结构体的指针,在PCB中有着struct file_stryct* files的指针指向数组存在的那个结构体。

 当我们从磁盘中load文件时,操作系统为了管理文件,为文件创建了一个结构体对象,然后,找到那一个进程打开的文件,在其中的数组中寻找一个未使用的下标,将该结构体的地址填入到数组3下标的位置中,因此我们获得了文件描述符fd = 3。

文件结构体对象中主要是文件的各种属性,还有更重要的是每个文件都要匹配一个缓冲区。当我们执行write函数时,进程PCB就会根据上述说的轨迹找到存放文件对象的数组,根据fd的数据找到文件的地址,再将buffer数组中的字符串拷贝到文件的缓冲区中。我们所谓的IO类read,write函数本质是拷贝函数。文件缓冲区什么时候刷新到磁盘中的指定位置,有OS自主决定。

至此第一个问题fd = 3?就暂时解决了。

如何理解一切皆文件

首先,我们有很多的硬件,每个硬件都有自己的匹配的驱动程序,例如键盘就有写入的函数程序...当我们打开键盘的时候都会在内存中创建文件结构体对象,在结构体中就有着对应的函数指针指向硬件中对应的方法。当进程访问各种硬件的时候在进程看来就是访问的各种文件结构体对象。因此可以得出Linux下一切皆文件。

我们使用OS的本质就是通过进程的方式进行OS的访问的!!!

FILE

在操作系统层面,我们必须要访问fd,才能找到文件,任何语言层访问外设或文件必须经历OS。

FILE是什么?与我们刚刚说的内核的struct file有什么关系?

FILE是一个结构体,是C语言给我们提供的。没有任何关系,上下层的关系

在FILE结构体中必定包含了文件描述符,下面我们可以来验证一下:

int main()  
{   
	printf("%d\n", stdin->_fileno);
	printf("%d\n", stdout->_fileno);
	printf("%d\n", stderr->_fileno);  
	FILE* fp = fopen(LOG, "w");  
  
	printf("%d\n", fp->_fileno);    
}

这样,从上图中就可以看出确实是如此。

重定向的本质

先来看一个简单的例子:

fclose(stdin); // 关闭了0号 close(0);  28   close(2);                                                                                                   29   int fd1 = open(LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);   
int fd2 = open(LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);                                 
int fd3 = open(LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);                                 
int fd4 = open(LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);                                 
int fd5 = open(LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);                                 
int fd6 = open(LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);                                 
                                                                                         
printf("%d\n", fd1);                                                                     
printf("%d\n", fd2);                                                                     
printf("%d\n", fd3);                                                                     
printf("%d\n", fd4);                                                                     
printf("%d\n", fd5);                                                                     
printf("%d\n", fd6); 

可以看到当我们关闭了0号与2号文件,新文件会从小的数依次分配。进程中文件描述符的分配规则:在文件描述符表中,最小的没有被使用的数组元素,分配给新文件。

再看一个有趣的例子:

close(1);
int fd = open(LOG, O_WRONLY | O_CREAT | O_TRUNC);
printf("you can see me\n"); 

现将1号文件即标准输出关闭,然后向屏幕打印信息,运行程序会发现下面的现象:屏幕上并没有打印信息,反而信息出现在了log文本文件中,这种情况就与重定向一致。

重定向的原理:在上层无法感知的情况下,在OS内部,更改进程对应的文件描述符表中,特定的下标指向。 

 同样可以关闭标准输入,达到一个输入重定向的效果:

close(0);
int fd = open(LOG, O_RDONLY);

int a, b;
scanf("%d %d", &a, &b);

printf("a:%d, b:%d\n", a, b);     

还有追加重定向:

close(1);
int fd = open(LOG, O_WRONLY | O_CREAT | O_APPEND, 0666);
printf("you can see me\n");

至此,第三个问题为什么会有如下情况就可以得到了解决:重定向只改写了1号文件描述符对应的文件,没有修改2号文件描述符。

我们可以通过重定向的方式将正确输出与错误输出相互分离:

但是这样的操作不是很便捷,这里OS就提供了一个函数可以来进行重定向操作

 

// close(1)
int fd1 = open(LOG_NORANL, O_WRONLY | O_CREAT | O_TRUNC, 0666);  
dup2(fd1, 1);  
fprintf(stdout, "helo fprintf->stdout\n");  
fprintf(stdout, "helo fprintf->stdout\n");  
fprintf(stdout, "helo fprintf->stdout\n");  
fprintf(stdout, "helo fprintf->stdout\n");  
fprintf(stdout, "helo fprintf->stdout\n");  

// close(2);  
int fd2 = open(LOG_ERROR, O_WRONLY | O_CREAT | O_TRUNC, 0666);  
dup2(fd2, 2);                                                                                              
fprintf(stderr, "helo fprintf->stderr\n");  
fprintf(stderr, "helo fprintf->stderr\n");  
fprintf(stderr, "helo fprintf->stderr\n");  
fprintf(stderr, "helo fprintf->stderr\n");  
fprintf(stderr, "helo fprintf->stderr\n"); 

 

 使用该函数特别需要注意newfd与oldfd的区别:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

console使用方法介绍

console是在写前端Javascript时经常会使用到&#xff0c;我平时使用最多的是console.log&#xff0c;相比大多数人也是如此吧&#xff01; 下面一起来看一下强大的console吧&#xff01; 01函数&#xff08;属性&#xff09; 包含如下函数 / 属性&#xff1a;memory、assert、c…

Hadoop三大框架之HDFS

一、概述HDFS产生的背景及定义HDFS产生背景随着数据量越来越大&#xff0c;在一个操作系统存不下所有的数据&#xff0c;那么就分配到更多的操作系统管理的磁盘中&#xff0c;但是不方便管理和维护&#xff0c;需要一种系统来管理多台机器上的文件&#xff0c;这就是分布式文件…

日入500+的程序员都在用的“接私活”平台

网上总说程序员的薪资很高&#xff0c;这我可就不同意了&#xff1a; 程序员的薪资哪里是很高&#xff0c;而是非常高&#xff01;而会接私活的程序员更是能拿到更高的收入&#xff01;作为一个程序员&#xff0c;这些接私活的网站一定要收藏起来&#xff0c;让你在“八小时外…

ChatGPT transformer 5篇经典论文以及代码和解读

一次性读懂ChatGPT的技术演进路线&#xff0c;根据李沐老师推荐的5篇经典论文&#xff0c;整理了论文原文、论文解读、Github代码实现。 2017Transformer继MLP、CNN、RNN后的第四大类架构2018GPT使用 Transformer 解码器来做预训练2018BERTTransformer一统NLP的开始2019GPT-2更…

区块链概论

目录 1.概述 2.密码学原理 2.1.hash函数 2.2.签名 3.数据结构 3.1.区块结构 3.2.hash pointer 3.3.merkle tree 3.3.1.概述 3.3.2.证明数据存在 3.3.3.证明数据不存在 4.比特币的共识协议 4.1.概述 4.2.验证有效性 4.2.1.验证交易有效性 4.2.2.验证节点有效性 …

YOLOv5源码逐行超详细注释与解读(6)——网络结构(1)yolo.py

前言 在上一篇中&#xff0c;我们简单介绍了YOLOv5的配置文件之一 yolov5s.yaml&#xff0c;这个文件中涉及很多参数&#xff0c;它们的调用会在这篇 yolo.py 和下一篇 common.py 中具体实现。 本篇我们会介绍 yolo.py&#xff0c;这是YOLO的特定模块&#xff0c;和网络构建有…

python【selenium的环境配置】

selenium 1.环境配置 1&#xff09;在环境设置里面安装selenium第三方库 pip install --user selenium2&#xff09; from selenium.webdriver import Chrome# 创建谷歌 b Chrome() # 获取网页 b.get(http://www.baidu.com) # 防止自动关闭 input()3&#xff09;在此之前&…

Rancher系列文章-Rancher v2.6使用脚本实现导入集群

概述 最近在玩 Rancher, 先从最基本的功能玩起, 目前有几个已经搭建好的 K8S 集群, 需要批量导入, 发现官网已经有批量导入的文档了. 根据 Rancher v2.6 进行验证微调后总结经验. 1. Rancher UI 获取创建集群参数 访问Rancher_URL/v3/clusters/&#xff0c;单击右上角“Crea…

第07章_单行函数

第07章_单行函数 &#x1f3e0;个人主页&#xff1a;shark-Gao &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是shark-Gao&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f389;目前状况&#xff1a;23届毕业生&#xff0c;目前在某…

进程地址空间+环境变量

目录 环境变量 进程地址空间 理解虚拟地址空间 进程地址空间区域划分 虚拟内存和物理内存建立联系 深刻理解虚拟地址空间 环境变量 当我们需要使用一个物品的时候&#xff0c;首先要先找到这个物品。同样的&#xff0c;当要运行一个程序&#xff08;指令&#xff09;时&a…

大公司为什么禁止SpringBoot项目用Tomcat?

前言 在SpringBoot框架中&#xff0c;我们使用最多的是Tomcat&#xff0c;这是SpringBoot默认的容器技术&#xff0c;而且是内嵌式的Tomcat。同时&#xff0c;SpringBoot也支持Undertow容器&#xff0c;我们可以很方便的用Undertow替换Tomcat&#xff0c;而Undertow的性能和内…

低功耗技术——流水线设计(加法器和乘法器)

文章目录前言一、流水线1、16bit加法器2、无符号4bit乘法器3、编写一个4bit乘法器模块&#xff0c;并例化该乘法器求解c12*a5*b二、降低FPGA功耗1、静态功耗2、动态功耗前言 2023.3.31 今天学习降低功耗的一些方法 一、流水线 电路最高工作频率&#xff1a;取决于最长的组合逻…

Windows下的详细Git安装

网址链接&#xff1a; Githttps://git-scm.com/ 下载后得到这个程序&#xff1a; 这里它给出的是使用Vim&#xff0c; 我改成的是VSCode: 第一种是让 Git 自己选择&#xff0c;名字是 master &#xff0c;但是未来也有可能会改为其他名字&#xff1b; 第二种是我们自行决定&a…

大文件上传时如何做到秒传?

本文已经收录到Github仓库&#xff0c;该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点&#xff0c;欢迎star~ Github地址 大家好&#xff0c;我是大彬~ …

Selenium基础篇之Select下拉列表选择

文章目录前言一、页面准备二、场景三、设计1.引入库2.启动浏览器实例3.访问本地演示html文件4.定位到select标签5.选择橘子&#x1f34a;5.1 通过索引5.2 通过value值5.3 通过text值6.强制等待7.结束webdriver进程结果前言 大家好&#xff0c;我是空空star&#xff0c;本篇给大…

ChatGPT火出圈,80%的设计师无事可做

自从疫情过后&#xff0c;设计行业开始变得很凌乱。很多同行开始打价格战&#xff0c;设计岗位逐渐演变成了一种新型的流水线。在我国&#xff0c;不管什么行业&#xff0c;到最终都会演变成饱和状态。用金融理论来说&#xff1a;供不应求&#xff0c;就赚钱&#xff0c;供过于…

STM32学习(十二)

软件定时原理 使用纯软件&#xff08;CPU死等&#xff09;的方式实现定时&#xff08;延时&#xff09;功能。 不精准&#xff1a;函数调用压栈进栈需要耗费额外的时间&#xff1b;流水线使得程序执行时间不确定。CPU死等。 定时器定时原理 使用精准的时基&#xff0c;通过…

【大数据之Hadoop】二、Hadoop生产集群搭建之完全分布式集群

1 运行准备 (1)准备3台客户机&#xff08;关闭防火墙、设置静态IP和主机名称&#xff09; (2)安装JDK&#xff0c;配置环境变量 (3)安装Hadoop&#xff0c;配置环境变量 (4)配置集群 (5)单点启动 (6)配置ssh (7)群起并测试集群 2 编写集群分发脚本xsync 集群分发脚本 在/hom…

OpenGL | 渲染带透明通道的2D精灵

一、Alpha测试 Alpha 测试的基本原理为&#xff1a;当绘制一个片元时&#xff0c;首先检测其 Alpha 值&#xff0c;若 Alpha 值满足要求&#xff0c;就通过测试&#xff0c;绘制此片元&#xff1b;否则丢弃此片元&#xff0c;不进行绘制。 glEnable(GL_ALPHA_TEST)&#xff1b…

基于springboot实现私人健身与教练预约管理系统【源码+论文】

基于springboot实现私人健身与教练预约管理系统演示开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea M…