本文主要是基于百度文库的《Linux2.4.30内核文件系统学习(多图).doc》和360doc的《Linux内核虚拟文件系统》修改而至,其实还参考了其他的一些文档,在此就不一一列举了。原本在听到这种文章后,都没有勇气再写点文件系统方面的东西了,那些文章实在太精彩了。最后还是鼓起勇气决定把整理的资料降低了一点自己的理解写出来,主要目的是让诸位前辈瞧瞧我的理解是否正确,另外就是备忘。
1、如何描述一个文件
我们先瞧瞧一个文件在显存和c盘上是怎样描述的。每位文件起码要有一个数据结构储存该文件的信息,包括uid、gid、flag、文件厚度、文件内容储存位置的数据结构等。在Linux中这个数据结构被称为inode,原本inode中也应当包括文件名称等信息,并且因为符号链接的存在,造成一个文件可能存在多个文件名称,因而把和文件名称相关的信息从inode中提出,专门放在dentry结构中。dentry通过其成员变量d_inode指向对应的inode数据结构。如右图所示
图1
另外,inode结构中还包括了成员i_fop,其类型是structfile_operations,其中包括的针对该文件的一些操作插口,如上图所示。
2、根据路径名找寻目标文件
在Linux中目录也被作为文件看待,只是目录是一种比较特殊的文件。其特殊之处在于文件的内容是该目录中文件和子目录的dentry的描述符,通过这种dentry的描述符可以找到文件或子目录的dentry,从而找到相应的inode。
下边我们瞧瞧若果按照绝对路径找寻一个文件/tmp/temp/abc的。
1、首先找到根文件系统的根目录文件的dentry和inode
2、由这个inode提供的操作插口i_op->lookup(),找到下一层节点‘tmp’的dentry和inode
3、由‘tmp’的inode找到‘temp’的dentry和inode
4、最后由‘temp’的inode找到‘abc’的dentry和inode
可以看见,整个找寻过程是一个递归的过程。
我们再瞧瞧怎样通过相对路径找寻文件/tmp/temp/abc,如果我们目前的工作目录为/tmp/temp/dir_a中,例如我们通过拷贝命令拷贝该文件:cp../abc./
怎样通过相对路径找寻文件呢?我们来瞧瞧dentry这个数据结构的成员,其中有一个是d_parent,数据结构定义如下
structdentry{删掉了无关的成员
structdentry*d_parent;/*parentdirectory*/
structinode*d_inode;/*Wherethenamebelongsto-NULLis*negative*/
unsignedchard_iname[DNAME_INLINE_LEN];/*smallnames*/
d_parent指向了本目录的父目录的dentrylinux命令chm,这样就在通过“..”时就是通过该表针找到的父目录dentry,找到父亲inode,从而找到父目录下的所有文件的信息。
3、进程中打开的文件
一个文件可以被多次打开,但是多个进程对一个文件的访问权限可能不同,因而打开方法都会不同(只读、读写、可执行)。而dentry和inode只能描述一个数学的文件,难以描述“打开”这个概念。
因而有必要引入file结构,来描述一个“被打开的文件”。每打开一个文件,就创建一个file结构。
file结构中包含以下信息:
打开这个文件的进程的uid,pid
打开的方法
读写的方法
当前在文件中的位置
实际上,打开文件的过程正是构建file,dentry,inode之间的关联的过程。如右图
图2
在进程中怎样和打开的文件相关联呢?我们来瞧瞧进程的数据结构
structtask_struct{只保留了相关信息
structfiles_struct*files;/*openfileinformation*/
每位进程包括“files”成员,其类型为files_struct。如右图所示
图3
进程中所有打开的文件的表针都存在了fd_arrary[]链表中。
4、虚拟文件系统
Linux通过虚拟文件系统(VFS)来支持不同的具体的文件系统,这么VFS究竟是哪些?
从程序员的角度看,VFS就是一套代码框架(framework),它将用户与具体的文件系统隔离开来。每位要通过mount命令挂接到Linux系统的储存设备linux是什么,如c盘、光盘等(它们各自对应具体的文件系统),每位设备对应的文件系统都要根据VFS的要求提供一套统一的插口。这样,用户就可以使用这种统一的插口在不同的文件系统中拷贝数据了。参考右图
图4
安装一个文件系统,不仅须要“被安装设备”外,还要指定一个“安装点”。“安装点”是早已存在的一个目录节点。比如把/dev/sda1安装到/mnt/win下,这么/mnt/win就是“安装点”。
但是文件系统要先安装后使用。因而,要使用/mnt/win这个“安装点”,必然要求它所在文件系统已也经被安装。
也就是说,安装一个文件系统,须要另外一个文件系统早已被安装。
这是一个鸡生蛋,蛋生鸡的问题:最顶楼的文件系统是怎样被安装的?
答案是,最顶楼文件系统的时侯是被安装在“根安装点”上的,而根安装点不属于任何文件系统,它对应的dentry、inode是由内核在初始化阶段陡然构造下来的。
最顶楼的文件系统称作“根文件系统”。Linux在启动的时侯,要求用户必须指定一个“根设备”,内核在初始化阶段,将“根设备”安装到“根安装点”上,因而有了根文件系统。这样,文件系统才算打算就绪。随后,用户就可以通过mount命令来安装新的设备。
5、mount设备(文件系统)
我们通过mount命令向Linux系统mount了一个设备。虽然该命令触发了两个过程,一个是文件系统注册过程(其实,假如文件系统已注册过的话,就不须要该步骤了),另一个才是真正意义上的mount设备的过程。
文件系统注册过程
Linux内核是可加载的,许多模块式可选的,只有真正须要使用时才加载她们。文件系统注册过程就是把对应某类型文件系统相关的模块加载到内核,并创建相关的数据结构。每位文件系统模块都有一个初始化解释器,它的作用就是VFS中进行注册,即填写一个称作file_system_type的数据结构。所有已注册的文件系统的file_system_type结构产生一个数组,我们把这个数组称为注册数组。
图5
每位设备在mount时都要搜索该注册数组,选择适宜自己设备文件系统的一项,并从中取出read_super()函数获取设备的超级块(储存在具体设备上,记录储存设备各类信息的一个储存块),并解析其内容。由于每种类型文件系统的超级块的格式不同,但是各自有特定的信息,每种文件系统必须使用对应的解析函数,否则内核就由于不认识该文件系统而未能完成安装。这就是注册文件系统的意义所在。
设备真正mount过程
总体数据结构,参考右图
图6
1、创建一个设备的vfsmount
2、为“被安装设备”创建一个super_block,并由具体的文件系统来设置这个super_block。在super_block中包含了该类型设备操作的各类插口的结构成员s_op,类型为super_operations。
3、为被安装设备的根目录节点创建dentry
4、为被安装设备的根目录节点创建inode,并由super_block->s_op->read_inode()来设置此inode
5、将super_block与“被安装设备“根目录节点dentry关联上去
6、将super_block中的s_root与“被安装设备”的根目录节点dentry关联上去
如图6所示,在linux2.4.30中有三条数组,文件系统类型结构file_system_type的数组头为file_systems,超级块结构super_block的数组头为super_blocks,挂接点结构vfsmount的数组头为vfsmntlist。
在Linux3.3.5中只有两条数组结构,文件系统类型结构file_system_type的数组头为file_systems,超级块结构super_block的数组头为super_blocks。数据结构vfsmount的结构定义还存在,但早已没有了mnt_list成员了。
6、挂接设备中查找文件的过程
下边的流程参考了linux3.3.5中的数据结构。
比如要打开/mnt/win/dir1/abc这个文件,就是按照这个路径,找到目标节点‘abc’对应的dentry《深入理解linux内核》 pdf,从而得到inode的过程。
找寻过程大致如下:
1、首先找到根文件系统的根目录节点dentry和inode
2、由这个inode提供的操作插口i_op->lookup(),找到下一层节点‘mnt’的dentry和inode
3、由‘mnt’的inode找到‘win’的dentry和inode
4、由于‘win’是个“安装点”,因而须要找到“被安装设备”/dev/sda1根目录节点的dentry和inode。“win”的dentry中有d_sb(超级块成员),d_sb中有“structdentry*s_root;”,s_root就是指向“/dev/sda1”的dentry。
5、然后由/dev/sda1根目录节点的inode负责找到下一层节点‘dir1’的dentry和inode
6、由于dir1是个“安装点”《深入理解linux内核》 pdf,因而须要利用dir1的dentry->d_sb->s_root找到/dev/sda2的根目录节点dentry和inode
7、最后由这个inode负责找到‘abc’的dentry和inode
可以看见,整个找寻过程是一个递归的过程。
完成找寻后,显存中结构如下,其中蓝色腰线是找寻目标节点的路径
原文链接: