Chinaunix首页 | 论坛 | 博客
  • 博客访问: 752128
  • 博文数量: 79
  • 博客积分: 2671
  • 博客等级: 少校
  • 技术积分: 1247
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-02 15:26
个人简介

宅男

文章分类

全部博文(79)

文章存档

2017年(11)

2016年(12)

2015年(6)

2012年(10)

2011年(33)

2010年(7)

分类: LINUX

2010-04-19 19:43:24

上一篇稍微提到了虚拟文件中设计的几个结构体:files_struct,dentry,inode,在说到路径名到目标节点之前先提几点:
(1)dentry数据结构代表着虚拟文件系统中的逻辑文件,可以存在多个逻辑对于同一个物理文件(即inode节点)的情况
(2)一遍一个有效的dentry总是有一个与之对应的有效的inode结构
内核中完成从路径名到节点工作的是path_walk函数,定义在/linux/fs/namei.c,我这儿是针对2.6.16的内核分析,不过别的版本的差别不是太大

int fastcall path_walk(const char * name, struct nameidata *nd)
{
    current->total_link_count = 0;
    return link_path_walk(name, nd);
}

这儿的传入参数是代表路径名的char型指针,还有一个参数是struct nameidata结构体,定义于/linux
/include/linux/namei.h
这是一个临时性的数据结构,用来返回搜索的结果

struct nameidata {
    struct dentry    *dentry;//当前目录的dentry数据结构
    struct vfsmount *mnt;//当前文件系统的安装信息
    struct qstr    last;//当前目录的名称等一些信息
    unsigned int    flags;//标志位
    int        last_type;
    unsigned    depth;//连接文件的深度(可能一个连接文件跟到最后还是一个了连接文件)
    char *saved_names[MAX_NESTED_LINKS + 1];//用来保存连接文件的一些信息,下标表示连接文件的深度
    /* Intent data */
    union {
        struct open_intent open;
    } intent;
};

下面再看一下struct qstr数据结构,这个结构体也是临时性的,主要用来保存当前目录的名称,杂凑值。
path_walk的主体是__link_path_walk函数:
一遍在进行path_walk之前,需要先准备struct nameidata数据结构:
在do_path_lookup中有这样一段准备代码

if (*name=='/') {

//如果目录是以/开始的,也就是绝对路径,nd->dentry指向文件系统的根目录(也就是从文件系统的根目录开始查找)

        if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
            nd->mnt = mntget(current->fs->altrootmnt);
            nd->dentry = dget(current->fs->altroot);
            read_unlock(&current->fs->lock);
            if (__emul_lookup_dentry(name,nd))
                goto out; /* found in altroot */
            read_lock(&current->fs->lock);
        }
        nd->mnt = mntget(current->fs->rootmnt);
        nd->dentry = dget(current->fs->root);
    } else if (dfd == AT_FDCWD) {

//否则从当前目录开始查找

        nd->mnt = mntget(current->fs->pwdmnt);
        nd->dentry = dget(current->fs->pwd);
    }

下面开始正式的进入__link_path_walk的分析:

    while (*name=='/')
        name++;

//如果目录名是以/开始的,那么跳过/符号,这儿是允许出现多个/号的

if (!*name)

goto return_reval;

inode = nd->dentry->d_inode;

//刚才上面提到了,一个有效的目录项总是存在一个与之对应得inode节点

if (nd->depth)

lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);

//如果nd->depth大于0.表示目录名最终文件是一个连接文件,设置标志,继续查找下去,直至找到最终的真实文件

下面进入了循环主体


for(;;;){
        unsigned long hash;
        struct qstr this;
//这个结构体struct qstr保存着一些当前查询的信息
        unsigned int c;
        nd->flags |= LOOKUP_CONTINUE;
//检查访问权限,这儿检查是路径中各层目录的访问权限
        err = exec_permission_lite(inode, nd);
        if (err == -EAGAIN)
            err = vfs_permission(nd, MAY_EXEC);
         if (err)
            break;
        this.name = name;
        c = *(const unsigned char *)name;
//初始化hash表
        hash = init_name_hash();
//算出下一级目录的杂凑值,当前目录是根目录,
//比如现在我们查的是/linux/drivers/char/sep4020_char/sep4020_gpio.c
//现在算的是linux的杂凑值
        do {
            name++;
            hash = partial_name_hash(c, hash);
            c = *(const unsigned char *)name;
        } while (c && (c != '/'));
        this.len = name - (const char *) this.name;
        this.hash = end_name_hash(hash);
        /* remove trailing slashes? */
//如果已经是最后一级目录,也就是我们要找的最终文件
        if (!c)
            goto last_component;
//如果要查找的是一个目录,如/linux/drivers
        while (*++name == '/');
        if (!*name)
            goto last_with_slashes;
        /*
         * "." and ".." are special - ".." especially so because it has
         * to be able to know about the current root directory and
         * parent relationships.
         */

//如果下一级目录是/./或者../呢
        if (this.name[0] == '.') switch (this.len) {
//对于这儿的switch case语句有点不明白,也许是GCC本身的优化
            default:
                break;
            case 2:    
                if (this.name[1] != '.')
                    break;
//如果是../,就需要返回到上一级目录,由follow_dotdot完成,找到父目录
                follow_dotdot(nd);
                inode = nd->dentry->d_inode;
                /* fallthrough */
            case 1:
                continue;
        }
        /*
         * See if the low-level filesystem might want
         * to use its own hash..
         */

//如果文件系统提供了杂凑值的算法,就重新再算一遍
        if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
            err = nd->dentry->d_op->d_hash(nd->dentry, &this);
            if (err < 0)
                break;
        }
        /* This does the actual lookups.. */
//真正的开始查找了,do_lookup(),首先现在内存中的杂凑表中查找,如果没有找到

//那么就要到磁盘或者相应的块设备中去查找,并在内存建立相应的数据结构
        err = do_lookup(nd, &this, &next);

//next是struct path类型的结构体,定义于/linux/fs/namei.c,用于保存从磁盘或者其他块设备上查找的信息
        if (err)
            break;
        err = -ENOENT;
        inode = next.dentry->d_inode;
        if (!inode)
            goto out_dput;
        err = -ENOTDIR;
        if (!inode->i_op)
            goto out_dput;
//如果文件系统支持连接文件,并且follow_link存在,调用do_follow_link继续擦找,

//因为各个文件系统的差异,最终还是调用的inode节点的操作结构体的lookup函数
        if (inode->i_op->follow_link) {
            err = do_follow_link(&next, nd);
            if (err)
                goto return_err;
            err = -ENOENT;
            inode = nd->dentry->d_inode;
            if (!inode)
                break;
            err = -ENOTDIR;
            if (!inode->i_op)
                break;
        } else
            path_to_nameidata(&next, nd);
        err = -ENOTDIR;
        if (!inode->i_op->lookup)
            break;
        continue;
        /* here ends the main loop */

//对中间节点和对路径名的终点的处理是不一样的,存在一点差异

//下面是对路径名终点的处理
last_with_slashes:
        lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
last_component:
        /* Clear LOOKUP_CONTINUE iff it was previously unset */

//如果已经是最终文件了,就不需要再找下去了
        nd->flags &= lookup_flags | ~LOOKUP_CONTINUE;
        if (lookup_flags & LOOKUP_PARENT)
            goto lookup_parent;

//如果最终是/..形式,就需要返回其父目录
        if (this.name[0] == '.') switch (this.len) {
            default:
                break;
            case 2:    
                if (this.name[1] != '.')
                    break;
                follow_dotdot(nd);
                inode = nd->dentry->d_inode;
                /* fallthrough */
            case 1:
                goto return_reval;
        }
        if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
            err = nd->dentry->d_op->d_hash(nd->dentry, &this);
            if (err < 0)
                break;
        }
        err = do_lookup(nd, &this, &next);
        if (err)
            break;
        inode = next.dentry->d_inode;
        if ((lookup_flags & LOOKUP_FOLLOW)

//中间节点如果是连接节点的话,无条件的跟踪下去

//但是对于路径名的终点,只有设置了标志位LOOKUP_FOLLOW才会跟踪下去

         && inode && inode->i_op && inode->i_op->follow_link) {
            err = do_follow_link(&next, nd);
            if (err)
                goto return_err;
            inode = nd->dentry->d_inode;
        } else
            path_to_nameidata(&next, nd);
        err = -ENOENT;
        if (!inode)
            break;

//同样,对于路径名终点是目录的情况,只有在标志位中设置了LOOKUP_DIRECTORY,

//并且操作结构体中lookup存在的情况下才会查找
        if (lookup_flags & LOOKUP_DIRECTORY) {
            err = -ENOTDIR;
            if (!inode->i_op || !inode->i_op->lookup)
                break;
        }
        goto return_base;
lookup_parent:
        nd->last = this;
        nd->last_type = LAST_NORM;
        if (this.name[0] != '.')
            goto return_base;
        if (this.len == 1)
            nd->last_type = LAST_DOT;
        else if (this.len == 2 && this.name[1] == '.')
            nd->last_type = LAST_DOTDOT;
        else
            goto return_base;
return_reval:
        /*
         * We bypassed the ordinary revalidation routines.
         * We may need to check the cached dentry for staleness.
         */

        if (nd->dentry && nd->dentry->d_sb &&
         (nd->dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) {
            err = -ESTALE;
            /* Note: we do not d_invalidate() */
            if (!nd->dentry->d_op->d_revalidate(nd->dentry, nd))
                break;
        }
return_base:
        return 0;
out_dput:
        dput_path(&next, nd);
        break;
    }

其实细看,这个函数也不难,都是对一些字符的操作判断,主要还是理清dentry,inode数据结构之间的关系
主要参考毛德操的《linux内核源代码情景分析》
阅读(1855) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~