操作系统如何访问文件
文件可按照打开状态分为,被进程打开的文件(内存文件)和未打开文件
被进程打开的文件被加载到了内存,没打开的文件在磁盘上
操作系统要管理打开的文件,管理方式:先描述,再组织。操作系统用struct file进行管理文件,里面包含了文件的所有内容,包含属性,但文件的属性又来自哪?由于文件没有被打开时在磁盘上,文件=内容+属性,struct file里文件的属性就来源于磁盘。
进程和文件通过指针数组来进行维护,文件描述符的本质是数组下标
task_struct中包含一个结构体指针,指针指向已打开的文件信息
files_struct结构体,该结构体有很多内容,其中有一个fd_array指针数组,数组默认大小为32或64
struct file里面有很多文件属性
操作系统是这样来访问文件的,fd_array也可以称为文件映射表/文件描述符表
文件描述符本质:数组下标
当用fopen打开文件时,底层调用open,open打开后会得到一个文件描述符,文件描述符被封装成FILE ,以FILE*的方式返回给fopen供用户去使用。
当用户开始fwrite的时候,传进来了一个FILE*(fopen的时候获得FILE*,fopen调用open获得fd,把fd放到FILE*里面),FILE*里面包含fd,并且FILE*里面封装了write,调用write之后就进入到了操作系统内部,然后自己执行了操作系统内部的write方法,执行write方法之后就找到了进程的task_struct,找到task_struct之后就找到了指针,然后找到files_struct,然后找到了fd_array[],这个数组再配合刚才传进来的fd,就找到了struct_file
文件描述符的分配规则
Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误。
0,1,2对应的物理设备一般是:键盘,显示器,显示器。
而现在知道,文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。
于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。
每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数
组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的标。
所以,只要拿着文件描述符,就可以找到对应的文件
我们关掉0
此时fd是0
修改一下,close(0)改为close(2)
我们可以看到此时fd是2,这是因为fd的分配规则是:fd会找到最小的,没有被占用的文件描述符。
此时我们把1关掉
此时不打印,此时fd一定是1,没打印这是因为1是标准输出流。标准输出流是往显示器打印,我们此时关掉了1,所以不往显示器打印了
注释掉关闭文件
注释掉后,内容打印到了log.txt中,这是因为打印的时候printf默认往stdout打(因为1里被stdout占领),stdout是file结构体,这个结构体里fileno默认为1,我们关闭了1,fd便去占领了1,这个fd是打开了log.txt的返回值,也就是1此时对应的是log.txt,printf只认1,而1此时被log.txt占领,所以打印到了log.txt,这种现象叫做输出重定向。常见的重定向有:>, >>, <
printf和fprintf这些应该都往显示器打印(标准输出),关闭1之后,打印到了log.txt中,这是因为1此时被log.txt占领。
当系统启动时,系统会默认打开三个文件,Linux一切皆文件键盘,显示器,显示器,系统会给这三个创建struct file对象,这三个文件分别是键盘,显示器,显示器。把这三个对象的地址填入0(标准输入流),1(标准输出流),2(标准错误流)所对应的指针数组中。在语言层面我们只在下标为1的地址里打印,如果1(默认为显示器)被改为了文件,我们则会把数据打印在文件中。0也是如此如果把0所在下标的内容改为其它文件地址,标准输入流此时就是这个文件,直接获取文件内容
重定向的本质其实是,在操作系统内,更改fd对应的内容指向
从指定的流 stream 读取一行,并把它存储在 buffer所指向的字符串内
此时我们输入什么,它打印什么
只读
稍作修改 ,此时关闭了0,给log.txt分配了0,也就是说log.txt此时成了标准输入流
fgets从log.txt获取了字符串,这就叫输入重定向
重定向
man dup2,把oldfd拷贝给newfd,拷贝的不是fd这个数字,而是指针数组里面的内容
此时如果要输出重定向,就是把myfile的地址放到下标为1的格子,此时应该
dup(3,1)
此时直接打印10086
.
输出重定向
把下标为fd的元素内容(log.txt的地址),拷贝给下标为1的元素
追加重定向,删掉O_TRUNC(清空)加上O_APPEND即可
此时没有close(fd)
添加close(fd),此时却什么都没打印,这是因为有缓冲区存在
dup2具有刷新缓冲区的操作
如何理解一切皆文件
Linux的设计哲学,体现在软件设计层上,Linux是用C语言写的。
用C语言实现面向对象,让结构体里面包含成员方法,使用函数指针,底层不同的文件,对应着不同的操作方法,下面这些设备都是外设,所以每一个设备的核心访问函数,都可以是read,write(这俩个代表I/O),所有的设备都可以有自己的read和write,但是,代码的实现,一定是不一样的,这种方式下硬件没有任何的差别,看待所有的文件的方式都统一成了struct file