您的位置:首页 > 运维架构 > Linux

Linux内核源代码情景分析-从路径名到目标节点

2017-11-09 22:24 295 查看
先看外包装,__user_walk,假设name为/usr/local/hello.c

[cpp]
view plain
copy

int __user_walk(const char *name, unsigned flags, struct nameidata *nd)  
{  
    char *tmp;  
    int err;  
  
    tmp = getname(name);//在系统空间分配一个页面,并从用户空间把文件名复制到这个页面  
    err = PTR_ERR(tmp);  
    if (!IS_ERR(tmp)) {  
        err = 0;  
        if (path_init(tmp, flags, nd))  
            err = path_walk(tmp, nd);  
        putname(tmp);  
    }  
    return err;  
}  

    第一个参数name为路径名。
    第二个参数flags可以为以下的标志:

[cpp]
view plain
copy

#define LOOKUP_FOLLOW       (1) //如果找到的目标只是"符号连接"到其它文件或者目录的一个目录项,则要顺着连接链一直找到终点  
#define LOOKUP_DIRECTORY    (2) //表示要寻找的目标必须是个目录  
#define LOOKUP_CONTINUE     (4)  
#define LOOKUP_POSITIVE     (8)  
#define LOOKUP_PARENT       (16) //找到父节点  
#define LOOKUP_NOALT        (32)  

    第三个参数nd是个结构指针,数据结构nameidata的定义如下:

[cpp]
view plain
copy

struct nameidata {  
    struct dentry *dentry;  
    struct vfsmount *mnt;  
    struct qstr last;  
    unsigned int flags;  
    int last_type;  
};  

    

    path_init,代码如下:

[cpp]
view plain
copy

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=='/')  
        return walk_init_root(name,nd);//如果路径名是以"/"开头的绝对路径,那就要通过这个函数从根目录开始查找  
    read_lock(¤t->fs->lock);  
    nd->mnt = mntget(current->fs->pwdmnt);//如果路径名不以"/"开头,那就当前目录开始  
    nd->dentry = dget(current->fs->pwd);  
    read_unlock(¤t->fs->lock);  
    return 1;  
}  

[cpp]
view plain
copy

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);  
    }  
    nd->mnt = mntget(current->fs->rootmnt);  
    nd->dentry = dget(current->fs->root);  
    read_unlock(¤t->fs->lock);  
    return 1;  
}  

    返回到__user_walk,继续执行path_walk,代码如下:

[cpp]
view plain
copy

int path_walk(const char * name, struct nameidata *nd)//name为/usr/local/hello.c  
{  
    struct dentry *dentry;  
    struct inode *inode;  
    int err;  
    unsigned int lookup_flags = nd->flags;  
  
    while (*name=='/')//跳过/usr/local/hello.c最开始的'/'  
        name++;  
    if (!*name)//如果只有'/',那么直接退出  
        goto return_base;  
  
    inode = nd->dentry->d_inode;//根节点的indode结构  
    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;//已经指向了usr的首字符'u'  
        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 != '/'));//如果字符c为空或者遇到了'/'则退出循环,本例中c指向的usr后面的'/'  
        this.len = name - (const char *) this.name;//usr的长度  
        this.hash = end_name_hash(hash);  
  
        /* remove trailing slashes? */  
        if (!c)//如果字符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) {//执行到这里,this代表中间节点,中间节点一定是目录  
            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) {//如果有d_hash这个函数  
            err = nd->dentry->d_op->d_hash(nd->dentry, &this);//使用这个函数,重新计算hash值  
            if (err < 0)  
                break;  
        }  
        /* This does the actual lookups.. */  
        dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE);//在内存中寻找该节点业已建立的dentry结构  
        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)  
            goto out_dput;  
        err = -ENOTDIR;   
        if (!inode->i_op)  
            goto out_dput;  
  
        if (inode->i_op->follow_link) {//看看这个指针是否为NULL,这个指针是在ext2_read_inode中设置的  
            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;//如果没有,直接附给nd->dentry  
        }  
        err = -ENOTDIR;   
        if (!inode->i_op->lookup)//iop指针也是ext2_read_inode中设置的为ext2_dir_inode_operations,因为当前/usr为目录节点,所以一定要有ext2_lookup这个指针  
            break;  
        continue;  
        /* here ends the main loop */  
  
