> 文章列表 > Linux复习 / 文件系统QA梳理

Linux复习 / 文件系统QA梳理

Linux复习 / 文件系统QA梳理

文章目录

    • 前言
    • Q&A
      • 文件的基本理解
        • Q:谈谈你是怎么理解文件的?
        • Q:什么是当前工作路径?
        • Q:文件与操作系统有着怎样的关系?
        • Q:为什么语言要封装系统接口?
      • 文件描述符
        • Q:系统和进程是怎样管理文件的?
        • Q:文件描述符的分配规则是怎样的?
        • Q:如何理解Linux下一切皆文件(Linux怎样将硬件也看成文件)?
      • 重定向
        • Q:重定向的原理是什么?
        • Q:如何使用重定向函数dup2?
      • 语言级别的缓冲区
        • Q:何为缓冲区?
        • Q:缓冲区的意义是什么?
        • Q:语言级别的缓冲区在哪?
        • Q:缓冲区的刷新策略有哪些?

前言

本篇博客梳理关于Linux文件系统相关Q&A,若读者也在复习这块知识,或者正在学习这块知识,可以通过这些Q&A检测自己的知识掌握情况。此外,思维导图已经更新至我的gitee,Q&A之外的体系梳理还请移步思维导图。

Q&A


文件的基本理解

Q:谈谈你是怎么理解文件的?

A:

  1. Linux中,文件由文件属性和文件内容组成,即使一个文件为空,它的大小也不可能是0。因为文件 = 文件内容 + 文件属性,文件属性(文件创建时间、文件所有者等)不可能为空,文件就不可能为空
  2. 和程序一样,文件在没有被打开之前存储在磁盘上,打开文件的本质是将文件从磁盘加载到内存中。所以打开文件使磁盘文件变为内存文件
  3. 我们使用fopen、fwrite这样的语言函数操作文件,这些函数将被编译形成程序。只有程序运行起来成为进程后,我们对文件的操作才能实现,所以其实是进程在替我们操作文件。只有搞懂了进程与文件间的关系,我们才能深刻理解文件操作

Q:什么是当前工作路径?

A:使用命令"ll /proc/进程pid"可以查看指定进程的信息,其中有两个字段cwd与exe。cwd是该进程的当前工作路径,exe是该进程对应的可执行文件完整的路径。

一般情况下,当前工作路径和可执行文件所在目录是相同的,若可执行文件打开一个不存在的文件,该文件会被创建在当前工作路径下。但当前工作路径和可执行文件所在目录无关。使用chdir函数可以修改cwd,之后创建的新文件会被存储到新的工作路径下,此时新文件和可执行文件不在同一目录下。

Q:文件与操作系统有着怎样的关系?

A:文件被操作系统管理。文件属于磁盘资源的一部分,而操作系统作为系统资源的管理者,管理者磁盘资源,所以操作系统需要对文件进行管理。我们(进程)对文件进行的操作需要告知其管理者(操作系统),不能绕过管理者访问被管理的资源,这会使资源陷入不可控状态。所以,文件操作一定贯穿了操作系统。

Q:为什么语言要封装系统接口?

A:一些函数调用,如:scanf,fopen涉及到系统资源的访问,只要访问系统资源就必须经过操作系统。所以语言需要封装操作系统向上提供的接口以访问操作系统管理的资源。不同操作系统提供的接口存在差异,语言采用穷举所有操作系统+条件编译的方式,实现对不同操作系统的兼容,使函数调用者与系统接口解耦,只需关注语言提供的统一的接口。并且语言提供的接口比系统接口简单,使用便捷,学习成本低。


文件描述符

Q:系统和进程是怎样管理文件的?

A:在系统中,文件信息使用struct file结构体描述,并用数据结构将struct file维护起来。在进程中,task_struct是进程控制块,其中有一个files指针,指向了struct files_struct结构体,该结构体包含了一个数组fd_array[],其类型为struct file*,也就是系统描述文件信息的结构体。在fd_array数组中,文件描述符作为数组下标,对应的位置存储struct file指针,指向了系统的struct file。它们之间的关系图如下:

Linux复习 / 文件系统QA梳理
若进程新打开了一个文件,系统先创建struct file结构体,将其添加到队列中,接着进程在fd_array[]数组的空位置(存储nullptr的位置)写入该struct file的地址,数组位置的下标就是文件的文件描述符。

Q:文件描述符的分配规则是怎样的?

A:文件描述符会优先使用最小的没有被使用的下标。open打开一个文件,系统会从头开始遍历fd_array[]数组,只要找到空位置就将文件的struct file指针写入该位置,并且将该位置的下标(文件描述符)作为open函数的返回值返回。

Q:如何理解Linux下一切皆文件(Linux怎样将硬件也看成文件)?

