存档在 2015年3月

open()在Linux内核的实现(5)-符号链接目录项的处理

2015年3月20日

1.基本说明

在open()的路径查找过程中,walk_component()的作用就是对具体的目录项进行处理。它通过handle_dots()处理LAST_DOT和LAST_DOTDOT类型的目录项,通过do_lookup()处理非LAST_DOT和LAST_DOTDOT类型的目录项。当经过了do_lookup的处理后,还需要通过should_follow_link()检查当前的目录项是否是符号链接。

如果当前的目录项具备符号链接特性,那么内核将试图通过unlazy_walk()将当前的rcu-walk切换到ref-walk。因为处理处理符号链接文件会使用实体文件系统的钩子函数follow_link(),这个函数可能会引起阻塞,因此必须切换到允许阻塞的ref-walk模式。如果切换失败,则返回-ECHILD,即返回值do_filp_open()处,将重新以ref-walk方式执行打开操作;否则,walk_component()将返回1,那么调用它的link_path_walk()将通过nested_symlink()处理符号链接文件。

2.函数分析

2.1.nested_symlink()

该函数声明如下:
static inline int nested_symlink(struct path *path, struct nameidata *nd);

符号链接的处理过程并不复杂,但需要清楚link_count和total_link_count两个关键的变量的含义。这两个变量均来自当前进程描述符current中,前者表示当前符号链接连续嵌套的次数,后者表示一个路径中符号链接的总数。这两个计数均有最大限制,link_count最大不能超过MAX_NESTED_LINKS(默认为8),total_link_count最大不能超过40。

该函数的主要处理流程如下:

1.首先检查link_count是否超过了限制,如果超过限制则向上返回ELOOP错误;

2.如果一切正常,则nd和current同时更新嵌套计数器;

3.为了对符号链接文件嵌套情况的处理,接下来将进入循环处理过程。每次处理过程都首先通过follow_link()获取当前目录项所指文件,返回最后该文件路径的最后一个目录项;并且通过walk_component()更新nd;特殊情况下,当前目录项(即当前正在处理的符号链接文件的最后一个目录项)所指的文件仍然是个符号链接文件,因此walk_componet()将返回1,因此继续执行循环体,直至遇到非符号链接文件;

4.当本次的符号链接文件获取到最终的目标时,对nd和current的嵌套计数进行减一操作;

5.结束;

2.2.follow_link()

该函数声明如下:
static __always_inline int
follow_link(struct path *link, struct nameidata *nd, void **p);

该函数的作用是对这个符号链接文件所指的目标文件进行“一探究竟”,通过对目标文件进行查找操作进而更新nd。主要操作包含以下内容:

1.首先判断total_link_count是否超过了最大限制;

2.通过cond_resched()进行一次进程调度。通过walk_component()中的解释可知,只要某个目录项为符号链接文件,那么当前的walk模式肯定为ref,由于当前进程一开始处于rcu模式下,可能导致某些进程抢占失败,因此这里有必要进行一次进程调度;

3.对current进行total_link_count加一操作;

4.通过nd_set_link()对当前nd中saved_names[nd->depth]字段进行置空操作;

5.设置当前目录项的类型为LAST_BIND;

6.通过当前目录项中follow_link钩子函数进行符号链接文件的跟随操作,即将这个符号链接文件所指的目标文件路径返回至saved_names[nd->depth]中;

7.如果获取目标文件路径成功,则通过__vfs_follow_link()对这个目标文件路径进行全新的查找;

2.3.__vfs_follow_link()

该函数是对link_path_walk()的再次封装,其声明如下:
static int __vfs_follow_link(struct nameidata *nd, const char *link);

这种封装其实是合理的,因为既然对目标文件路径进行一个全新的查找工作,那可以使用前面的link_path_walk()函数,因为这个函数本身就是对路径的查找。

这里举个简单的例子(示例1)说明这种函数的调用情况。比如存在路径“/home/edsionte/files/abc/”,简称路径A,其中files目录是一个符号链接文件,其指向”/home/wang/test/exe/”目录(简称路径B,exe和test为普通目录)。如果要open路径A,那么简单的处理步骤是:

1.通过link_path_walk对路径A进行查找;

2.当walk_component()进行到files目录项时,发现其为符号链接文件,那么将通过nested_symlink()对这个目录项进行处理;

3.nested_symlink()中的follow_link()发现files指向的文件路径为路径B。为了检查路径B的合法性以及更新nd等信息,将通过__vfs_follow_link()对路径B进行查找工作;

4.调用link_path_walk()对路径B进行遍历,最终获取路径B的最后一个目录项exe;

5.nested_symlink()将执行walk_component(),并确定路径B最后的目录项test不是符号连接文件,因此退出nested_symlink();返回路径A的link_path_walk();

6.关于路径A的查找工作完毕;

特殊情况下(示例2),如果上述示例1中的路径B中,test目录同样为符号链接文件,并且指向名为C的路径“/home/tian/bin/”,其中,bin为普通目录;那么open路径A这个操作将会多一轮的处理流程。基于上述的处理流程步骤如下:

1.通过link_path_walk对路径A进行查找;

2.当walk_component()进行到files目录项时,发现其为符号链接文件,那么将通过nested_symlink()对这个目录项进行处理;

3.follow_link()发现files指向的文件路径为路径B。为了检查路径B的合法性以及更新nd等信息,将通过__vfs_follow_link()对路径B进行查找工作;

4.调用link_path_walk()对路径B进行遍历,当walk_component()进行至B中的test目录项时,由于其为符号链接文件,因此将调用nested_symlink();

5.nested_symlink()处理目录项test,由于test指向的路径C中所有目录项都是非符号链接文件,因此循环体执行一次,最终返回C的最后一个目录项bin至路径B的link_path_walk()中;

6.路径B的link_path_walk()继续进行,直至遍历到最后一个目录项exe;它将返回到3中的follow_link()中,接着执行walk_component()发现exe为普通目录项,则nested_symlink()结束;

7.路径A的link_path_walk()继续对路径A的下一个目录项abc进行walk_component();

通过上述示例可以看出,符号链接文件的嵌套数并不是指link_path_walk()的嵌套调用数目,而是指nested_symlink()的调用数。只有当符号链接文件(路径A)所指的文件(路径B)中依旧存在符号链接(指向路径C)时,才会出现nested_symlink()的嵌套。

3.总结

符号链接文件的处理方式比较特殊,但是本质的实现还是基于link_path_walk(),该函数是整个路径查找框架中的基础实现。对于一个路径来说,经过link_path_walk()的处理,nd将保存最后一个目录项的信息,但是最后一个目录项代表的文件(或目录)是否存在并不知晓,需要通过调用link_path_walk()的path_openat()做最后的处理。

参考资料:

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/;

open()在Linux内核的实现(4)-普通目录项的处理

2015年3月3日

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/;

windows 7 ultimate product key

windows 7 ultimate product key

winrar download free

winrar download free

winzip registration code

winzip registration code

winzip free download

winzip free download

winzip activation code

winzip activation code

windows 7 key generator

windows 7 key generator

winzip freeware

winzip freeware

winzip free download full version

winzip free download full version

free winrar download

free winrar download

free winrar

free winrar

windows 7 crack

windows 7 crack

windows xp product key

windows xp product key

windows 7 activation crack

windows7 activation crack

free winzip

free winzip

winrar free download

winrar free download

winrar free

winrar free

download winrar free

download winrar free

windows 7 product key

windows 7 product key