Linux虛擬文件系統--文件路徑名的解析(3)--普通文件名

對於一個文件路徑的分量,如果其不爲'.'和'..'則屬於普通文件名,普通文件名的解析由do_lookup()函數來處理

static int do_lookup(struct nameidata *nd, struct qstr *name,
             struct path *path)
{
    struct vfsmount *mnt = nd->path.mnt;
    struct dentry *dentry = __d_lookup(nd->path.dentry, name);//查找name對應的dentry
 
    if (!dentry)//dentry不存在,跳轉至need_lookup
        goto need_lookup;
    /*如果底層文件系統中定義了d_revalidate函數,則要判斷目錄項是否有效,以保證一致性,
      該函數是針對於網絡文件系統存在的*/
    if (dentry->d_op && dentry->d_op->d_revalidate)
        goto need_revalidate;
done:
    path->mnt = mnt;
    path->dentry = dentry;
    /*這裏由於path往下走了一層,因此要調用__follow_mount()判斷dentry對應的目錄下是否掛載了其他的文件系統,
    以保證對應的mnt是正確的*/
    __follow_mount(path);
    return 0;
 
need_lookup:
    /*沒有找到name對應的dentry,則要創建新的dentry並從磁盤中讀取數據保存在dentry中*/
    dentry = real_lookup(nd->path.dentry, name, nd);
    if (IS_ERR(dentry))
        goto fail;
    goto done;
 
need_revalidate:
    dentry = do_revalidate(dentry, nd);
    if (!dentry)
        goto need_lookup;
    if (IS_ERR(dentry))
        goto fail;
    goto done;
 
fail:
    return PTR_ERR(dentry);
}
 

可以想象,搜索一個文件(目錄)時,首先肯定要在dentry緩存中查找,當緩存中查找不到對應的dentry時,才需要從磁盤中查找,並新建一個dentry,將磁盤中的數據保存到其中。找到了目標dentry後,就將相應的信息保存到path中,這裏因爲路徑向下進了一層,因此要判斷下層目錄是否有新的文件系統掛載的問題,和上文討論的類似,因此要通過__follow_mount()函數判斷是否有文件系統掛載在該目錄下,另外,對於網絡文件系統,還要通過文件系統中定義的d_revalidate()函數來判斷該dentry是否有效以保證一致性。

先來看看在dentry緩存中查找的過程

struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)
{
    unsigned int len = name->len;
    unsigned int hash = name->hash;
    const unsigned char *str = name->name;
    struct hlist_head *head = d_hash(parent,hash);//通過parent的地址和hash(hash是name的哈希值)進行定位
    struct dentry *found = NULL;
    struct hlist_node *node;
    struct dentry *dentry;
 
    rcu_read_lock();
    
    hlist_for_each_entry_rcu(dentry, node, head, d_hash) {//掃描head對應的碰撞溢出表
        struct qstr *qstr;
 
        if (dentry->d_name.hash != hash)//name的hash值不相符,則放棄該dentry
            continue;
        if (dentry->d_parent != parent)//父目錄不一樣,則放棄該dentry
            continue;
 
        spin_lock(&dentry->d_lock);
 
        /*
         * Recheck the dentry after taking the lock - d_move may have
         * changed things.  Don't bother checking the hash because we're
         * about to compare the whole name anyway.
         */
        if (dentry->d_parent != parent)
            goto next;
 
        /* non-existing due to RCU? */
        if (d_unhashed(dentry))
            goto next;
 
        /*
         * It is safe to compare names since d_move() cannot
         * change the qstr (protected by d_lock).
         */
         /*當確保了父目錄和文件名的哈希值與目標dentry的一致性後,接下來就只用匹配文件名了*/
        qstr = &dentry->d_name;//取當前dentry的文件名
        /*如果父目錄文件系統定義了比較文件名的方法,則調用之*/
        if (parent->d_op && parent->d_op->d_compare) {
            if (parent->d_op->d_compare(parent, qstr, name))
                goto next;
        } else {//如果沒定義
            if (qstr->len != len)//先確定長度是否相等
                goto next;
            if (memcmp(qstr->name, str, len))//再比較內存
                goto next;
        }
 
        atomic_inc(&dentry->d_count);
        found = dentry; //這裏表明找到了目標dentry
        spin_unlock(&dentry->d_lock);
        break;
next:
        spin_unlock(&dentry->d_lock);
     }
     rcu_read_unlock();
 
     return found;
}

 

d_hash()函數將父目錄dentry的地址和所要查找的文件名的哈希值組合起來,重新構建一個哈希值,並根據其定位到
dentry_hashtable哈希表中,dentry_hashtable是dentry緩存的一部分,所有的dentry都會保存在dentry_hashtable中,這樣一來,就得到了一個哈希表的溢出鏈表的表頭,即代碼中的head變量。下面的工作就是掃描這個鏈表,並從中查找真正的目標。

 

如果在dentry_hashtable中沒能找到目標dentry,則通過real_lookup()函數從磁盤中查找

static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
{
    struct dentry * result;
    struct inode *dir = parent->d_inode;
 
    mutex_lock(&dir->i_mutex);
    /*
     * First re-do the cached lookup just in case it was created
     * while we waited for the directory semaphore..
     *
     * FIXME! This could use version numbering or similar to
     * avoid unnecessary cache lookups.
     *
     * The "dcache_lock" is purely to protect the RCU list walker
     * from concurrent renames at this point (we mustn't get false
     * negatives from the RCU list walk here, unlike the optimistic
     * fast walk).
     *
     * so doing d_lookup() (with seqlock), instead of lockfree __d_lookup
     */
     //再從磁盤讀取前再進行一次d_lookup嘗試,因爲之前等待互斥鎖時有可能已經創建了相應的dentry
    result = d_lookup(parent, name);
 
    /*下面進行dentry的創建*/
    if (!result) {
        struct dentry *dentry;
 
        /* Don't create child dentry for a dead directory. */
        result = ERR_PTR(-ENOENT);
        if (IS_DEADDIR(dir))
            goto out_unlock;
 
        /*分配一個dentry並進行相應的初始化*/
        dentry = d_alloc(parent, name);
        result = ERR_PTR(-ENOMEM);
        if (dentry) {
            /*調用特定於文件系統的lookup函數從磁盤中讀取數據並將dentry添入散列表*/
            result = dir->i_op->lookup(dir, dentry, nd);
            if (result)
                dput(dentry);
            else
                result = dentry;
        }
out_unlock:
        mutex_unlock(&dir->i_mutex);
        return result;
    }
 
    /*
     * Uhhuh! Nasty case: the cache was re-populated while
     * we waited on the semaphore. Need to revalidate.
     */
    mutex_unlock(&dir->i_mutex);
    if (result->d_op && result->d_op->d_revalidate) {
        result = do_revalidate(result, nd);
        if (!result)
            result = ERR_PTR(-ENOENT);
    }
    return result;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章