一.Linux系统的文件接口
我们先介绍下Linux系统俩种的文件接口,引出一些问题。
1.open()
我们看下说明:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
pathname: 要打开或创建的目标文件
flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags参数:
- O_RDONLY: 只读打开
- O_WRONLY: 只写打开
- O_RDWR : 读,写打开 这三个常量,必须指定一个且只能指定一个
- O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限 O_APPEND: 追加写data
mode:表示设置文件访问权限的初始值,和用户掩码umask有关。
返回值: 成功:新打开的文件描述符 ,失败:-1
2.close
int close(int fd);
作用:关闭fd文件描述符所对应的文件。
函数返回值:0成功,-1出错
二.内存级文件的管理
open函数的返回值为一个整型,那么我们是如何通过这个整型找到对应的文件执行操作的呢?
1.struct file
进程是计算机系统执行计算任务的基本单位,进程访问文件执行数据读写之前,操作系统会从外设获取相应的文件信息,在内存中建立struct file结构体对象来描述和管理文件,进程借助struct file
结构体提供的文件信息执行文件操作。
2.进程PCBstruct task_struct
与文件struct file
之间的关联
进程pcb中含有一个*files指针,指向一个file*的数组,file*指针再指向对应的struct file结构体,这样我们的进程只要有文件在数组中的下标,我们就可以访问到对应的文件。那么这个数组下标是什么呢,没错,file*的数组下标就是文件描述符fd。
因此,open函数只要返回一个文件描述符fd,我们的进程就可以通过对应的数组位置找到 struct file,其他文件接口也是如此。
3.linux下一切皆文件的理解
当我们加载硬件到内存时,操作系统也会为对应的硬件用struct file来描述,将他当成文件看待
struct file 中封装了文件的基本信息,其中也封装了文件的方法集,方法集中含有的文件方法指针又指向了具体的方法(如文件提供的读写方法)。
这样我们操作文件时可以屏蔽不同文件(不同设备)的差异 。
三.文件缓冲区
在linux中还存在文件缓冲区,他是在内存中由struct file 来维护的一块区域,当文件缓冲区满足条件后才会输入到文件中。
文件缓存区的优点:聚集数据一次拷贝,提升整体效率。
需要注意的是我们谈的是内核级的缓存区,在各种语言中也有文件缓存区,例如c语言。这样可以监视系统调用的开销,我们会先将内容填充语言提供的缓存区,满足条件后刷新到内核级缓冲区,最后再写入到文件中去。
四.磁盘的物理构造
磁盘可以简单看成是由许多盘片和磁头构成的,每一面都对应有一个磁头来读取信息。
每个盘片上的一个特定的圆环就是磁道/柱面,其中的一段圆弧就是扇区,每一个磁道和扇区都有特定的编号。
当我们查找文件时就可以用CHS定位法
这就是为什么我们可以看到磁盘工作时要旋转(找扇区)和伸缩的(找磁道)的理由。
五.磁盘级文件管理
1.磁盘的逻辑存储
物理上的磁盘是一个个环但是我们在逻辑上我们可以把它看成一个顺序存储的线:
我们可以把他看出无数扇区构成的线,这样我们只要有相应的扇区编号sector,就可以用下面的方法来找出盘片,磁道,扇区的对应位置。
需要注意的是操作系统认为一次操作一个扇区太小了(512字节),一次操作的是4kb,8个扇区,因此在操作系统的眼里磁盘是这样的
2.具体构造
文件=文件属性+文件内容,管理文件就需要管理文件属性和文件内容
由于单纯管理一个磁盘太大了,我们把磁盘先分区后分组,来进行管理,当我们能管理一个分区,就可以用相同的方法管理其他的分区。
块组的组成部分:
- SuperBlock(超级块):包含的是整个文件系统的全局信息,比如 inode 总个数、块总个数、每个块组的 inode 个数、每个块组的块个数等等。SuperBlock的存在意义是备份文件系统的重要全局信息,保证文件系统的安全性和稳定性(属于区级信息,部分组中有)
- GroupDescriptorTable(块组描述符):包含文件系统中所有块组的属性信息,与SuperBlock的作用类似。(组间信息,每个组都有)
- BlockBitmap(数据位图)和inodeBitmap (inode 位图): 用于标识对应的数据块或inode是空闲的,还是正在被使用中。
- inodeTable(inode 列表):包含了块组中所有的inode,inode与磁盘文件是一一对应的关系,inode中存储磁盘文件的文件信息和文件的数据块引索
- Data blocks(数据块):存储文件的有效数据
Data blocks 可以看成用来存储数据的数据块 :
inode Table可以看成存了许多的struct inode结构体的表格, struct inode里面存储了文件的属性(注意没有存储文件名),如大小,类型,权限等等,因此struct inode的大小是固定的。除了文件信息,inode结构体中还存了两个重要的属性:inode 编号和int block[15],前者是用来表示不同的inode,后者是用来表明该文件用了哪些数据块。
当操作系统要操作文件时,需要的只是inode 编号,通过inode编号,我们就可以找到文件的stuct inode,再通过int block[15],就可以找到对应的数据块执行操作。
那么我们是怎么获得inode编号的呢,首先任何一个文件都存在于一个目录中,目录也是一个文件,他也有文件内容,那么他的文件内容是什么呢? 目录下其他文件的文件名和inode的映射关系
因此,我们通过文件名及映射关系就可以找到inode编号,需要注意的是目录也是文件,要访问目录的内容,也需要目录的inode编号,而要目录的inode编号,我们就要访问上级目录的文件内容,这样就会一直递归,直到根目录,根目录的inode编号是确定的。比如我们要访问/temp/file1/ha.txt下的文件内容,用根目录的inode编号访问到temp的文件名和inode编号的映射关系,从而得到temp的inode编号,再这样逐层访问,找到ha.txt的inode编号。
需要注意的是我们上述的讨论都是在一个分区内,对于不同的分区inode编号都是从零开始的,这时我们要怎么通过inode编号来找呢?
这里我们就要明白,一个具有文件系统的分区,要被Linux使用,就要将这个分区进行挂载到对应的目录下 这样我们通过路径的前缀就可以知道对应的分区。
六.总结
我们从内存级文件讲到了磁盘级文件,明白了文件描述符的意义,也明白了PCB如何与struct file产生关系,也讲了文件缓存区的存在及理由,同时讲解了磁盘的物理构造及操作系统的对他的管理。相信一定会加深你对Linux操作系统文件的理解。