一.预备知识
- 文件=内容+属性
- 进程如果想要访问文件,必须先打开文件,即先将其加载到内存,这个工作由操作系统完成
- 一个进程可以打开多个文件,多个进程可以打开多个文件
- 操作系统要想对这么多打开的文件进行管理,必须“先描述,再组织”
最终结论:进程打开文件,操作系统会先将磁盘上的文件加载到内存,然后形成文件对象(struct file),用文件的属性填充这个结构体,然后以链表等数据结构将若干个struct file组织起来,这样就实现了对打开文件的管理
文件可以分为未被打开的文件和打开的文件,前者在磁盘上,后者被加载到内存,本文研究的是打开的文件和进程之间的关系
二.系统调用接口open
man 2 open
pathname:带路径的文件名,如果没带路径就在当前工作目录下查找
flags:打开方式,使用系统提供的宏,用按位或组合它们
常用的宏:
O_WRONLY:只读
O_RDONLY:只写
O_RDWR:读写
O_CREAT:文件不存在就创建
O_TRUNC:清空文件内容
O_APPEND:在文件尾部追加写入
mode:如果创建了新文件,文件的最终权限是mode和权限掩码的合成
例如:
open("log.txt", O_WRONLY | O_CREAT | O_TRUNC) 等价于fopen("log.txt", "w")
open("log.txt", O_WRONLY | O_CREAT | O_APPEND) 等价于fopen("log.txt", "a")
三.文件描述符fd
open的返回值是什么?
操作系统管理众多的struct file以达到管理打开文件的目的,操作系统如何得知哪些文件是哪个进程打开的呢?
实际上,进程PCB中有一个struct files_struct,其中记录了该进程打开的所有文件。 这个结构体中有一个struct file* fd_array指针数组,叫做文件描述符表,每个存储单元可能是NULL,也可能存放着指向文件对象struct file的指针。
当一个文件被open时,操作系统形成struct file结构体,然后在进程的文件描述符表中按顺序查找,如果遇到空的存储单元,就将struct file的指针存起来,然后将数组下标返回给上层,这个下标就是该文件的文件描述符fd,进程通过fd在文件描述符表中找到struct file指针,进而访问相应的文件。
验证如下:
open返回的文件描述符依次递增,3,4,5就是文件对象指针在文件描述符表中的下标。
那么为什么不是从0开始递增呢?0,1,2放了什么呢?
四.C语言的文件指针FILE*与fd的关系
C语言程序默认打开三个文件流:标准输入流(stdin),标准输出流(stdout),标准错误流(stderr)。文件流指的就是FILE*,它是C语言操作文件的标识符。但是操作系统是只认识fd的,所以FILE结构体中封装了fd。
Linux下一切皆文件。stdin指向的FILE中封装了标准输入文件(键盘)的文件描述符0,stdout指向的FILE中封装了标准输出文件(显示器)的文件描述符1,stderr指向的FILE中封装了标准错误文件(stderr)的文件描述符2。数组的0,1,2三个下标已经被占据了,所以新的struct file*只能从3开始放了。