last_with_slashes:  
        ......  
last_component:  
        ......  
}  

[cpp]
view plain
copy

struct qstr {  
    const unsigned char * name;  
    unsigned int len;  
    unsigned int hash;  
};  

    
    cached_lookup,在内存中寻找该节点业已建立的dentry结构,代码如下:

[cpp]
view plain
copy

static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, int flags)  
{  
    struct dentry * dentry = d_lookup(parent, name);  
  
    if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {  
        if (!dentry->d_op->d_revalidate(dentry, flags) && !d_invalidate(dentry)) {  
            dput(dentry);  
            dentry = NULL;  
        }  
    }  
    return dentry;  
}  

[cpp]
view plain
copy

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 list_head *head = d_hash(parent,hash);  
    struct list_head *tmp;  
  
    spin_lock(&dcache_lock);  
    tmp = head->next;  
    for (;;) {  
        struct dentry * dentry = list_entry(tmp, struct dentry, d_hash);  
        if (tmp == head)  
            break;  
        tmp = tmp->next;  
        if (dentry->d_name.hash != hash)//比较hash值  
            continue;  
        if (dentry->d_parent != parent)//比较父节点的dentry结构是否一样  
            continue;  
        if (parent->d_op && parent->d_op->d_compare) {  
            if (parent->d_op->d_compare(parent, &dentry->d_name, name))  
                continue;  
        } else {  
            if (dentry->d_name.len != len)//比较长度  
                continue;  
            if (memcmp(dentry->d_name.name, str, len))//比较名字  
                continue;  
        }  
        __dget_locked(dentry);  
        dentry->d_flags |= DCACHE_REFERENCED;  
        spin_unlock(&dcache_lock);  
        return dentry;//如果找到则返回节点的dentry结构  
    }  
    spin_unlock(&dcache_lock);  
    return NULL;//如果没有找到,返回NULL  
}  

    内核中有个杂凑表dentry_hashtable,是一个list_head指针数组,一旦在内存中建立起一个目录节点的dentry结构,就根据其节点名的杂凑值挂入杂凑表中的某个队列,需要寻找时则还是根据杂凑值从杂凑表查找。d_hash就是根据杂凑值找到杂凑表中的某一个队列头。

[cpp]
view plain
copy

static inline struct list_head * d_hash(struct dentry * parent, unsigned long hash)  
{  
    hash += (unsigned long) parent / L1_CACHE_BYTES;  
    hash = hash ^ (hash >> D_HASHBITS) ^ (hash >> D_HASHBITS*2);  
    return dentry_hashtable + (hash & D_HASHMASK);  
}  

    根据name在队列里中寻找,是否已经存在这个dentry结构。
 

    如果没有找到,那么要建立该节点的dentry结构,real_lookup代码如下:

[cpp]
view plain
copy

static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, int flags)  
{  
    struct dentry * result;  
    struct inode *dir = parent->d_inode;  
  
    down(&dir->i_sem);  
    /* 
     * 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. 
     */  
    result = d_lookup(parent, name);//再一次在内存中寻找该节点业已建立的dentry结构  
    if (!result) {//如果还是没有找到  
        struct dentry * dentry = d_alloc(parent, name);//分配该节点的dentry结构  
        result = ERR_PTR(-ENOMEM);  
        if (dentry) {  
            lock_kernel();  
            result = dir->i_op->lookup(dir, dentry);//分配该节点的inode结构,并挂在dentry结构上  
            unlock_kernel();  
            if (result)  
                dput(dentry);  
            else  
                result = dentry;  
        }  
        up(&dir->i_sem);  
        return result;  
    }  
  
    /* 
     * Uhhuh! Nasty case: the cache was re-populated while 
     * we waited on the semaphore. Need to revalidate. 
     */  
    up(&dir->i_sem);  
    if (result->d_op && result->d_op->d_revalidate) {  
        if (!result->d_op->d_revalidate(result, flags) && !d_invalidate(result)) {  
            dput(result);  
            result = ERR_PTR(-ENOENT);  
        }  
    }  
    return result;  
}  

    d_alloc,分配该节点的dentry结构,代码如下:

