感谢各位 点赞 收藏 评论 三连支持
本文章收录于专栏【Linux系统编程】
❀希望能对大家有所帮助❀
本文章由 风君子吖 原创
前言
之前我们学习回顾了C语言文件操作的接口函数,并且学会了使用系统给我们提供的文件操作接口函数,
还知道了许多的概念,对于语言层面上的接口函数,它们在底层必然会对系统接口函数进行封装,这不仅便于我们使用,而且还实现了语言的跨平台性。
而学习系统调用接口,就是学习语言层面上我们一些我们无法理解的东西,能够更好的了解底层,再今后的学习更加受益匪浅!
而在Linux中,万物皆文件,所以不仅仅只是磁盘上存储的文件叫文件,我们的各种硬件也能被称之为文件,所以,我们调用系统提供的文件操作函数也可以对硬件进行各种操作,当然,这种操作并不是我们用户层来操作的,而是进程经过OS之手来进行操作的,因为只有操作系统才有对硬件进行IO的权限!
上一篇文章,我们对于文件描述符fd进行了初步的了解,而今天,我们就来详细讲解文件描述符fd与整个文件体系的密切关系
文件描述符fd
想要理解文件描述符就得明白这样一个概念,怎么样的文件才会被给予文件描述符? 一台计算机中的磁盘上可能会有上万的文件,那么我们需要对所有的文件都进行管理吗? 肯定不能,所以,我们只能对被进程打开的文件进行管理,而这些文件也会被加载到内存中,而这些被打开的文件,就会被给予文件描述符。
上篇文章中,我们从打印的文件描述符来看,它似乎是一串连续的数字,并且我们打开的文件的描述符都是从3开始依次增加的,这是因为在系统在进程运行时,会默认打开三个文件,这些文件就是标准输入(0),标准输出(1),标准错误(2)。
从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充当下标就再合适不过了!
重定向
对于fd文件描述符的作用我们已经知道了,它是被files_struct 用来找对应的文件的,那么我们来看下面比较有意思的现象。
先将fd为1的文件关闭,也就是标准输出文件关闭,然后再打开log.txt文件
[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系统调用
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的文件内容。
通过这个接口就能更好的实现输入、输出、追加重定向,因为如果使用上面的那种还需要关闭一个文件,更加麻烦。
输出重定向
追加重定向
输入重定向
总结
本篇文章,主要讲解了被打开的文件在内存内核中被存储的数据结构,详细讲解了其体系结构,并且讲解了fd文件描述符在整个体系结构的作用,用于作为下标访问对应文件内容。
还讲解了系统提供的dup2接口的作用,以及如何调用dup2 实现输入、输出、追加重定向。
但是,对于文件我们仍然有许多疑问,例如文件缓冲区,还有struct file 存储的一些什么标志性内容,这些我打算放在下一篇文章来进行详细讲解。