首页 > 系统相关 >[ Linux ] 文件描述符和重定向

[ Linux ] 文件描述符和重定向

时间:2022-11-10 13:00:31浏览次数:34  
标签:文件 重定向 int 描述符 fd Linux include open

在上篇文章我们复习了C文件IO操作并且认识了文件相关的系统调用接口。本篇文章我们要引入文件描述符的概念。

0.文件描述符

0.1引入文件描述符

我们在认识open接口时知道了该接口有一个int的返回值,但是当时我们并没有重点介绍这个返回值到底是什么,而这里我们将重点介绍这个返回值。因此我们用man手册查一下open函数的返回值。根据手册描述open函数如果打开或创建成功则会返回一个新的文件描述符,否则失败则返回-1。

[ Linux ] 文件描述符和重定向_fd

那我们写一段代码来验证一下这个返回值,看看效果

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

int main()
{
int fd1 = open("log1.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
int fd2 = open("log2.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
int fd3 = open("log3.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
int fd4 = open("log4.txt",O_WRONLY | 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);
return 0;
}

[ Linux ] 文件描述符和重定向_fd_02

我们看到文件被创建出来了,输出的fd也都是大于1的整数,但是这里有两个问题:

  1. 为什么fd是从3开始的呢?0,1,2去哪儿了呢?

答:0,1,2被默认打开了。0叫做标准输入(键盘),1叫做标准输出(显示器),2叫做标准错误(显示器)。

On program startup, the integer file descriptors associated with the streams stdin, stdout, and stderr are 0, 1, and 2,respectively. (在程序启动时,与流stdin、stdout和stderr关联的整数文件描述符分别为0、1和2。)

[ Linux ] 文件描述符和重定向_重定向_03

[ Linux ] 文件描述符和重定向_文件描述符_04

[ Linux ] 文件描述符和重定向_重定向_05

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

int main()
{
printf("stdin:%d\n",stdin->_fileno);
printf("stdout:%d\n",stdout->_fileno);
printf("stderr:%d\n",stderr->_fileno);
return 0;
}

[ Linux ] 文件描述符和重定向_重定向_06

函数对应的接口

数据类型的对应

fopen/fclose/fread/fwrite.....

FILE* ->FILE

open/close/read/write......

fd

因此我们使用的C语言接口一定封装了系统调用接口。

2.fd为什么是0,12,3,4,5......

一般而言,一个进程可不可以打开多个文件?答案当然是可以的,所以在内核中,一个进程:打开的文件 = 1:n

所以系统在运行中有可能会存在大量的被打开的文件,操作系统要对被打开的文件进行管理。那么操作系统如何管理这些被打开的文件呢?--->先描述再组织 因此一个文件被打开在内核中要创建被打开文件的内核数据结构--先描述。 struct file 内部包含了大量的内容和属性。操作系统将多个文件的struct file用链表连接起来,因此对被打开文件的管理转换成了对链表的增删查改!那么进程如何和打开的文件建立映射关系呢?因此我们在task_struct中包含一个struct files_srruct * fs指针指向strcut files_struct内部有一个指针数组,存的就是打开文件的fd_array[ ]. 因此要对文件进行操作时,只需要得到这个数组的下标即可。

[ Linux ] 文件描述符和重定向_文件描述符_07

而现在知道,文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了fifile结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*fifiles, 指向一张表fifiles_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件

0.2 文件描述符的分配规则

通过上文的了解我们知道了文件描述符是从3开始的,因为0,1,2默认分给了stdin,stdout,stderr.那么当我们关闭0或者2我们再重新创建一个文件时,他的文件描述符会是几呢?

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

int main()
{
close(0);
int fd = open("myfile.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
if(fd < 0)
{
perror("open");
return 1;
}
printf("fd:%d\n",fd);
close(fd);

return 0;
}

[ Linux ] 文件描述符和重定向_文件描述符_08

[ Linux ] 文件描述符和重定向_文件描述符_09

发现是结果是: fd: 0或者fd:2 可见,文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

0.3 重定向

那如果我们关闭fd为1呢?根据文件描述符的分配规则,新建文件的文件描述符fd会是1吗?我们来看代码:

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

int main()
{
close(1);
int fd = open("myfile.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
if(fd < 0)
{
perror("open");
return 1;
}
printf("fd:%d\n",fd);
close(fd);

return 0;
}

[ Linux ] 文件描述符和重定向_文件描述符_10

此时我们发现什么都没有,这是为什么呢?这时候我们要谈谈重定向了。根据文件描述的分配规则,我们新建的文件描述符fd一定是1,虽然不再指向对应的显示器了,但是已经指向了myfile.txt的底层数据结构了。那么我们如何查看到这个fd呢?这就跟输出重定向相关了,那么我们必须刷新一下才能看到,那么为什么要刷新呢?这是跟缓冲区相关的我们后面解释。

[ Linux ] 文件描述符和重定向_重定向_11

0.3.1 重定向的本质

在上述代码中,我们本来要往显示器上面写,最终却变成了向指定文件中写,这就是输出重定向。

当我们close(1)后,新建一个文件时,根据文件描述符分配规则,1号下标会指向新建的文件,因此凡是往1号文件描述符写的内容都写到myfile当中,而不再写到标准输出了。

[ Linux ] 文件描述符和重定向_重定向_12

如果我们要进行重定向,上层只认0,1,2,3,4,5这样的fd,我们可以在OS内部通过一定的方式调整数组的特定下标的内容(指向),我们就可以完成重定向操作!

0.3.2 dup2 -- 重定向的具体操作

上面的一对数据,都是内核数据结构,只有OS有权限,因此我们用户在使用的时候必须提供接口.因此操作系统提供了dup2

[ Linux ] 文件描述符和重定向_文件描述符_13

dup2的作用:

makes newfd be the copy of oldfd, closing newfd first if necessary。

[ Linux ] 文件描述符和重定向_重定向_14

dup2的使用:

假设我们要实现刚刚输出重定向的操作,那么根据dup2的描述,他是copy什么呢?参数怎么传?

dup2是copy数组下标对应的内容(文件地址的拷贝)最终的结果是newfd的内容是oldfd的一份拷贝,因此最后只剩下oldfd的内容。因此dup2是将oldfd拷到newfd内。因此如果要输出重定向,是要将fd为3的内容拷贝到fd为1内部。因此最后的内容和fd为3的内容保持一致。

因此输出重定向 dup2(3,1); 我们使用代码来验证一下

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

int main()
{
int fd = open("myfile.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
if(fd < 0)
{
perror("open");
return 1;
}

dup2(fd,1);

printf("文件的fd:%d\n",fd);
fflush(stdout);
close(fd);
return 0;
}

[ Linux ] 文件描述符和重定向_fd_15

printf是C库当中的IO函数,一般往 stdout 中输出,但是stdout底层访问文件的时候,找的还是fd:1, 但此时,fd:1下标所表示内容,已经变成了myfifile的地址,不再是显示器文件的地址,所以,输出的任何消息都会往文件中写入,进而完成输出重定向。

追加重定向:

[ Linux ] 文件描述符和重定向_文件描述符_16

输入重定向:

[ Linux ] 文件描述符和重定向_文件描述符_17



(本篇完)


标签:文件,重定向,int,描述符,fd,Linux,include,open
From: https://blog.51cto.com/xingyuli/5836911

相关文章

  • 如何优雅的在Linux下开机自动重启脚本
    1简介经常碰到机器断电之后需要重启一大堆服务,为了防止这种事情发生,设置开机自启的脚本十分的重要,我们习惯性的做法就是编写一个重启脚本,然后在 /etc/rc.local 中去完......
  • Linux修改成静态ip+虚拟机设置NAT
    一般改为静态ip都是想内网固定ip搭配虚拟机搭测试环境        打开虚拟机的这个路径,ifcf-enp2s0是自己的网卡名称vi/etc/sysconfig/network-scripts......
  • Linux配置环境变量
    Linux配置环境变量在配置Linux系统服务器的时候,我们常常需要设置系统环境变量,这篇文章就是总结几种常见的配置环境变量的方式。读取环境变量export命令读取当前系统......
  • Linux查看资源限制
    //显示所有资源限制ulimit-a real-timenon-blockingtime(microseconds,-R)unlimitedcorefilesize(blocks,-c)0datasegsize(......
  • Linux学习笔记(9)——学习Shell脚本
    学习shell脚本学习shell脚本一、什么是shell脚本1.1为什么要学习shell脚本1.2第一个脚本的编写与执行1.3建立shell脚本的良好编写习惯二、简单的shell脚本......
  • 皕杰报表的Linux部署
    对于一个新的linux系统,在上面部署皕杰报表需要下面的步骤: 1.首先得配置java环境变量,因为皕杰报表是纯java的,所以环境变量必不可少。2.然后把linux版的tomcat放到Linux系统......
  • 在PetaLinux工程中调试关键模块代码
    在PetaLinux工程中,与单板相关的UBoot、Linux等模块,经常需要编辑、调试。在旧版本中,可以为UBoot、Linux选择“ext-local-src”,指定UBoot、Linux的代码,从而使PetaLinux使......
  • 20201317-Linux-Thread 互斥测试
    #include<stdio.h>#include<stdlib.h>#include<pthread.h>//linux线程库#include<ctype.h>//测试和映射字符的库structarg_set{ char*fname; int......
  • Windows路径或者Linux路径映射成web路径进行访问代码
    packagecom.soft.mpms.zframe.config;importjava.io.File;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.servlet.c......
  • curl跟踪重定向
    curl -Li https://xxxi 输出响应头,这样就能定位302之类的L跟踪重定向到最后一种是通过302重定向,需要解析header里面的内容<?php$context=stream_context_create(......