A:操作系统无法直接操作硬件,只能通过硬件厂商提供的驱动操作硬件,驱动中封装了操作硬件的相关函数。一个设备极大概率有读和写两个操作,这两个操作封装在驱动程序中。而struct file结构体有两个函数指针,分别指向了驱动的读函数和写函数(这是C语言中的面向对象思想),假设进程需要读取磁盘,那么操作系统会通过进程找到磁盘对应的struct file结构体,通过其中的读指针找到磁盘的读函数,也就是驱动中的读函数,并且调用它。如果硬件没有读或写方法,那么struct file结构体中对应的函数指针为空。

Linux复习 / 文件系统QA梳理

总结一下:Linux使用struct file结构体以统一视角看待软硬件,软件有读/写方法,硬件大概率也是有的,Linux通过struct file中的函数指针,以面向对象的方式指向软硬件的读/写方法,若软硬件没有读写方法,那么该函数指针为空。

所以Linux用虚拟文件系统,将进程与硬件进行了解耦,并且使进程以统一的视角(文件)看待软硬件,以统一的方式管理不同的资源,通过‘加一层’的方式使进程的设计与实现更加的简单。


重定向

Q:重定向的原理是什么?

A:重定向的含义是:改变数据的流动方向。本该向A输出的数据,却输出到了B。

操作系统通过文件描述符标识文件,并且默认将fd为0、1、2的文件视为标准输入流、标准输出流、标准错误流,上层的语言设计文件操作接口也是如此。stdin、stdout、stderr对应的fd为0、1、2,printf本质上是对stdout文件进行输出,对应的fd为1,如果fd为1的文件不再是stdout而是其他文件,那么本该输出到标准输出的数据将会被输出到其他地方,这就是重定向。

由于文件描述符只是task_struct中fd_array[]数组的下标,我们只要改变fd_array[]数组的内容,使文件描述符指向其他文件,就可以实现重定向。比如关闭了fd为1的文件,此时创建新文件,根据文件描述符的分配规则,新文件的fd为1,此时使用printf输出数据,数据将被重新的到新文件,而不是屏幕(标准输出)上。

可以实现重定向的原因是:操作系统只根据fd识别文件,而高级语言默认将0、1、2文件对应标准输入、标准输出、标准错误。在改变底层的fd指向的文件的前后,使用C语言向标准文件流输入输出,可能得到不同的结果。

Linux复习 / 文件系统QA梳理

Q:如何使用重定向函数dup2?

A:函数原型

#include <unistd.h>
int dup2(int oldfd, int newfd);

函数参数是两个文件描述符,该函数会将oldfd拷贝覆盖到newfd。从原理的角度上理解:系统将fd_array[]数组中下标为oldfd元素拷贝到下标为newfd的位置上,使原newfd位置的struct file*指针被覆盖为oldfd位置的struct file\\,此时再向newfd输出/输入,数据就被重定向到oldfd文件中。

比如:

dup2(1, 2);

将标准错误流重定向到标准输出流,此时再向标准错误流输出数据,数据会被输出到标准输出中。关于返回值:若重定向成功,函数返回newfd的值,若重定向失败,函数返回-1并设置errno。

总结:dup2有两个参数,后一个参数对应的文件输入/输出会被重定向到前一个文件。

语言级别的缓冲区

Q:何为缓冲区?

A:使用语言级别的文件接口向文件进行输入/输出时,数据不会直接递达对应文件/递达进程本身,而是被放入缓冲区中,当缓冲区的数据满足刷新条件时,这些数据才会从缓冲区中被移除,进行下一步动作。

Q:缓冲区的意义是什么?

A:解放进程,提高进程的工作效率,减少进程的IO次数。进程将数据放入缓冲区中,不进行真正的IO,只有缓冲区中的数据满足刷新条件时,才会进行真正的IO,这样的集中IO提高了进程的传输效率。并且进程与磁盘这样的低速设备进行IO时,每次IO的速度极慢,集中IO的同时也减少了IO次数,整体上减少了进程IO的时间。

Q:语言级别的缓冲区在哪?

A:根据之前的认识,c语言有一个结构体FILE,其中封装了系统级别的文件描述符,除此之外FILE中还有一个缓冲区结构。所以说,语言级别的缓冲区有语言的结构进行维护,缓冲区封装在FILE结构中。

Q:缓冲区的刷新策略有哪些?

A:

  • 常规刷新:
    1. 无缓冲:直接将数据进行IO,如write函数
    2. 行缓冲:将换行符’\\n’之前的数据(一行的数据)进行IO,如printf,fprintf
    3. 全缓冲:直到缓冲区满,才会进行IO,如块设备:磁盘
  • 特殊刷新:
    1. 使用语言函数fflush进行强制缓冲区数据刷新
    2. 进程退出时,会清空文件缓冲区中的数据,清空的操作就是强制刷新