欢迎光临
我们一直在努力

【Linux篇】数字世界的“通行证”——文件描述符深度解析

于此止?:个人主页

❄️个人专栏传送门:《Linux篇》 《QT百日筑基篇》 《算法篇》 

《C++与STL剖析篇》 《数据结构篇》 《Python基础篇》《C语言重难点剖析》

⭐️纵有狂风拔地起,我亦乘风破万里⭐️

文件描述符的详解


目录/索引:

目录

文件描述符的详解

目录/索引:

回顾

文件描述符fd

0 & 1 &2 文件描述符

“0”

“1”

“2”

深度理解FILE

深度理解文件

文件描述符的分配原则:

重定向操作:

追加重定向 vs 输出重定向

​编辑重定向

输出重定向的场景

追加重定向

dup2


回顾

首先我们回顾上一篇文章,我们注意对应的open的返回值,我们在看文档搂一眼。

他会返回我们一个名字为文件描述符的东西,那这个东西是什么呢???

我们现在就来剖析他。

文件描述符fd

通过对open的学习,我们知道他会返回一个文件的描述符。这个文件的描述符是一个整数, 他的返回值是一个int类型的。我们os只认识我们的文件描述符,也是通过文件描述符来打开对应的文件的。

0 & 1 &2 文件描述符

那我们现在来打印一下这个对应的文件描述符吧。 这里我们发现我们的文件描述是3。

那我们多开几个文件呢?

这里我们开4个来进行测试,这里我们发现这个文件描述符是从3开始的开始进行递增的。那么现在有个问题,那我们的文件描述符是从3开始编号的吗???如果不是那我们的1,2呢。是不是还有0呢?

现在我们带着这些问题来进一步的进行说明。

其实我们有对应的1,2甚至还有0,他是我们在启动的时候默认打开的三个文件, 他们分别是标准输入(0)、标准输出(1)、标准错误(2)。

这三个文件符号代表者键盘、显示器、显示器。

我们可以写代码进行对应的验证:

“0”

我们把我们对应的0号关闭我们是不是就能不能给buff字符串进行输入了???如果成立那就说明我们的猜想是正确的。

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

int main()
{
close(0);
char buff[1024];
scanf("%s", buff);
printf("%s\\n", buff);

return 0;
}

果然我们不能进行输入,他自动就到buff这个空间一些没用的数据。所以我们的0号是我们的标准输入。

“1”

接着这样的思想我们是不是给个字符串我们把1给关了是不是我们对应的字符串的内容也打印不出来呀。那我们进行试验看一下。

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

int main()
{
close(1);
char buff[1024]="你好,fd";
printf("%s\\n", buff);

return 0;
}

我们发现我们关了对应的文件描述符1之后我们字符串的内容就打印不出来了。

“2”

这个2就是对应的标准错误。

深度理解FILE

我们发现我们之前像我们的c语言,我们打开文件一般都返回FILE这个指针,我们不应该返回一个int的文件描述符吗?那么就只有一个东西我们的FILE文件流是一个结构体。是c语言标准库给我们进行封装的一个我们用于文件操作的一个东西。

这个FILE的内部一定是封装了对应的文件描述符fd用于我们的文件的打开等一系列的动作。

深度理解文件

对文件内容做任何操作,都必须先把文件加载到对应的内核缓冲区中。(也就相当于一次拷贝)

每当我们打开一个文件,文件描述符都有一些对应对的位置来链接文件。

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

文件描述符的分配原则:

我们通过前面的描述知道了对应的文件操作符大概就是一个数组的下表,那他的分配规则也是一个数组的下表那样的规则吗?

我们来测试一下:

我们运行下面的代码发现我们的结果是3,那如果我们把这个3关闭我们再次申请打开一个文件呢?

会是4(递增试增加)还是3呢(数组式占用)。我们来测试一下吧:

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

