下面是一些预备的知识:
我们先来回顾一下这张图
如果你对此图并不了解,甚至完全都知道这张图,那么下面的内容了解起来可能并不容易。
第一,首先我们要明确的认识一点,文件流指针是各种语言对系统调用接口的封装,无论是什么语言,在对文件进行操作的时候底层都是通过调用一批文件相关的系统调用接口来实现的。
比如C和C++都有着自己的一套关于文件操作的函数。
第二,我们在学习语言阶段的时候,所谈论到的缓冲区都是在上图中的语言层的,这种缓冲区的刷新规则比较的简单,有无缓冲,行缓冲,全缓冲/满缓冲三种刷新策略。这里就不在详细的了解这三种刷新策略了。
第三,操作系统内是有自己的一套管理文件的方式的,并且每个文件都有对应的缓冲区,要注意,这个缓冲区是在系统层面的,要和上面所说的语言层面的缓冲区区别开来。
了解完这三点之后,我们再来看究竟什么是文件流指针,下面我们以C语言的文件操作和linux操作系统来讲解。
文件流指针:
文件流指针就是C中那个可以指向文件位置的指针 一般就是用fopen打开文件 会返回一个FILE类型的指针,这个FILE是一个结构体 里面有描述这个文件的信息,比如文件的大小和其他的一些属性,其中也包括缓冲区。特别需要需要注意的一点是,无论是那种语言对文件进行操作,其底层一定是通过调用系统调用接口实现的,所以,这个FILE结构体内部一定有文件描述符或者是对文件描述符进行了一些封装,这点下面还会提到为什么。
在谈文件描述符之前,我们再来了解一个共同点:
当我们使用例如C语言打开一个文件,并且向文件中写入一些内容时,这些内容先被写入到于洋层面的那个缓冲区里,也就是FILE结构体内部的缓冲区中,再通过具体的缓冲区刷新策略将该缓冲区的内容写入到操作系统层面的缓冲区中。比如我们采用行刷新的策略,那么如果遇到\n就将包括\n和之前的内容刷新到操作系统中,再由操作系统按照系统的缓冲区的刷新策略刷新到对应的文件中去。
在操作系统中有很多的文件,那么我们怎么知道将语言层面的缓冲区内容刷新到那个操作系统的那个文件对应的缓冲区呢?
在一个进程被创建的时候,操作系统就要将其管理起来,因此需要创建PCB将许多的进程统一以某种数据结构管理起来,每个进程的PCB中都会有一个指向文件描述符表的指针,通过该指针我们就能找到该进程的文件描述符表,而这个文件描述符表本质上是一个数组,这个数组中的每个元素就是操作系统管理起来的对应的文件地址。
参考下图:
这个文件描述符表的下标,我们就可以理解为文件描述符
文件描述符是操作系统用来表示打开的文件的一个标记,是操作系统用来描述文件信息的一个东西,其实跟文件流指针挺像的。
联系:
上面我们所谈到的,FILE结构体中一定有文件描述符或者是对文件描述符进行了一些封装,指的就是这个文件描述符下标,通过该下标,我们的进程才能找到具体的文件描述符表中的元素,该元素就是对应文件的地址,通过该地址我们就能将语言层面的缓冲区和操作系统层面的缓冲区联系起来,也就能知道在语言层面的缓冲区内容该被刷新到对应文件系统中的哪个文件缓冲区了!
区别:
操作系统层面上所谈到的文件缓冲区的刷新策略要比语言层面上的复杂的多的多,我们也不必去深究。另外还有就是尽管各种语言的文件操作有所差异,但是对于同一种操作系统而言都是一样的,都是通过系统调用接口来实现真正的文件操作。