Chinaunix首页 | 论坛 | 博客
  • 博客访问: 715960
  • 博文数量: 183
  • 博客积分: 2650
  • 博客等级: 少校
  • 技术积分: 1428
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-22 17:02
文章分类
文章存档

2017年(1)

2015年(46)

2014年(4)

2013年(8)

2012年(2)

2011年(27)

2010年(35)

2009年(60)

分类: LINUX

2010-01-28 17:14:37

文件系统

dentry inode file

这里有个重要的图片, 还有几个数据结构列在前面, 会对后面的分析有所帮助…

数据结构1
struct nameidata {
    struct dentry *dentry;
    struct vfsmount *mnt;
    struct qstr last;
    unsigned int flags;
    int last_type;
};
数据结构 2
struct dentry {    // 该结构所代表的是逻辑意义上的文件, 记录的是其逻辑上的属性.
    atomic_t d_count;
    unsigned int d_flags;
    struct inode * d_inode; /* Where the name belongs to - NULL is negative */
    struct dentry * d_parent; /* parent directory */
    struct list_head d_vfsmnt;
    struct list_head d_hash; /* lookup hash list */
    struct list_head d_lru;   /* d_count = 0 LRU list */
    struct list_head d_child; /* child of parent list */
    struct list_head d_subdirs; /* our children */
    struct list_head d_alias; /* inode alias list */
    struct qstr d_name;
    unsigned long d_time;   /* used by d_revalidate */
    struct dentry_operations *d_op;
    struct super_block * d_sb; /* The root of the dentry tree */
    unsigned long d_reftime; /* last time referenced */
    void * d_fsdata;   /* fs-specific data */
    unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */
};

数据结构 3
struct inode {     // 该结构所代表的是物理上的文件, 记录的是其物理上的属性.(比如说文件的存储位置, 被哪些不同名所指向, 所属用户.组, 所在设备号, 创建 修改时间等等)
    struct list_head i_hash;
    struct list_head i_list;
    struct list_head i_dentry;
    struct list_head i_dirty_buffers;
    unsigned long   i_ino;
    atomic_t   i_count;
    kdev_t    i_dev;
    umode_t    i_mode;
    nlink_t    i_nlink;
    uid_t    i_uid;
    gid_t    i_gid;
    kdev_t    i_rdev;
    loff_t    i_size;
    time_t    i_atime;
    time_t    i_mtime;
    time_t    i_ctime;
    unsigned long   i_blksize;
    unsigned long   i_blocks;
    unsigned long   i_version;
    struct semaphore i_sem;
    struct semaphore i_zombie;
    struct inode_operations *i_op;
    struct file_operations *i_fop; /* former ->i_op->default_file_ops */
    struct super_block *i_sb;
    wait_queue_head_t i_wait;
    struct file_lock *i_flock;
    struct address_space *i_mapping;
    struct address_space i_data;
    struct dquot   *i_dquot[MAXQUOTAS];
    struct pipe_inode_info *i_pipe;
    struct block_device *i_bdev;
    unsigned long   i_dnotify_mask; /* Directory notify events */
    struct dnotify_struct *i_dnotify; /* for directory notifications */
    unsigned long   i_state;
    unsigned int   i_flags;
    unsigned char   i_sock;
    atomic_t   i_writecount;
    unsigned int   i_attr_flags;
    __u32    i_generation;
    union {
       struct ext2_inode_info   ext2_i;
    …
    } u;
};
数据结构 4
struct qstr {
    const unsigned char * name;
    unsigned int len;
    unsigned int hash;
};


先分析这样一个函数.在path_walk 之前要对 path_walk返回的结构体nameidata指针进行必要的初始化.
Fs/namei.c   L690-702
int path_init(const char *name, unsigned int flags, struct nameidata *nd)
{
    nd->last_type = LAST_ROOT; /* if there are only slashes... */
    nd->flags = flags;
    if (*name=='/')   //如果当前给出路径是以’/’开始, 则说明给出的是绝对路径, 也就是说要从根目录一层一层的找下去, 所以这里nameidata指针中的 dentry 要取当前进程的根目录的 dentry.
       return walk_init_root(name,nd); //如是取路径.
    read_lock(¤t->fs->lock);
    // 否则的话说明当前给出路径是相对路径, 要从当前进程环境中取出当前目录的dentry, 及mnt .
    nd->mnt = mntget(current->fs->pwdmnt);
    nd->dentry = dget(current->fs->pwd);
    read_unlock(¤t->fs->lock);
    return 1;
}