[cpp]
view plain
copy

struct dentry * d_alloc(struct dentry * parent, const struct qstr *name)  
{  
    char * str;  
    struct dentry *dentry;  
  
    dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL); //分配了dentry结构  
    if (!dentry)  
        return NULL;  
  
    if (name->len > DNAME_INLINE_LEN-1) {  
        str = kmalloc(NAME_ALLOC_LEN(name->len), GFP_KERNEL);  
        if (!str) {  
            kmem_cache_free(dentry_cache, dentry);   
            return NULL;  
        }  
    } else  
        str = dentry->d_iname;   
  
    memcpy(str, name->name, name->len);  
    str[name->len] = 0;  
  
    atomic_set(&dentry->d_count, 1);  
    dentry->d_flags = 0;  
    dentry->d_inode = NULL;  
    dentry->d_parent = NULL;  
    dentry->d_sb = NULL;  
    dentry->d_name.name = str;//名字  
    dentry->d_name.len = name->len;//长度  
    dentry->d_name.hash = name->hash;//hash值  
    dentry->d_op = NULL;  
    dentry->d_fsdata = NULL;  
    INIT_LIST_HEAD(&dentry->d_vfsmnt);//见下面关于dentry的说明  
    INIT_LIST_HEAD(&dentry->d_hash);  
    INIT_LIST_HEAD(&dentry->d_lru);  
    INIT_LIST_HEAD(&dentry->d_subdirs);  
    INIT_LIST_HEAD(&dentry->d_alias);  
    if (parent) {  
        dentry->d_parent = dget(parent);  
        dentry->d_sb = parent->d_sb;  
        spin_lock(&dcache_lock);  
        list_add(&dentry->d_child, &parent->d_subdirs);  
        spin_unlock(&dcache_lock);  
    } else  
        INIT_LIST_HEAD(&dentry->d_child);  
  
    dentry_stat.nr_dentry++;  
    return dentry;  
}  

    其中dentry结构,如下:

[cpp]
view plain
copy

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 */  
};  

    1、每个dentry结构都通过队列头d_hash链入杂凑表dentry_hashtable中的某个队列里。
    2、共享计数为0的dentry结构都通过队列头d_lru链入LRU队列dentry_unused。在队列中等待释放或者"东山再起"。

    3、每个dentry结构都通过指针d_inode指向一个inode数据结构。但是多个dentry结构可以指向同一个indoe数据结构。

    4、指向同一个inode数据结构的dentry结构都通过队列头d_alias链接在一起,都在该inode结构的i_dentry队列中。

    5、每个dentry结构都通过指针d_parent指向其父目录节点的dentry结构,并通过队列头d_child跟同一目录中的其它节点的dentry结构链接在一起,都在父目录节点的d_subdirs队列中。

    6、每个dentry结构都通过指针d_sb指向一个super_block数据结构。

    7、每个dentry结构都通过指针d_op指向一个dentry_operations数据结构。

    8、每个dentry结构都有个队列头d_vfsmnt,用于文件系统的安装。

    返回到real_lookup中,如果该节点的dentry结构分配成功,那么调用dir->i_op->lookup(dir, dentry)来分配该节点的inode结构,并挂在dentry结构上。其中dir是父目录节点的inode结构。

    对于ext2文件系统来说,lookup为ext2_lookup。代码如下:

[cpp]
view plain
copy

static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry)  
{  
    struct inode * inode;  
    struct ext2_dir_entry_2 * de;  
    struct buffer_head * bh;  
  
    if (dentry->d_name.len > EXT2_NAME_LEN)  
        return ERR_PTR(-ENAMETOOLONG);  
  
    bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de);//根据父节点的inode结构中inode->u.ext2_i.i_data找到对应的目录项  
    inode = NULL;  
    if (bh) {  
        unsigned long ino = le32_to_cpu(de->inode);//得到该节点inode结构的节点号  
        brelse (bh);  
        inode = iget(dir->i_sb, ino);//通过这个节点号寻找该节点的inode结构  
  
        if (!inode)  
            return ERR_PTR(-EACCES);  
    }  
    d_add(dentry, inode);//把该节点的inode结构和dentry连接在一起  
    return NULL;  
}  

    有关文件系统的细节,请看文件系统,用这篇文章的术语,ext2_find_entry根据父节点的inode结构中inode->u.ext2_i.i_data找到对应的目录项,这个目录项的结构是ext2_dir_entry_2结构,读者在前面已经看过dentry结构,它们的区别是ext2_dir_entry_2结构对应硬盘上的页目录项结构,而dentry是动态建立的。

