如果你想保存你的Word资料至本地硬盘,你就会触发一个文件系统写操作。如果你想将一个
文件从本地电脑拷贝到U盘时,你会触发一次文件系统的读写过程。大家知道,为了简化用户
对文件的管理,操作系统提供了文件系统对数据资料进行了管理,文件系统是操作系统最为重
要的组成部分。一旦你想往文件系统写入数据时,一个新的IO请求就会在用户态诞生,但是,
其绝大部分的人生旅程都会在内核空间。对于不同的应用类型,IO请求的属性会大相径庭。除
了文件本身应该具备的基本属性(读写权限等)之外,我们还需要考虑文件的访问模式:异步
IO还是同步IO?对文件系统的Cache是如何控制的?应用程序和内核程序之间是如何交互的?
所以,在创建一个IO时,我们需要考虑很多这样的因素。
我们知道,当我们需要进行文件操作的时候,5个API函数是必不可少的。Create,Open,
Close,Write和Read函数实现了对文件的所有操作。Create函数用来打开一个文件,如果该文
件不存在,那么需要在磁盘上创建该文件。Open函数用于打开一个指定的文件。如果在Open
函数中指定O_CREATE标记,那么Open函数同样可以实现Create函数的功能。Close函数用于释
放文件句柄。Write和Read函数用于实现文件的读写过程。举个例子,如果用户需要对一个文
件进行写操作,那么首先调用Open函数打开想要操作的文件,函数完成之后获取所要操作文
件的句柄;然后调用Write函数将数据写入文件;最后采用Close函数释放文件句柄,结束文件
写入过程。上述过程大家应该都非常的熟悉,在上述过程中,整个系统到底发生了哪些操作
呢?
众所周知,用户态的API函数通过系统调用陷入内核。对于Open函数对应了sys_open函数例
程。该函数的主要职责是查找指定文件的inode,然后在内核中生成对应的文件对象。在Linux
中,Sys_open函数调用do_sys_open完成具体功能。在do_sys_open中通过do_filp_open函数完成
文件名解析、inode对象查找,然后创建file对象,最后执行特定文件对应的file->open函数。
Do_filp_open过程中的核心处理函数是link_path_walk。该函数完成了基本的文件路径的解析功
能,是名字字符串解析处理实现的核心。该函数的实现基于分级解析处理的思想。例如,当需
要解析“/dev/mapper/map0”字符串时,其首先需要判断从何处开始解析,根目录还是当前
目录?这个例子是从根目录开始解析的,那么首先获取根目录的dentry对象并开始分析后继字
符串。处理过程是以'/’字符为界按序提取字符串。根据规则,首先我们可以提取“dev”字
符串,并且计算该字符串的Hash值,通过该Hash值查找dentry下的inode Hash表,就可以很快
的找到/dev/目录下的inode对象。Hash值的计算是比较简单的,把所有字符对应的值累加起来
就可以得到一个Hash值。根据规则,依此类推,最后解析得到”/dev/mapper/”目录的inode对象
以及文件名字符串“map0”。到这一步为止,link_path_walk函数的使命完成,最后可以通过
do_last函数获取或者创建文件inode。如果用户态程序设置了O_CREATE标记,那么系统如果找
不到用户指定的inode,do_last会创建一个新的文件inode,并且把这些信息以元数据的形式写
入磁盘。当指定文件的inode找到之后,另一件很重要的事情就是初始化file文件对象。初始化
文件对象通过__dentry_open函数来实现。文件对象通过inode参数进行初始化,并且把inode的
操作方法函数集告诉给file对象。一旦file对象初始化成功之后,调用文件对象的open函数执行
进一步的初始化工作。
通过上述分析,整个过程看似比较复杂,涉及到dentry,inode以及file对象。其实这个模型还
是很简单的。Dentry用来描述文件目录,在磁盘上会采用元数据的方式存储在一个block中,文
件目录本身在Linux中也是一个文件。Inode描述一个具体的文件,也通过元数据的方式在磁盘
上保存。如果对一个文件系统从根目录开始往下看,整个文件系统是一颗庞大的inode树:
评论