Fs/namei.c   L672-688
static inline int
walk_init_root(const char *name, struct nameidata *nd)
{
    read_lock(¤t->fs->lock);
    if (current->fs->altroot && !(nd->flags & LOOKUP _NOALT)) {
    // 这里只读出了, 如果当前路径有替换根目录, 并且对应禁止标志位也无效,那么使用替换 目录.
       nd->mnt = mntget(current->fs->altrootmnt);
       nd->dentry = dget(current->fs->altroot);
       read_unlock(¤t->fs->lock);
       if (__emul_lookup_dentry(name,nd))
        return 0;
       read_lock(¤t->fs->lock);
    }
    //如果没有替换路径那么把当前用户根目录信息赋值到nameidata数据下.
    nd->mnt = mntget(current->fs->rootmnt);
    nd->dentry = dget(current->fs->root);
    read_unlock(¤t->fs->lock);
    return 1;
}
到这里, 在path_walk函数中所要用到的 dentry 及 mnt等数据都以初始化完成. 可以进行查找了. (这里要查找的是目标目录或文件的dentry及 mnt )

/*
* Name resolution.
*
* This is the basic name resolution function, turning a pathname
* into the final dentry.
*
* We expect 'base' to be positive and a directory.
*/
int path_walk(const char * name, struct nameidata *nd)
{
    struct dentry *dentry;
    struct inode *inode;
    int err;
    unsigned int lookup_flags = nd->flags;

    while (*name=='/')
       name++;
    if (!*name)
       goto return_base;
    //以上 去掉给出路径最前面所有 ’/’ 字符.如果只有一个 ‘/’ 或全是 ‘/’ 那么 直接返回当传入的nameidata信息.
    inode = nd->dentry->d_inode;
    if (current->link_count)
     lookup_flags = LOOKUP_FOLLOW;

/* At this point we know we have a real path component. */
    for(;;) { // 主要的循环, 在这要遍历给出路径中每一个路径或文件.
       unsigned long hash;
       struct qstr this;
       unsigned int c;

       err = permission(inode, MAY_EXEC); //当前路径有执行的权限吗?
       dentry = ERR_PTR(err);
        if (err)
            break;

       this.name = name;
       c = *(const unsigned char *)name;

       hash = init_name_hash(); //初始化哈希值.
       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; //给定最后一个节点是文件
       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) {
             default:
                 break;
             case 2:
                 if (this.name[1] != '.')
                     break;
                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.. */
       // 在内存中查找该目录或文件的dentry是否存在(过).
       dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
       if (!dentry) {
            dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
           // 到磁盘上去查找该目录或文件 在dentry 是否存在(过).
           err = PTR_ERR(dentry);
            if (IS_ERR(dentry))就用自带的哈希函数.
                break;
      }
       /* Check mountpoints.. */
       //如果找到目录是一个设备的挂载点, 那么前进到被挂载设备的根目录上去, 直到设备的根目录下没有挂载其它设备为止.
       while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry))
        ;

       err = -ENOENT;
      inode = dentry->d_inode;
       if (!inode) //如果目录或文件已找到, 无论是目录还是文件, 这个inode 一定不为空. 需要这个数据去操作本节点.
            goto out_dput;
      err = -ENOTDIR;
       if (!inode->i_op) // 节点操作函数不能为空.
            goto out_dput;

      if (inode->i_op->follow_link) {
            err = do_follow_link(dentry, nd); //验证当前目录或文件是否是链接. 并前进到真正的节点.
        dput(dentry);
        if (err)
             goto return_err;
        err = -ENOENT;
        inode = nd->dentry->d_inode;
       if (!inode)
            break;
        err = -ENOTDIR;
       if (!inode->i_op)
             break;
       } else {
           dput(nd->dentry);
            nd->dentry = dentry;
      }
       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:   //最后一个节点是文件.
       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_base;
       }
       //最后一个节点自己有哈希函数.
       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;
       }
       // 在内存中查找最后一个节点.
       dentry = cached_lookup(nd->dentry, &this, 0);
      if (!dentry) {
        // 到物理介质上去查找当前节点.
            dentry = real_lookup(nd->dentry, &this, 0);
            err = PTR_ERR(dentry);
            if (IS_ERR(dentry))
                 break;
       }
       //最后一级目录或文件是否其它设备mount点, 如果是那么一直找到新设备根目录没有被其它设备mount 为止.
       while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry))
       ;
       inode = dentry->d_inode;
      if ((lookup_flags & LOOKUP_FOLLOW)
          && inode && inode->i_op && inode->i_op->follow_link) {
        err = do_follow_link(dentry, nd); //查看最后一级节点是不是其它文件或目录的链接, 如果是则前进到真实目录或文件上去.
        dput(dentry);
        if (err)
             goto return_err;
        inode = nd->dentry->d_inode;
       } else {
            dput(nd->dentry);
            nd->dentry = dentry;
      }
       err = -ENOENT;
       if (!inode)
            goto no_inode;
      if (lookup_flags & LOOKUP_DIRECTORY) {
            err = -ENOTDIR;
       if (!inode->i_op || !inode->i_op->lookup)
           break;
       }
       goto return_base;
