在
上一篇里面我们以打开"/home/gaobsh/a.txt"为例分析了系统调用"open"的内核实现。其中我们假设了路径名中所有部分的dentry结构体均已存在在系统缓存中。即通过
lookup_fast()函数成功找到目标文件的dentry。这里我们分析一下如果上述路径名某一部分如果不能通过
lookup_fast()函数查找到的情况。这里还以
上一篇里面的例子进行分析。在
walk_component()函数通过调用
lookup_fast()函数如果失败,则进入
lookup_slow()函数。在
lookup_slow()函数为目标文件创建一个新的dentry,并加入到系统缓存中。现来分析其具体过程。例如假设文件夹“gaobsh”尚不存在在系统缓存中(虽然这种情况几乎不存在。。。)。
lookup_slow()函数在本示例中定义等价与如下(在这里我们仍然不考虑函数调用失败和进程同步等问题):
static struct dentry *lookup_slow(const struct qstr *name,
struct dentry *dir,
unsigned int flags)
{
struct dentry *dentry = ERR_PTR(-ENOENT), *old;
struct inode *inode = dir->d_inode;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
inode_lock_shared(inode);
/* Don't go there if it's already dead */
if (unlikely(IS_DEADDIR(inode))) /* if 判断为 0 */
...
again:
dentry = d_alloc_parallel(dir, name, &wq);
if (IS_ERR(dentry)) /* if 判断为 0 */
...
if (unlikely(!d_in_lookup(dentry))) { /*不考虑并发性问题, if 判断为 0 */
...
} else {
old = inode->i_op->lookup(inode, dentry, flags); /* 一般情况下,返回NULL */
d_lookup_done(dentry);
if (unlikely(old)) { /* if 判断为 0 */
...
}
}
out:
inode_unlock_shared(inode);
return dentry;
}
先来分析其中的d_alloc_parallel()函数。在本例中,该函数定义等价与如下,(该函数涉及很多并发性问题的考虑,参考这篇文章):
struct dentry *d_alloc_parallel(struct dentry *parent,
const struct qstr *name,
wait_queue_head_t *wq)
{
unsigned int hash = name->hash;
struct hlist_bl_head *b = in_lookup_hash(parent, hash);
struct hlist_bl_node *node;
struct dentry *new = d_alloc(parent, name);
struct dentry *dentry;
unsigned seq, r_seq, d_seq;
if (unlikely(!new)) /* if 判断为 0 */
...
retry:
rcu_read_lock();
seq = smp_load_acquire(&parent->d_inode->i_dir_seq) & ~1;
r_seq = read_seqbegin(&rename_lock);
dentry = __d_lookup