[cpp]
view plain
copy

struct ext2_dir_entry_2 {  
    __u32   inode;          /* Inode number */  
    __u16   rec_len;        /* Directory entry length */  
    __u8    name_len;       /* Name length */  
    __u8    file_type;  
    char    name[EXT2_NAME_LEN];    /* File name */  
};  

    ext2_find_entry找到的数据块通常有许多目录项,根据dentry->d_name.name, dentry->d_name.len找到对应的目录项结构ext2_dir_entry_2。

    然后根据de->inode,得到该节点inode结构的节点号,通过这个节点号寻找该节点的inode结构,iget如下:

[cpp]
view plain
copy

static inline struct inode *iget(struct super_block *sb, unsigned long ino)  
{  
    return iget4(sb, ino, NULL, NULL);  
}  

[cpp]
view plain
copy

struct inode *iget4(struct super_block *sb, unsigned long ino, find_inode_t find_actor, void *opaque)  
{  
    struct list_head * head = inode_hashtable + hash(sb,ino);  
    struct inode * inode;  
  
    spin_lock(&inode_lock);  
    inode = find_inode(sb, ino, head, find_actor, opaque);//在杂凑表队列中寻找  
    if (inode) {//找到了  
        __iget(inode);//递增计数  
        spin_unlock(&inode_lock);  
        wait_on_inode(inode);  
        return inode;  
    }  
    spin_unlock(&inode_lock);  
  
    /* 
     * get_new_inode() will do the right thing, re-trying the search 
     * in case it had to block at any point. 
     */  
    return get_new_inode(sb, ino, head, find_actor, opaque);//分配一个新的该节点的inode结构  
}  

    inode结构也有个杂凑表inode_hashtable,已经建立的inode结构都通过结构中的i_hash(也是一个list_head)挂在该杂凑表的某一队列中,所以首先要通过find_inode()在杂凑表队列中寻找。

[cpp]
view plain
copy

static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head, find_inode_t find_actor, void *opaque)  
{  
    struct list_head *tmp;  
    struct inode * inode;  
  
    tmp = head;  
    for (;;) {  
        tmp = tmp->next;  
        inode = NULL;  
        if (tmp == head)  
            break;  
        inode = list_entry(tmp, struct inode, i_hash);  
        if (inode->i_ino != ino)//对比节点号  
            continue;  
        if (inode->i_sb != sb)//和超级块结构  
            continue;  
        if (find_actor && !find_actor(inode, ino, opaque))  
            continue;  
        break;  
    }  
    return inode;  
}  

    如果没有找到,get_new_inode分配一个新的该节点的inode结构,代码如下:

[cpp]
view plain
copy

static struct inode * get_new_inode(struct super_block *sb, unsigned long ino, struct list_head *head, find_inode_t find_actor, void *opaque)  
{  
    struct inode * inode;  
  
    inode = alloc_inode();  
    if (inode) {  
        struct inode * old;  
  
        spin_lock(&inode_lock);  
        /* We released the lock, so.. */  
        old = find_inode(sb, ino, head, find_actor, opaque);//再一次在杂凑表队列中寻找  
        if (!old) {//如果没有找到  
            inodes_stat.nr_inodes++;  
            list_add(&inode->i_list, &inode_in_use);  
            list_add(&inode->i_hash, head);//加入到对应的hash表  
            inode->i_sb = sb;//超级块结构  
            inode->i_dev = sb->s_dev;//设备号  
            inode->i_ino = ino;//节点号  
            inode->i_flags = 0;  
            atomic_set(&inode->i_count, 1);  
            inode->i_state = I_LOCK;  
            spin_unlock(&inode_lock);  
  
            clean_inode(inode);  
            sb->s_op->read_inode(inode);  
  
            /* 
             * This is special!  We do not need the spinlock 
             * when clearing I_LOCK, because we're guaranteed 
             * that nobody else tries to do anything about the 
             * state of the inode when it is locked, as we 
             * just created it (so there can be no old holders 
             * that haven't tested I_LOCK). 
             */  
            inode->i_state &= ~I_LOCK;  
            wake_up(&inode->i_wait);  
  
            return inode;  
        }  
  
        /* 
         * Uhhuh, somebody else created the same inode under 
         * us. Use the old inode instead of the one we just 
         * allocated. 
         */  
        __iget(old);//如果找到了inode结构  
        spin_unlock(&inode_lock);  
        destroy_inode(inode);  
        inode = old;//使用找到的inode结构  
        wait_on_inode(inode);  
    }  
    return inode;  
}  

    alloc_inode,分配一个新的inode结构,代码如下:

[cpp]
view plain
copy

#define alloc_inode() \  
     ((struct inode *) kmem_cache_alloc(inode_cachep, SLAB_KERNEL))  

    sb->s_op->read_inode,把硬盘上的inode结构读入,并合理的赋值给内存中这个动态的inode。下面我们分别介绍下动态的inode结构和硬盘上的ext2_inode结构。
    动态的inode结构:

[cpp]
view plain
copy

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 minix_inode_info     minix_i;  
        struct ext2_inode_info      ext2_i;  
        struct hpfs_inode_info      hpfs_i;  
        struct ntfs_inode_info      ntfs_i;  
        struct msdos_inode_info     msdos_i;  
        struct umsdos_inode_info    umsdos_i;  
        struct iso_inode_info       isofs_i;  
        struct nfs_inode_info       nfs_i;  
        struct sysv_inode_info      sysv_i;  
        struct affs_inode_info      affs_i;  
        struct ufs_inode_info       ufs_i;  
        struct efs_inode_info       efs_i;  
        struct romfs_inode_info     romfs_i;  
        struct shmem_inode_info     shmem_i;  
        struct coda_inode_info      coda_i;  
        struct smb_inode_info       smbfs_i;  
        struct hfs_inode_info       hfs_i;  
        struct adfs_inode_info      adfs_i;  
        struct qnx4_inode_info      qnx4_i;  
        struct bfs_inode_info       bfs_i;  
        struct udf_inode_info       udf_i;  
        struct ncp_inode_info       ncpfs_i;  
        struct proc_inode_info      proc_i;  
        struct socket           socket_i;  
        struct usbdev_inode_info        usbdev_i;  
        void                *generic_ip;  
    } u;  
}  

    硬盘上的ext2_inode结构:

[cpp]
view plain
copy

struct ext2_inode {  
    __u16   i_mode;     /* File mode */  
    __u16   i_uid;      /* Low 16 bits of Owner Uid */  
    __u32   i_size;     /* Size in bytes */  
    __u32   i_atime;    /* Access time */  
    __u32   i_ctime;    /* Creation time */  
    __u32   i_mtime;    /* Modification time */  
    __u32   i_dtime;    /* Deletion Time */  
    __u16   i_gid;      /* Low 16 bits of Group Id */  
    __u16   i_links_count;  /* Links count */  
    __u32   i_blocks;   /* Blocks count */  
    __u32   i_flags;    /* File flags */  
    union {  
        struct {  
            __u32  l_i_reserved1;  
        } linux1;  
        struct {  
            __u32  h_i_translator;  
        } hurd1;  
        struct {  
            __u32  m_i_reserved1;  
        } masix1;  
    } osd1;             /* OS dependent 1 */  
    __u32   i_block[EXT2_N_BLOCKS];/* Pointers to blocks */  
    __u32   i_generation;   /* File version (for NFS) */  
    __u32   i_file_acl; /* File ACL */  
    __u32   i_dir_acl;  /* Directory ACL */  
    __u32   i_faddr;    /* Fragment address */  
    union {  
        struct {  
            __u8    l_i_frag;   /* Fragment number */  
            __u8    l_i_fsize;  /* Fragment size */  
            __u16   i_pad1;  
            __u16   l_i_uid_high;   /* these 2 fields    */  
            __u16   l_i_gid_high;   /* were reserved2[0] */  
            __u32   l_i_reserved2;  
        } linux2;  
        struct {  
            __u8    h_i_frag;   /* Fragment number */  
            __u8    h_i_fsize;  /* Fragment size */  
            __u16   h_i_mode_high;  
            __u16   h_i_uid_high;  
            __u16   h_i_gid_high;  
            __u32   h_i_author;  
        } hurd2;  
        struct {  
            __u8    m_i_frag;   /* Fragment number */  
            __u8    m_i_fsize;  /* Fragment size */  
            __u16   m_pad1;  
            __u32   m_i_reserved2[2];  
        } masix2;  
    } osd2;             /* OS dependent 2 */  
}  

     sb->s_op->read_inode,把硬盘上的inode结构读入,并合理的赋值给内存中这个动态的inode。