no_inode:
       err = -ENOENT;
       if (lookup_flags & (LOOKUP_POSITIVE|LOOKUP_DIRECTORY))
          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;
return_base:
       return 0;
out_dput:
       dput(dentry);
       break;
    }
    path_release(nd);
    return_err:
    return err;
}

举例如:
/////root//////share/c/test1/main.c
第一步, 去年root前的 ‘/’ 通过代码 while (*name=='/') name++;
第二步, 计算root的哈希值, 在内存中查找dentry, 看是否存在, 不存在则到物理设备中去查找对应的dentry, 如是其它设备的挂载点, 则前进到其设备的根节点, 一直打到新设备的根节点不再有其它设备被mount为止. 如该节点是链接, 则前进到真正的节点.
第三步, 去年 当前节点与下一个节点之间多余的 ‘/’ 字符. 重复第二步.

相关函数:
Path_walk() -> follow_dotdot()    ./linux/fs/namei.c L380 - 413
static inline void follow_dotdot(struct nameidata *nd)
{
    while(1) {
       struct vfsmount *parent;
       struct dentry *dentry;
       read_lock(¤t->fs->lock);
       if (nd->dentry == current->fs->root &&
          nd->mnt == current->fs->rootmnt) { //如果已经是根设备了. 什么也不做.
            read_unlock(¤t->fs->lock);
           break;
       }
       read_unlock(¤t->fs->lock);
       spin_lock(&dcache_lock);
       if (nd->dentry != nd->mnt->mnt_root) { // 如果当前节点dentry 不等于当前节点monut数据结构中的根设备的dentry, 说明当前节点不是做为根节点被mount到其它设备上去的. Dentry中的d_vfsmnt是记录当前目录下有多少个mount设备的, 不能通过它来判断来当前节点是不是有其它设备被 mount ;
            dentry = dget(nd->dentry->d_parent); //取出父节点.
            spin_unlock(&dcache_lock);
           dput(nd->dentry);
           nd->dentry = dentry;   //向父目录前进.
           break;
     }
      // 以下情况是 当前节点是另一个设备mount 后的根目录, 再向上, 就要跳向其它设备了.
      parent=nd->mnt->mnt_parent;
       if (parent == nd->mnt) {   // 这种情况是不应该出现的. 既然当前节点已经是其它设备的mount 点, 两个mnt对象就不应该为同一个.
          spin_unlock(&dcache_lock);
          break;
      }
      mntget(parent);
      dentry=dget(nd->mnt->mnt_mountpoint); // 取 mount 节点的dentry;
      spin_unlock(&dcache_lock);
       dput(nd->dentry);
       nd->dentry = dentry; // 跳回mount点. 这好像有点问题, 现在的dentry 是mount点的, 而现在是要向上级目录跳, 是从新设备的根目录向上跳, 不应该跳到mount点, 而应该跳到 mount 点的上层目录. ???
      // 呵呵 , 注意这里是while(1)死循环, 而当前节点是mount点的时候是没有break的, 在下一次循环的时候 会向上跳并 执行break 跳出while(1);
      mntput(nd->mnt);
      nd->mnt = parent;
    }
}

阅读(1333) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~