三:代码流程

1,path_lookupat()函数首先调用path_init()函数,path_init()函数主要是初始化查询,将nd实例的mnt和dentry成员设置为根目录或者工作目录的对应项

    a,绝对路径(以/开始),获得根目录的dentry。它存储在task_struct中fs指向的fs_struct结构中。task_struct->fs_struct.root  
    b,相对路径,直接从当前进程task_struct结构中的获得指针fs,它指向的一个fs_struct,fs_struct中有一个指向“当前工作目录”的dentry。

 
2,path_lookupat()然后调用link_path_walk()函数,static int link_path_walk(const char*name,struct nameidata *nd),link_path_walk()函数判断传进去的路径名如果是'/'则直接返回0,然后调用may_lookup()函数对inode节点做权限检查,如果权限不够也直接 返回fail,在Unix中,只有目录是可执行的,它才可以被遍历。


3, path_lookupat()然后调用walk_component()函数,该函数作用是根据分量名找打对应的path,如果是符合链接则调用nested_symlink()函数处理符合链,walk_component()首先跳过‘.’和‘..’,如果是‘.’则直接跳过,如果是‘..’则要考虑父目录是否是另外一个挂载点,如果是的话可能要切换文件系统。

 

4,walk_component()函数执行真正的dentry缓存的查找,在dentry_hashtable中查找@this的名字,找不到则使用inode的inode_operations->lookup中的操作从底层设备载入进来,这个哈希表用父目录的dentry和当前目录名字的hash值也就是this->hash作为键值。而且这个函数还处理目录的权限以及目录是装载点的情况,由于一个目录下可以装载多个文件系统,最新装载的文件系统隐藏以前的装载,若是装载点,则顺着装载点一直查找,直到最上层的装载点也就是当前可以看到的文件系统,当这个函数返回1,则表示这个目录是符号链接,下面进行特殊处理。函数调用成功则 @nd->path 表示this.name这个名字所表示的目录,也是就当前解析成功的目录,然后下一次循环解析下一个部分时候,这个目录就当做父目录在dentry缓存中查找,直至所有的部分全部完成


   walk_component()函数首先作以下判断if (unlikely(type != LAST_NORM)),如果当前要处理的是‘.’或者‘..’则调用handle_dots()函数处理,处理目录是'.'和'..’的情况,'.'很好处理,直接跳过就可以了,'..'稍微麻烦,因为当前目录有可能是一个装载点,跳到上一级目录就要切换文件系统。如果要处理的不是'.'和'..'则会调用do_lookup()函数,该函数这个从dentry缓存中查找,找不到就从底层设备中找,并且会处理装载点的情况,如果是一个符号链接,则会调用nested_symlink()函数,


   do_lookup()函数首先调用__d_lookup()函数,在dentry_hashtable,以父dentry实例地址和name(要解析的分量)的hash为key查找对应的dentry实例,如果在缓存中找到相应的实例,主要调用__d_lookup()函数,即使找到,也不保证它是最新的,必须调用底层文件系统的dentry_operations中的d_revalidate()函数来检测缓存项是否任然有效,如果有效则作为缓存搜索结果返回。如果在dcache缓存中没有找到要找的dentry实例,则重新加载,调用lookup_hash()函数,lookup_hash()函数首先调用lookup_dcache()函数在缓存中生成新的dentry实例,然后调用lookup_real()函数,它是具体的文件系统相关函数,读取磁盘的inode节点信息,并将inode节点和目录项对象相关联,在iget索引节点时,将索引节点加入inode cache, 在关联inode节点时,将目录项对象加入了dentry cache,如果找到的路径分量对应的path是挂载点,则找到该挂载点上的最后的子系统
 

   如果最近解析的目录不是已安装文件系统的根目录,那么必须回到父目录:把nd->dentry置为nd->dentry->d_parent,在父目录上调用follow_mount(),继续下一个分量,follow_mount()函数检查nd->dentry是否是某文件系统的安装点(nd->dentry->d_mounted的值大于0);如果是,则调用lookup_mnt()搜索目录项 高速缓存中已安装文件系统的根目录,并把nd->dentry和nd->mnt更新为相应已安装文件系统的安装点和安装系统对象的地址;然后重复整个操作(几个文件系统可以安装在同一个安装点上)。从本质上说,由于进程可能从某个文件系统的目录开始路径名的查找,而该目录被另一个安装在其父目录上的文件系统所隐藏,那么当需要回到父目录时,则调用follow_mount()函数。

  

5,nested_symlink()函数处理符合链接,一个目录最大包含的归链接的次数为8,最终主要通过follow_link()函数完成符号链接的解析。

 

四:相关问题:
1,如何防止目录的寻找陷入无终止的循环

答:内核允许8个递归和40个连续的链接

进程的task_struct结构中有个计数器total_link_count.在搜索过程中有可能碰到一个节点(目录项)只是指向另一个节点的链接,此时就用这个计数器来对   链的长度进行计数,这样,当链的长度达到某一个值时就可以终止搜索而失败,返回,以防陷入循环。内核允许一个路径中最大的symbol links数目为40,另一方面,当顺着符号链接进入另一个设备上的文件系统   时,有可能会递归地调用path_walk。所以,进入path_walk后,如果发现这个计数器值非0,就表示正在顺着符号链接递归调用path_walk往前搜索过程中,   此时不管怎样都把LOOKUP_FOLLOW标志位设成1

 

2,pathwalk举例分析:
    假设把aufs文件系统挂载到了/home/mnt/au,我们要查找的文件是/home/mnt/au/ woman_star/lbb。具体步骤如下:
步骤 1:首先是根据分隔号得到home目录,然后计算home的hash值,调用do_lookup。
步骤 2:这样就获得了home文件(目录文件)的inode,因为 home的inode无follow_link调用,最终调用path_to_nameidata。
步骤 3:对mnt目录用步骤1和步骤2查找。
步骤 4:查找au;因为au是个挂载点,在do_lookup函数需要根据两个文件系统的挂载点解析,解析后,父目录就换成了aufs的根目录。。
步骤 5:对woman star目录用步骤1和步骤2查找。
步骤 6:最后的目标文件lbb由last_component分支处理。在woman_star目录可以找到。

posted on 2014-11-01 22:11  知了112  阅读(1680)  评论(0编辑  收藏  举报