int main() {
int fd = open("add.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
printf("%d\\n",fd);
return 0;
}

通过下面的结果我们就能看到他对应的文件描述符就是3,那么符合我们数组样的。

所以我们能得到结论:

我们除去0,1,2这三个文件描述符时,我们打开文件的文件描述符,会一直先占有前面空位置的文件描述符的位置。也就是我们找到一个最小未被使用的位置进行填写。作为新的文件描述符。

我们可以试验一下我们关闭0号文件描述符我们下次申请的时候可能就给我们返回对应的0号的位置。

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

int main() {
int fd = open("add.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
printf("%d\\n",fd);
close(fd);
int fd1 = open("add.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
printf("%d\\n",fd1);
return 0;
}

close(0)

我们发现我们占有的果然是我们对应的0号下表。

重定向操作:

在 我们的命令行中也经常使用重定向的操作:

我们来看一下这个对应的重定向操作吧:

这是对应的输入输入重定向。

echo "这个是一个重定向操作" > test.txt

我们还有追加重定向:

echo "这个是一个重定向操作" >> test.txt

追加重定向 vs 输出重定向

我们的追加重定向是直接在文件后面进行添加的,我们的输出重定向是我们会清空文件的。我们来看一下:

我们能看到我们之前的文件已经被清空,我们后面的内容又被进行了对应的写入。

重定向

输出重定向的场景

在将重定向之前我们先看一个现象:

我们下面的代码发现我们应该会输出这段文字,到显示器中我们的显示器我们可认为是stdout,我们发现我们将他关闭后我们的文件并没有进行在显示器中进行打印, 这是什么呢???

哦我们知道前面的文件描述符的分配规则,我们的这个fd就是对应的文件描述符。肯定是1号文件描述符。那我们看看我们对应的add.txt文件。

我们惊奇的发现原本在显示器中的文件结果到了对应的add.txt文件中,这种操作就叫做重定向。

ok。这样的原理是什么呢?我们画图来进行分析。

此时像我们这样我们对应的1号文件描述符已经指向了我们新打开的文件。

其实在这里我们可以大胆的进行猜测。

我们的向显示器(stdout)进行输入一定是封装了1号的文件描述符,这样我们打印的时候会默认的向我们的显示器中进行打印。而我们刚才关闭了我们对应的1号文件描述符所以他不再像我们的显示器进行打印了。而是向我们的新的1号文件描述符进行打印了。

我们可以得出对应的结论:printf向1号文件描述符进行打印。默认的我们1号文件描述符是stdout且stdout的内部肯定是封装了对应的1号文件描述符。我们将我们的1号文件描述符进行替换,那么他就会向我们新的1号文件描述符进行打印。

那我们有了上面的认识,那我们试一试追加重定向:

追加重定向

我们想一想该怎么进行对应的追加重定向呢???

这里看到我们的文件打开的方式,我们好像只要改一个参数就可以完成对应的重定向了。我们只要把清空换成对应的追加应该就可以了。

我们发现我们果然能完成对应的重定向操作了。

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

int main()
{
close(1);
int fd = open("add.txt", O_WRONLY | O_CREAT | O_APPEND,0666);
printf("你好,重定向");
return 0;
}

我们运行查看对应的结果。这里就完成了我们对应的追加重定向操作。

输入重定向:

标准输入(stdin):程序默认从键盘读取数据,文件描述符 = 0。 输入重定向:把程序的数据源,从键盘改成文件 / 管道等,不再手动敲键盘输入。

在我们的命令行也是能进行对应的输入重定向的,我们来看一下

cat < test.txt

我们的输出重定向是原本打印到显示器中的字符转而打到了对应的对应的文件中那我们的输入重定向是不是将我们将文件的内容读到我们对应的显示器上。

那这样肯定和我们对应的0号文件描述符有关。

效果等价于 cat test.txt(二者原理不同,表象相似)

  • cat test.txt:cat 主动打开文件读取
  • cat < test.txt:shell 打开文件,把文件内容交给 cat 当作标准输入

运行下面的代码我们发现他不是在我们的标准的输入里面进行读取,而是我们关闭了对应的标准输入默认找新的文件描述符0也即是我们的add.txt里面进行查找。所我们的scanf默认会找我们的文件描述符0发现我们已经将stdin被关闭了,我们的新的文件描述符0是add.txt了。所以我们默认会像我们add.txt进行读取。

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

int main()
{
close(0);
int fd = open("add.txt", O_RDONLY);
char buffer[1024];
scanf("%s", buffer);
printf("%s\\n", buffer);
return 0;
}

dup2

dup2是我们进行重定向操作的一个方便的系统调用,他的本质就是底层指针的拷贝。

我们看一下官方文档,看一下如何去使用这个函数。

这里dup2有两个参数,一个是旧的文件描述符,一个是新的文件描述符。

细节问题:

dup2()系统调用执行与dup()相同的任务,但它不是使用编号最低的未使用文件描述符,而是使用newfd中指定的文件描述符号。如果文件描述符newfd先前已打开,则在重新使用之前将其静默关闭。

我们dup2就是将oldfd覆盖到新的newfd中, 如果我们要实现我们的输出重定向我们只需要dup2(fd,1)就可以了。下面进行画图查看:

返回值:

成功返回新的文件描述符, 失败-1被返回,错误码被设置。

代码的演示:

我们运行发现我们期待的效果果然和我们的预期是一样的。

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

int main()
{
int fd =open("add.txt", O_WRONLY | O_TRUNC | O_CREAT, 0666);
int n = dup2(fd,1);
if(n<0)
{
perror("dup2 fail!");
exit(1);
}

printf("你好dup2");
return 0;
}

我们查看我们的add.txt文件:

果然是我们的输出重定向

这里我们输出一个结论对我们的重定向有一个更加深刻的理解:

重定向=打开文件+dup2

赞(0)
未经允许不得转载:171主机测评 » 【Linux篇】数字世界的“通行证”——文件描述符深度解析
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址