1.基本说明
在open()的路径查找过程中,walk_component()将对路径中的每个目录项进行遍历,当目录项为普通目录项时,将通过do_lookup()对其进行查找。在路径查找过程中,普通目录项算是比较常见的,因此更应该采取高效的方法节省查找时间。
为了提高查找效率,内核通过rcu-walk方式避免ref-walk由于锁机制而引入的阻塞问题;其次,内核通过dentry缓存的方式将目录项以哈希表的方式组织起来,以便提高查找效率。这两种方式在一定程度上可以提高路径查找的效率,不过它们并不是适合所有情况。比如,rcu-walk方式不能使用可能会引起阻塞的函数,有时候要查找的目录项并不位于缓存当中而只能从磁盘上获取信息。
由于上述提高查找效率的方式可能会查找失败,因此内核一般会经过多次目录项的查找。也就是说,内核会先进行高效的查找方式,如果在这种模式下查找失败,那么再进行传统的查找方式,保证查找工作的正常进行。具体的,内核会先进行rcu-walk方式,如果查找失败,则进行ref-walk方式;内核会优先在目录项缓存中进行快速的查找,如果查找失败,则通过访问磁盘进行慢速的查找。
2.函数实现
2.1.do_lookup()
内核通过do_lookup()进行普通目录项的查找工作。该函数声明如下:
tatic int do_lookup(struct nameidata *nd, struct qstr *name, struct path *path, struct inode **inode);
这个函数主要通过区分不同的情景来选择对应的查找方式。总体来说,先以rcu-walk方式在目录项缓存中查找,如果成功,则查找结束返回;否则,将rcu-walk切换到ref-walk模式;ref-walk模式下会先进行目录项缓存的查找,如果查找成功,则返回;否则,进行ref-walk下的磁盘查找;如果成功,则返回,否则,查找失败并返回错误码ENOENT;该错误码对应到用户态即为“No such file or directory”。
如果以rcu-walk模式进入该函数,则主要的查找步骤如下:
1.如果当前是rcu-walk模式(设置了LOOKUP_RCU),则先通过__d_lookup_rcu在目录项缓存中查找;
2.如果查找失败,则跳入unlazy标号处;如果查找成功,则继续;
3.通过follow_mount_rcu()判断当前的目录项是否为挂载点,如果是,则跨越挂载点;否则继续;
4.如果rcu-walk方式下查找缓存成功,则返回0;否则,进入unlazy标号;
5.unlazy标号是将当前的rcu-walk切换成ref-walk模式;如果切换成功,则继续;否则,返回ECHILD。即返回到do_filp_open()处,重新进行ref模式的查找;
6.当前查找模式为ref-walk,并且此时在缓存中并未找到对应的目录项;那么接下来必须试图在磁盘上进行查找了。不过在进行磁盘查找之前,还是会再次调用d_lookup()进行一次内存查找,因为retry标号下的代码有互斥锁,很可能该函数再此处会阻塞,而在阻塞阶段就目标目录项就有可能被载入内存,这样就可以省去在磁盘上查找目录项的工作;
7.如果d_lookup()查找成功,则继续;否则,通过d_alloc_and_lookup()分配并在磁盘上查找dentry;具体的,调用当前文件系统的lookup钩子函数;如果磁盘查找失败,则错误返回;否则,继续;
8.此时,已获取到要查找的目录项;则再通过follow_managed()对当前目录项进行检查,比如,检查当前目录项是否为挂载点;
9.获取当前目录项对应的inode信息;
10.返回;
如果以ref-walk模式进入该函数,则首先会通过__d_lookup()在目录项缓存中查找;如果查找成功,则接下来的步骤与rcu-walk步骤8-10相同;如果查找失败,则接下来的步骤与rcu-walk步骤6-10相同。
3.总结
普通目录项的查找工作其实并不复杂,无非是在内存中查找或在磁盘中查找。只不过,内核为了提高路径查找效率同时引入了rcu-walk方式,从而增加了代码分析的复杂度。如果当前目录项为符号链接文件,则内核的处理方式又是另一种方式,具体的实现过程可参见本系列的下一篇文章。
参考资料:
1.Linux源码3.2.69;
2.Linux系统调用open七日游:http://blog.chinaunix.net/uid-20522771-id-4419666.html
3.深入理解Linux内核:http://book.douban.com/subject/2287506/;
4.深入Linux内核架构:http://book.douban.com/subject/4843567/;
5.Linux内核探秘:http://book.douban.com/subject/25817503/;