[cpp]
view plain
copy

void ext2_read_inode (struct inode * inode)  
{  
    struct buffer_head * bh;  
    struct ext2_inode * raw_inode;  
    unsigned long block_group;  
    unsigned long group_desc;  
    unsigned long desc;  
    unsigned long block;  
    unsigned long offset;  
    struct ext2_group_desc * gdp;  
  
    if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO &&  
         inode->i_ino != EXT2_ACL_DATA_INO &&  
         inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) ||  
        inode->i_ino > le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_inodes_count)) {  
        ext2_error (inode->i_sb, "ext2_read_inode",  
                "bad inode number: %lu", inode->i_ino);  
        goto bad_inode;  
    }  
    block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);  
    if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count) {  
        ext2_error (inode->i_sb, "ext2_read_inode",  
                "group >= groups count");  
        goto bad_inode;  
    }  
    group_desc = block_group >> EXT2_DESC_PER_BLOCK_BITS(inode->i_sb);  
    desc = block_group & (EXT2_DESC_PER_BLOCK(inode->i_sb) - 1);  
    bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc];  
    if (!bh) {  
        ext2_error (inode->i_sb, "ext2_read_inode",  
                "Descriptor not loaded");  
        goto bad_inode;  
    }  
  
    gdp = (struct ext2_group_desc *) bh->b_data;  
    /* 
     * Figure out the offset within the block group inode table 
     */  
    offset = ((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) *  
        EXT2_INODE_SIZE(inode->i_sb);  
    block = le32_to_cpu(gdp[desc].bg_inode_table) +  
        (offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));  
    if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) {  
        ext2_error (inode->i_sb, "ext2_read_inode",  
                "unable to read inode block - "  
                "inode=%lu, block=%lu", inode->i_ino, block);  
        goto bad_inode;  
    }  
    offset &= (EXT2_BLOCK_SIZE(inode->i_sb) - 1);  
    raw_inode = (struct ext2_inode *) (bh->b_data + offset);//根据节点号,找到硬盘上的ext2_inode结构  
  
    inode->i_mode = le16_to_cpu(raw_inode->i_mode);//mode也来来源于ext2_inode结构  
    inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);  
    inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);  
    if(!(test_opt (inode->i_sb, NO_UID32))) {  
        inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;  
        inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;  
    }  
    inode->i_nlink = le16_to_cpu(raw_inode->i_links_count);  
    inode->i_size = le32_to_cpu(raw_inode->i_size);  
    inode->i_atime = le32_to_cpu(raw_inode->i_atime);  
    inode->i_ctime = le32_to_cpu(raw_inode->i_ctime);  
    inode->i_mtime = le32_to_cpu(raw_inode->i_mtime);  
    inode->u.ext2_i.i_dtime = le32_to_cpu(raw_inode->i_dtime);  
    /* We now have enough fields to check if the inode was active or not. 
     * This is needed because nfsd might try to access dead inodes 
     * the test is that same one that e2fsck uses 
     * NeilBrown 1999oct15 
     */  
    if (inode->i_nlink == 0 && (inode->i_mode == 0 || inode->u.ext2_i.i_dtime)) {  
        /* this inode is deleted */  
        brelse (bh);  
        goto bad_inode;  
    }  
    inode->i_blksize = PAGE_SIZE;    /* This is the optimal IO size (for stat), not the fs block size */  
    inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);  
    inode->i_version = ++event;  
    inode->u.ext2_i.i_flags = le32_to_cpu(raw_inode->i_flags);//联合体的ext2_i被复制  
    inode->u.ext2_i.i_faddr = le32_to_cpu(raw_inode->i_faddr);  
    inode->u.ext2_i.i_frag_no = raw_inode->i_frag;  
    inode->u.ext2_i.i_frag_size = raw_inode->i_fsize;  
    inode->u.ext2_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl);  
    if (S_ISDIR(inode->i_mode))  
        inode->u.ext2_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);  
    else {  
        inode->u.ext2_i.i_high_size = le32_to_cpu(raw_inode->i_size_high);  
        inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;  
    }  
    inode->i_generation = le32_to_cpu(raw_inode->i_generation);  
    inode->u.ext2_i.i_block_group = block_group;  
  
    /* 
     * NOTE! The in-memory inode i_data array is in little-endian order 
     * even on big-endian machines: we do NOT byteswap the block numbers! 
     */  
    for (block = 0; block < EXT2_N_BLOCKS; block++)  
        inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];  
  
    if (inode->i_ino == EXT2_ACL_IDX_INO ||  
        inode->i_ino == EXT2_ACL_DATA_INO)  
        /* Nothing to do */ ;  
    else if (S_ISREG(inode->i_mode)) {//根据不同的该节点的inode结构不同状态,设置不同的指针  
        inode->i_op = &ext2_file_inode_operations;  
        inode->i_fop = &ext2_file_operations;  
        inode->i_mapping->a_ops = &ext2_aops;  
    } else if (S_ISDIR(inode->i_mode)) {//目前是目录  
        inode->i_op = &ext2_dir_inode_operations;  
        inode->i_fop = &ext2_dir_operations;  
    } else if (S_ISLNK(inode->i_mode)) {  
        if (!inode->i_blocks)  
            inode->i_op = &ext2_fast_symlink_inode_operations;  
        else {  
            inode->i_op = &page_symlink_inode_operations;  
            inode->i_mapping->a_ops = &ext2_aops;  
        }  
    } else   
        init_special_inode(inode, inode->i_mode,  
                   le32_to_cpu(raw_inode->i_block[0]));  
    brelse (bh);  
    inode->i_attr_flags = 0;  
    if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) {  
        inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS;  
        inode->i_flags |= S_SYNC;  
    }  
    if (inode->u.ext2_i.i_flags & EXT2_APPEND_FL) {  
        inode->i_attr_flags |= ATTR_FLAG_APPEND;  
        inode->i_flags |= S_APPEND;  
    }  
    if (inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_FL) {  
        inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE;  
        inode->i_flags |= S_IMMUTABLE;  
    }  
    if (inode->u.ext2_i.i_flags & EXT2_NOATIME_FL) {  
        inode->i_attr_flags |= ATTR_FLAG_NOATIME;  
        inode->i_flags |= S_NOATIME;  
    }  
    return;  
      
