一般看来,文件分为未被打开的文件和打开的文件。通常未被打开的文件都在磁盘上,当一个进程打开某一个文件时,该文件就会被加载到内存,被操作系统所管理。
而为了管理这些文件,这些被打开的文件在内核中都会有一个结构体(struct file),里面存放了该文件的各种属性信息,如大小,在磁盘中的位置,被哪个进程所打开的等等。
实际上,某一个进程可能会打开多个文件,二者是1:n的关系。这些被打开的进程都会有一个结构体来存放自身属性,以便操作系统来管理,而这些结构体彼此间通过双链表相互链接。所以说,当我们要对文件进行各种操作时,就会访问该结构体的有关属性并进行修改;有时要打开或删除一个文件时,其本质就是对这个链表进行增删查改的操作。
我们知道进程为了便于管理,也会有一个结构体(task_struct),既然文件是被进程所打开,那么进程的结构体与文件的结构体之间就应该会存在某种关系。事实也确实如此,进程结构体中有一个结构体指针struct file_struct * file,其指向名为struct file_struct 这个结构体,在该结构体中,有一个指针数组,下标分别为0,1,2…。进程每打开一个文件,都会将该文件的struct file的地址填入该数组,二者就此建立关系。
以Linux centos7为例,打开文件的底层调用接口open
可以看到返回的是一个int型的整数,而该整数就是上述所说的打开的文件在文件描述符表中的下标。
可以通过代码验证
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 #include <stdio.h>
5
6 int main()
7 {
8 int fd = open("log.txt", O_CREAT);
9 printf("fd:%d\n",fd);
10 return 0;
11 }
结果如下:
为什么是3呢?这是因为一个进程在创建时,会默认打开3个数据流,分别是标准输入(stdin),标准输出(stdout)和标准错误(stderr)。在Linux下一切皆文件,所以相当于打开了三个文件,占据了数据描述符表的前3个。所以我们在新建一个文件时一般下标都是从3开始。
在Linux操作系统底层只通过该下标来对文件进行操作,如下
write函数:
close函数:
read函数:
现在知道了底层原理,回想起我们先前学习C语言时写的文件代码
FILE* fp = ("log.txt", "a");
其中FILE就是C封装的一个库,我们暂时还不知道里面其他的内容。但是我们现在肯定了其内部肯定封装了我们上面所提到的fd。