Linux内核阅读--文件路径查找(二)
来源:互联网 发布:温职院网络课程 编辑:程序博客网 时间:2024/04/20 04:18
Linux文件路径查找的基本策略,是从查找根(一般是根目录或当前目录)开始,逐级向下查找。具体到代码中,每个查找的节点被表示为path,path的定义如下。
struct path { struct vfsmount *mnt; struct dentry *dentry;};我们可以看到,linux用挂载点和目录项来唯一表示一个路径节点。目录查找,就是不断的从父路径节点找到子目录节点的过程。在路径的查找中,可能会遇到一些跳转,比如遇到挂载点,遇到符号链接,遇到"..",就需要跳转到相应的位置继续查询。
如果没有遇到需要跳转的情况,则主要依赖dentry中保存的信息进行路径查找。dentry中的信息,一个是inode、superblock等文件系统相关的信息,这些信息可以用来从磁盘中查询子目录。除了文件系统信息,dentry也维护了一个Cache,用来存储之前查询过的目录项。
一般来说,dentry有很大的机率被同时访问。并发访问的时候,为了维护dentry结构的内部一致性,每次查找子目录项,都需要对父目录加锁。在高并发的情形下,因为'/'之类的目录很容易被访问文,锁冲突的概率比较大,路径查找的性能就会降低。
linux的解决方案是采用RCU算法,dentry的数据结构被设计为读安全的,即在不加锁的情况下读,可能读到老数据,但不会造成飞指针指之类的恶性事故。linux在每个dentry记录一个版本号,在查询之前会将这个版本号记录下来,等查询操作完成之后,再对比一个版本号有无变化,没有变化,说明这次读操作访问的数据结构是一致的,结果有效。
因为每次路径查找,往往都是对最后一个节点进行修改,最容易冲突的dentry节点往往是最不常被修改的,因此这种算法可以比较有效的解决锁冲突问题。不过这里存在一个问题,如果读操作失败该如何处理?难道要接着重试么?这种重试会不会造成死循环?
linux采用的策略是,如果某次读dentry失败,则放弃RCU策略,转为层层加锁策略。另外,假如查询需要下放到文件系统层,linux也会放弃RCU策略,转入加锁、引用计数策略。下面贴一下路径查找的核心代码,具体看一下流程。
static inline int walk_component(struct nameidata *nd, struct path *path, int follow){ struct inode *inode; int err; /* * "." and ".." are special - ".." especially so because it has * to be able to know about the current root directory and * parent relationships. */ if (unlikely(nd->last_type != LAST_NORM)) return handle_dots(nd, nd->last_type); err = lookup_fast(nd, path, &inode); if (unlikely(err)) { if (err < 0) goto out_err; err = lookup_slow(nd, path); if (err < 0) goto out_err; inode = path->dentry->d_inode; } err = -ENOENT; if (!inode) goto out_path_put; if (should_follow_link(inode, follow)) { if (nd->flags & LOOKUP_RCU) { if (unlikely(unlazy_walk(nd, path->dentry))) { err = -ECHILD; goto out_err; } } BUG_ON(inode != path->dentry->d_inode); return 1; } path_to_nameidata(path, nd); nd->inode = inode; return 0;out_path_put: path_to_nameidata(path, nd);out_err: terminate_walk(nd); return err;}walk_compoment完成正是从父目录查询子目录项的功能。我们可以看到,每次内核都会都会尝试用lookup_fast查询dentry中的缓存,看一下是否命中,如果没有命中,则会用lookup_slow下降到文件系统层进行路径查找。之后我们还可以注意到一个细节,就是当遇到符号链接的时候,内核也会调用unlazy_walk函数来终止RCU查找模式。
下面我从lookup_fast中截取一段核心代码 。
if (nd->flags & LOOKUP_RCU) { unsigned seq; dentry = __d_lookup_rcu(parent, name, &seq, nd->inode); if (!dentry) goto unlazy; /* * This sequence count validates that the inode matches * the dentry name information from lookup. */ *inode = dentry->d_inode; if (read_seqcount_retry(&dentry->d_seq, seq)) return -ECHILD; /* * This sequence count validates that the parent had no * changes while we did the lookup of the dentry above. * * The memory barrier in read_seqcount_begin of child is * enough, we can use __read_seqcount_retry here. */ if (__read_seqcount_retry(&parent->d_seq, nd->seq)) return -ECHILD; nd->seq = seq; if (unlikely(d_need_lookup(dentry))) { goto unlazy; } path->mnt = mnt; path->dentry = dentry; if (unlikely(!__follow_mount_rcu(nd, path, inode))) goto unlazy; if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT)) goto unlazy; return 0; unlazy: if (unlazy_walk(nd, dentry)) return -ECHILD; } else { dentry = __d_lookup(parent, name); }这里我们能够更清晰的看到,内核在RCU模式下会不加锁查询dentry 缓存,在非RCU模式下,则会用加锁的方式。当RCU读不成功,则读取结果不靠谱,终止当前查询流程,然后用非RCU的方式从当前进度继续查询。如果查询dentry 缓存找不到所需目录项,则会调用unlazy_walk,在当前查询流程里直接转入非RCU模式。
- Linux内核阅读--文件路径查找(二)
- Linux内核阅读--文件路径查找(一)
- linux 查找文件路径
- linux 查找文件路径
- linux查找文件路径
- linux查找文件路径
- linux查找文件路径命令
- Linux 内核阅读工具eclipse+qemu(二)
- Linux内核Makefile文件(二)
- Linux内核Makefile文件(二)
- Linux入门(二)文件查找
- Linux内核文件的查找和修改
- 字符串查找(文件路径查找)
- linux中实现文件路径的查找
- linux下查找mysql等文件路径
- 170718 Linux 文件路径切换与查找
- 内核管理设备(顺序阅读二)
- 内核源码阅读(二)进程复制
- Dwr实现自动补全功能
- 在windows下写成批处理文件(.bat)顺序执行命令
- HTTP Header 详解
- sqlplus -oracle 11g 连接问题
- Linux常见命令记录
- Linux内核阅读--文件路径查找(二)
- 求过圆心直线与圆的两个交点
- POJ2392:Space Elevator(多重背包)
- 如何选择最好的外贸企业建站的主机
- zoj 题型分类
- 跨函数使用内存问题
- ethtool - 查看网卡信息
- 实现重定向页面
- IIS错误:错误信息 401.3: 您无权使用您提供的凭据查看此目录或页