bad_inode:  
    make_bad_inode(inode);  
    return;  
}  

    其中ext2_dir_inode_operations如下:

[cpp]
view plain
copy

struct inode_operations ext2_dir_inode_operations = {  
    create:     ext2_create,  
    lookup:     ext2_lookup,  
    link:       ext2_link,  
    unlink:     ext2_unlink,  
    symlink:    ext2_symlink,  
    mkdir:      ext2_mkdir,  
    rmdir:      ext2_rmdir,  
    mknod:      ext2_mknod,  
    rename:     ext2_rename,  
};  

    iget获取到该节点的inode结构后,就返回ext2_lookup,继续执行d_add(dentry, inode),把该节点的inode结构和dentry连接在一起。

[cpp]
view plain
copy

static __inline__ void d_add(struct dentry * entry, struct inode * inode)  
{  
    d_instantiate(entry, inode);//该节点的inode结构和dentry连接在一起  
    d_rehash(entry);//把dentry结构放入杂凑表的某一个队里中  
}  

[cpp]
view plain
copy

void d_instantiate(struct dentry *entry, struct inode * inode)  
{  
    spin_lock(&dcache_lock);  
    if (inode)  
        list_add(&entry->d_alias, &inode->i_dentry);//可能一个inode结构对应多个entry结构  
    entry->d_inode = inode;//链接在一起了  
    spin_unlock(&dcache_lock);  
}  

