都说linux的哲学是“一切皆文件”,c盘文件是文件,目录是文件,管线是文件,socket套接字也是文件,这为开发者提供了很大方便,开发者对于文件只须要使用统一的read,write操作就可以与这种特殊的文件交互。
这就使用到了Linux的VFS虚拟文件系统(virtualFilesystem),这儿的VFS相当于一个垫圈,VFS为所有方式的文件,目录等向用户程序提供了统一的插口以及访问方法,促使用户程序毋须考虑底层的文件系统linux社区,只须要统一的操作就可以完成对文件的访问。
LinuxVFS相当于一个内核对用户态曝露的一个插口,用户不用关心自己所要访问的具体文件系统是哪些,只须要使用插口中的方式,就可以完成对特定文件系统的访问,这样的设计在用户层面做到了统一。同时,VFS与常说的插口也并不相同,一般一个实现类要实现插口,就须要实现插口中定义的所有方式,并且对于文件系统并不尽然linux 文件系统分区,具体文件系统可以对不须要的操作不予实现。
01
文件系统分类
假如把LinuxVFS支持的文件系统分类,大约可以分为三类
c盘文件系统
这些文件系统用于管理c盘中的文件,如Ext2/3,overlay等
网路文件系统
这些文件系统可以使实现在本地访问远端的计算机的文件,如常见的NFS文件系统,SMB等
特殊文件系统
这些文件系统不管理本地或则远端c盘空间,不须要c盘分配空间,并且占用显存,如proc文件系统,sysfs,sockfs等
02
操作系统角度看文件
Linux文件系统会为每位文件都分配两个数据结构,索引节点(inode)和目录项(dentry)。
索引节点
拿来记录文件的元数据,如文件编号、文件大小、类型、创建文件的用户的标示符、上一次访问的时间和上一次更改的时间,文件的储存位置等信息,我们可以用stat命令查看,如右图查看一个socket套接字的inode信息。
索引节点与文件是一一对应的关系,注意,inode是储存在c盘上的,占用c盘空间的,只有在访问的该文件时侯才能创建一个inode结构体副本。
目录项
拿来记录文件的名子,索引节点的表针,以及和其他目录项的关联关系。
一般情况下找到一个文件是通过文件的路径来找的,而不是直接通过inode编号。例如,我们要访问一个/usr/a.txt文件并对他操作,系统会解析文件路径,首先是“/”根目录,之后找到a文件夹目录linux 分区,最后找到a.txt文件,这儿的每一次找寻找寻到的都是dentry,最终找到a.txt的dentry结构体,结构体上面d_inode数组对应着a.txt的inode,在这寻觅过程中,每一个目录也是一个文件,也有自己对应的inode,这可能有点乱。可以看右图理解dentry与inode的关系。不过,不同于inode存在于块设备中,dentry是内核维护的一个显存数据结构,一般被称作目录项缓存(dcache)。
该图画的不确切,因为每位文件夹的子目录并不止一个,所以这儿的detry的d_child数组是该文件子目录的一个数组。为了易于看出联系,本图这儿直接指向了一个子目录。
因为Linux中存在硬链接,所以一个inode可能会有多个denery
除此之外,还有个很为关键的概念,超级块对象
超级块对象
超级块对象,是对一种文件系统的描述,反映了文件系统的整体控制信息,每种文件系统必须实现超级块对象,对于c盘文件系统来说,该对象存在于c盘的某块区域,当被挂载的时侯,会被读到显存中。对于非c盘的文件系统,会在现场创建并加载到显存中。所有的超级块对象都通过一个单向循环数组联接上去。因为挂载路径的不同,可能同一个文件系统类型如(nfs)在一台机器上对应了几个超级块。
超级块中的一个重要成员是数组,包含所有更改过的inode,也就是脏inode,可以使用该数组很容易分辨下来那个文件被更改过,并配合内核线程将数据写回c盘,还有一个重要对象是s_op,定义了超级块对象针对其对应inode的所有操作方式,例如分配,释放,标记索引节点等一系列操作。
03
进程角度看文件
对于进程来说,自己打开的文件都使用fd文件描述符表示,也就是文件描述符。不同的进程可以拥有相同的文件描述符,文件描述符使用整数表示,我们可以在/proc/$(PID)/fd中见到某个进程打开的所有文件描述符。
通常而言,每位进程启动就会占用三个文件描述符0到2,0代表标准输入,1代表标准输出,2代表形成错误的标准输出。如右图。
然而进程见到的文件描述符是如何与上述的文件对应上去的呢?
每位进程都有自己的task_struct结构体,该结构体记录了进程的所有执行信息,也包括打开的文件,具体表现为fs数组和files数组。
struct task_struct {
......
struct fs_struct *fs;// 文件系统信息
struct files_struct *files; // 打开文件信息
......
}
进程打开的文件都被file_struct结构体所保存,进程以及用户听到的整数进程描述符,是file_struct结构体中的数组fd_array链表的索引。
struct files_struct {
......
struct fdtable __rcu *fdt; // 指向其他fd表的指针
struct fdtable fdtab; // 基fd表
spinlock_t file_lock ____cacheline_aligned_in_smp;
int next_fd; // 已分配的文件描述符加1,表示缓存下一个可用的fd
struct file __rcu *fd_array[NR_OPEN_DEFAULT];// 文件对象指针的初始化数组
......
}
链表中的每位元素代表一个文件对象示例
struct file {
......
struct path f_path; //文件的路径,dentry等信息
struct inode* f_inode;//文件的inode信息
const struct file_operations*f_op;//文件的操作集合,如read,write等
......
}
这个文件示例中对应了文件的路径,dentry信息,文件的操作集合,以及文件的inode信息等。不同的文件系统对应的f_op不同,
当我们对文件进行操作时,会将操作转化为具体文件系统的实现方法。
看一下LinuxVFS定义的通用的file_operations结构。
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
int (*open) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
......
};
每位文件系统就会有自己的函数集实现,如ext3文件系统,当系统调用file_operations里的函数时侯,会直接对应到自己的实现方法。
const struct file_operations ext3_file_operations = {
.llseek = generic_file_llseek,
.read = do_sync_read,
.write = do_sync_write,
.open = generic_file_open,
......
};
如NFS文件系统,当我们步入挂载NFS的目录中,执行cat命令查看一个文件时侯,也是通过read系统调用,对应到NFS文件系统上对应的file_operations中read实现方法,再通过底层TCP合同实现文件内容从服务器到顾客端(本机)的传输。
接出来用一个简单示例看下文件描述符是如何对应具体文件的。
示例
如右图,三个进程打开同一个的文件,进程1和2使用的同一个硬链接
每位进程都有自己的文件fd指向了自己的文件对象,因为进程12对应的路径相同,所以对应的目录项对象(dentry)也相同,因为是同一个文件,所以在目录项对象(dentry)中指向的索引节点对象(inode)相等,该inode对象可以通过i_sb数组找到自己对应的超级块对象,也可以找到自己在c盘的位置,超级块对象也可以找到c盘中自己的位置。
缓冲区缓存
因为对c盘的IO读写很慢,所以在实际的IO过程中,都是将c盘的数据读取到显存页(pagecache即下文的cache)中,最后用户进程操作的是显存,而不是c盘,最终通过内核线程同步,或则用户进程执行fsync系系统调用刷入c盘。
内核对文件提供了两种缓存机制,buffer和cache,buffer用于对块设备文件(即c盘等硬件)的缓存,cache用于对文件系统的缓存。在读写普通文件时侯,恳求会经过文件系统来与c盘或其他介质交互,这时侯用的是cache缓存,而在直接读写c盘硬件时侯,用的则是buffer缓存。
04
结束语
Linux系统通过VFS的方式对文件进行统一管理,程序员可以不用关心每种方式的文件系统的底层实现,直接使用统一的系统调用方式就可以完成IO等操作,本文主要对Linux文件系统分类,文件的具体表现方式与进程与文件的对应关系展开描述,并且对于Linux文件系统概貌来说还是冰山一角,如有不足之处或疑惑linux 文件系统分区,欢迎后台留言一起阐述。
05
参考资料