0


Linux - fd文件描述符和文件详解

                                            ​​​​​​​             ​​​​​​​ ![7db93f3910ad44a1954db5498e2f2a68.gif](https://img-blog.csdnimg.cn/7db93f3910ad44a1954db5498e2f2a68.gif)



                                           感谢各位 点赞 收藏 评论 三连支持



                                          本文章收录于专栏【Linux系统编程】   





                                                 ❀希望能对大家有所帮助❀



                                                   本文章由 风君子吖 原创

    ​​​​​​​        ​​​​​​​        ​​​​​​​               ![4db2d7e7fd9e46368e95cbc4f464fe5a.gif](https://img-blog.csdnimg.cn/4db2d7e7fd9e46368e95cbc4f464fe5a.gif)

前言

之前我们学习回顾了C语言文件操作的接口函数,并且学会了使用系统给我们提供的文件操作接口函数,

还知道了许多的概念,对于语言层面上的接口函数,它们在底层必然会对系统接口函数进行封装,这不仅便于我们使用,而且还实现了语言的跨平台性。

而学习系统调用接口,就是学习语言层面上我们一些我们无法理解的东西,能够更好的了解底层,再今后的学习更加受益匪浅!

而在Linux中,万物皆文件,所以不仅仅只是磁盘上存储的文件叫文件,我们的各种硬件也能被称之为文件,所以,我们调用系统提供的文件操作函数也可以对硬件进行各种操作,当然,这种操作并不是我们用户层来操作的,而是进程经过OS之手来进行操作的,因为只有操作系统才有对硬件进行IO的权限!

上一篇文章,我们对于文件描述符fd进行了初步的了解,而今天,我们就来详细讲解文件描述符fd与整个文件体系的密切关系

文件描述符fd

想要理解文件描述符就得明白这样一个概念,怎么样的文件才会被给予文件描述符? 一台计算机中的磁盘上可能会有上万的文件,那么我们需要对所有的文件都进行管理吗? 肯定不能,所以,我们只能对被进程打开的文件进行管理,而这些文件也会被加载到内存中,而这些被打开的文件,就会被给予文件描述符。

上篇文章中,我们从打印的文件描述符来看,它似乎是一串连续的数字,并且我们打开的文件的描述符都是从3开始依次增加的,这是因为在系统在进程运行时,会默认打开三个文件,这些文件就是标准输入(0),标准输出(1),标准错误(2)。

bf127b6d4fdd4a609ec5a6e0a77f30e0.png

从fd的结果来看 为什么是一串连续的数字,这有什么特殊的含义吗? 从我们之前的经验来看,一串连续的数字通常会联想到什么? 没错,就是数组下标,那么这跟数组下标真的有关系吗?如果真的是数组下标,那么有什么用,这就需要明白文件在内存中的体系结构。

文件体系结构(重点)

这里讲的文件体系结构指的是被进程打开的文件,而被进程打开的文件是要被加载的内存的,也就是要在内核中创建一个管理文件的数据结构,更重要的是,既然是被进程所打开,那么就需要被进程所管理起来。

而进程的管理模块是我们熟的不能再熟的PCB(process control block) ,那么有什么办法可以储存文件的几乎所有内容呢? -> 答案是struct 结构体 ,而这个结构体叫做struct file{...} ,这个结构体包含了一个文件的几乎所有内容。

但是一个进程只能打开一个文件吗? 肯定是不止的,光进程打开的默认三个文件标准输入输出、错误就有三个文件,所以一个进程是可以打开多个文件的,而进程与被打开的文件的比例就是1:n,所以我们就又需要一个结构体来管理所有的打开的文件,这个结构体叫做files_struct,并且这个结构体是被存储于进程的PCB之中的,所以就有了这么一个对应关系

struct task_struct(PCB)->struct files_struct->struct file

files_struct储存多个struct file是通过一个指针数组来完成的,而这个指针数组,每个元素都是存储着struct* file,那么这个时候fd充当下标就再合适不过了!

686e7a6cb580421e881210a8887ee7cc.png

重定向

对于fd文件描述符的作用我们已经知道了,它是被files_struct 用来找对应的文件的,那么我们来看下面比较有意思的现象。

先将fd为1的文件关闭,也就是标准输出文件关闭,然后再打开log.txt文件

7e423479907c414bbd8bf5b9360e1d43.png

[fengjunzi@VM-4-2-centos lesson14_fd]$ ./myfile 
[fengjunzi@VM-4-2-centos lesson14_fd]$ ll
total 24
-rw-rw-r-- 1 fengjunzi fengjunzi   22 Jun  7 18:27 log.txt
-rw-rw-r-- 1 fengjunzi fengjunzi   65 Jun  7 13:03 Makefile
-rwxrwxr-x 1 fengjunzi fengjunzi 8608 Jun  7 18:27 myfile
-rw-rw-r-- 1 fengjunzi fengjunzi  340 Jun  7 18:27 myfile.c
[fengjunzi@VM-4-2-centos lesson14_fd]$ cat log.txt 
hello world
hello 123

这个时候运行程序发现并没有打印hello world 和 hello 123 在屏幕上,并且再查看log.txt的内容,发现竟然写到了log.txt 里面,这是怎么一回事?

close(1)关闭的是标准输出,那么关闭的是stdout吗? 这是不准确的,因为stdout是FILE*类型,而FILE是C语言定义的,那么系统是认fd还是认FILE? 而stdout要跟标准输出要有关联,那么C语言一定对stdout进行了fd的封装,并且stdout的fd一定是1 ,所以这里我们虽然close(1),但是stdout仍然存在,且stdout的fd仍然是1,但是stdout已经不再指向的是标准输出了,那是指的什么? -> 从结果来看,这时候的stdout指向的是log.txt文件。

为什么会指向log.txt?因为我们先关闭了fd = 1 ,而在我们打开新文件的时候,是会先从头检查fd_array是否存在空指针,如果有空指针,就会把该位置的fd分配给那个文件,所以这个时候log.txt就顶替了stdout的 fd,而printf,和fprintf无不是针对fd = 1 的文件进行写入!

那么这种现象叫做什么?是不是很像输出重定向?那么输出重定向的实现就是我们上面所写的那样吗? 不是,因为系统提供了一个接口来更好的实现这个功能!

而通过这种方式,我们如果先close(0),再调用scanf,是不是就相当于输入重定向了?

dup2系统调用

19f23ecda0904f64b6df8d78f3f86c91.png

dup2() makes newfd be the copy of oldfd, closing newfd first if necessary, but note the following:

 *  If oldfd is not a valid file descriptor, then the call fails, and newfd is not closed.

 *  If oldfd is a valid file descriptor, and newfd has the same value as oldfd, then dup2() does nothing, and returns newfd.

翻译过来就是使newfd被拷贝于oldfd,拷贝的是什么,当然是文件的内容,newfd指向的文件内容会被拷贝成oldfd的文件内容。

通过这个接口就能更好的实现输入、输出、追加重定向,因为如果使用上面的那种还需要关闭一个文件,更加麻烦。

输出重定向

8242f98e5e2c42e6970540ddb7865610.png

c01bd0ca59ea4c1ab66603b1dad09700.png

追加重定向

2147f67c5648442a96fcf747771cc494.png

1ffe231c50214e8eb7e384c845eb1830.png

输入重定向

c29d952decd1421996fe5f45dc77b2cb.png

d7498f90754540589e8e63f06e9128cb.png

总结

本篇文章,主要讲解了被打开的文件在内存内核中被存储的数据结构,详细讲解了其体系结构,并且讲解了fd文件描述符在整个体系结构的作用,用于作为下标访问对应文件内容。

还讲解了系统提供的dup2接口的作用,以及如何调用dup2 实现输入、输出、追加重定向。

但是,对于文件我们仍然有许多疑问,例如文件缓冲区,还有struct file 存储的一些什么标志性内容,这些我打算放在下一篇文章来进行详细讲解。

标签: c语言 linux 进程

本文转载自: https://blog.csdn.net/fengjunziya/article/details/131085666
版权归原作者 风君子吖 所有, 如有侵权,请联系我们删除。

“Linux - fd文件描述符和文件详解”的评论:

还没有评论