[cpp]
view plain
copy

void d_rehash(struct dentry * entry)  
{  
    struct list_head *list = d_hash(entry->d_parent, entry->d_name.hash);  
    spin_lock(&dcache_lock);  
    list_add(&entry->d_hash, list);  
    spin_unlock(&dcache_lock);  
}  

    而inode结构放入杂凑表的某个队列是在get_new_inode里面:

[cpp]
view plain
copy

list_add(&inode->i_hash, head);  

    
    real_lookup返回该节点的dentry结构。然后检查是否是挂在点,如果不是nd->dentry被赋值为dentry。由于是目录节点,所有lookup指针不能为NULL。

    第一轮循环,执行到这里,continue开始第二轮循环。

[cpp]
view plain
copy

if (!inode->i_op->lookup)  
    break;  
continue;  

    
    第二轮循环,如下:

[cpp]
view plain
copy

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;  
  
    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;//指向local的第一个字符'l'  
        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;//local的长度  
        this.hash = end_name_hash(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 = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE);  
        if (!dentry) {  
            dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE);  
            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)  
            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:  
        ......  
last_component:  
        ......  
}  

    除上面注释外,其他的流程和和第一次循环都一样。
    第二轮循环,也是执行到这里,continue开始第三轮循环:

[cpp]
view plain
copy

if (!inode->i_op->lookup)//由于/usr/local/hello.c,local仍然是目录节点  
    break;  
continue;  

    第三轮循环,如下:

[cpp]
view plain
copy

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;  
  
    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;//指向了hello.c的第一个字符'h'  
        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;//hello.c的长度  
        this.hash = end_name_hash(hash);  
  
        /* remove trailing slashes? */  
        if (!c)//c为NULL了  
            goto last_component;  
        while (*++name == '/');  
        if (!*name)  
            goto last_with_slashes;  
  
        .....  
  
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) {//如果最后一个节点是目录,且是dot或者dotdot  
            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;  
        }  
        while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry))//是否是挂载点  
            ;  
        inode = dentry->d_inode;  
        if ((lookup_flags & LOOKUP_FOLLOW)//和第一次和第二次循环不同,必须LOOKUP_FOLLOW标志位置1  
            && 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;//赋值给nd->dentry  
        }  
        err = -ENOENT;  
        if (!inode)  
            goto no_inode;  
        if (lookup_flags & LOOKUP_DIRECTORY) {//如果最后一个节点是目录,也就是这个标志为置1的时候,还要检查指针是否为NULL  
            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->dentry是父节点的dentry结构  
        nd->last = this;//nd->last是最后一个节点的名字  
        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;  
}  

    real_lookup由于hello.c节点是文件,所以执行如下代码段:

[cpp]
view plain
copy

if (inode->i_ino == EXT2_ACL_IDX_INO ||  
        inode->i_ino == EXT2_ACL_DATA_INO)  
        /* Nothing to do */ ;  
    else if (S_ISREG(inode->i_mode)) {//hello.c是文件,所以执行这段代码  
        inode->i_op = &ext2_file_inode_operations;  
        inode->i_fop = &ext2_file_operations;  
        inode->i_mapping->a_ops = &ext2_aops;  
    } else if (S_ISDIR(inode->i_mode)) {  
        inode->i_op = &ext2_dir_inode_operations;  
        inode->i_fop = &ext2_dir_operations;  
    } else if (S_ISLNK(inode->i_mode)) {  
        if (!inode->i_blocks)  
            inode->i_op = &ext2_fast_symlink_inode_operations;  
        else {  
            inode->i_op = &page_symlink_inode_operations;  
            inode->i_mapping->a_ops = &ext2_aops;  
        }  
    } else   
        init_special_inode(inode, inode->i_mode,  
                   le32_to_cpu(raw_inode->i_block[0]));  

    至此,path_walk的三层循环,都分析完了,nd->dentry最终指向了/usr/local/hello.c这个文件节点的dentry结构,其中的d_inode指向了该节点的inode结构。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: