Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3844682
  • 博文数量: 146
  • 博客积分: 3918
  • 博客等级: 少校
  • 技术积分: 8584
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-17 13:52
个人简介

个人微薄: weibo.com/manuscola

文章分类

全部博文(146)

文章存档

2016年(3)

2015年(2)

2014年(5)

2013年(42)

2012年(31)

2011年(58)

2010年(5)

分类: LINUX

2011-11-08 21:57:51

  1. 首先看下dentry的定义:

  2. #define DNAME_INLINE_LEN_MIN 36

  3. struct dentry {
  4.     atomic_t d_count;
  5.     unsigned int d_flags;        /* protected by d_lock */
  6.     spinlock_t d_lock;        /* per dentry lock */
  7.     struct inode *d_inode;        /* Where the name belongs to - NULL is
  8.                      * negative */
  9.     /*
  10.      * The next three fields are touched by __d_lookup. Place them here
  11.      * so they all fit in a cache line.
  12.      */
  13.     struct hlist_node d_hash;    /* lookup hash list */
  14.     struct dentry *d_parent;    /* parent directory */
  15.     struct qstr d_name;

  16.     struct list_head d_lru;        /* LRU list */
  17.     /*
  18.      * d_child and d_rcu can share memory
  19.      */
  20.     union {
  21.         struct list_head d_child;    /* child of parent list */
  22.          struct rcu_head d_rcu;
  23.     } d_u;
  24.     struct list_head d_subdirs;    /* our children */
  25.     struct list_head d_alias;    /* inode alias list */
  26.     unsigned long d_time;        /* used by d_revalidate */
  27.     struct dentry_operations *d_op;
  28.     struct super_block *d_sb;    /* The root of the dentry tree */
  29.     void *d_fsdata;            /* fs-specific data */
  30. #ifdef CONFIG_PROFILING
  31.     struct dcookie_struct *d_cookie; /* cookie, if any */
  32. #endif
  33.     int d_mounted;
  34.     unsigned char d_iname[DNAME_INLINE_LEN_MIN];    /* small names */
  35. };
下面结合相关函数解释相应的字段:

1 d_alloc

  1. struct dentry *d_alloc(struct dentry *parent,const struct qstr *name)
  2. {
  3.         struct dentry *dentry;
  4.         char *dname;

  5.         dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);
  6.         if (!dentry)
  7.                 return NULL;

  8.         if (name->len > DNAME_INLINE_LEN-1) {
  9.                 dname = kmalloc(name->len + 1, GFP_KERNEL);
  10.                 if (!dname) {
  11.                         kmem_cache_free(dentry_cache, dentry);
  12.                         return NULL;
  13.                 }
  14.         } else {
  15.                 dname = dentry->d_iname;
  16.         }
  17.              
  18.         dentry->d_name.name = dname;

  19.         dentry->d_name.len = name->len;
  20.         dentry->d_name.hash = name->hash;
  21.         memcpy(dname, name->name, name->len);
  22.         dname[name->len] = 0;

  23.         atomic_set(&dentry->d_count, 1);
  24.         dentry->d_flags = DCACHE_UNHASHED;
  25.         spin_lock_init(&dentry->d_lock);
  26.         dentry->d_inode = NULL;
  27.         dentry->d_parent = NULL;
  28.         dentry->d_sb = NULL;
  29.         dentry->d_op = NULL;
  30.         dentry->d_fsdata = NULL;
  31.         dentry->d_mounted = 0;
  32. #ifdef CONFIG_PROFILING
  33.         dentry->d_cookie = NULL;
  34. #endif
  35.         INIT_HLIST_NODE(&dentry->d_hash);
  36.         INIT_LIST_HEAD(&dentry->d_lru);
  37.         INIT_LIST_HEAD(&dentry->d_subdirs);
  38.         INIT_LIST_HEAD(&dentry->d_alias);

  39.         if (parent) {
  40.                 dentry->d_parent = dget(parent);
  41.                 dentry->d_sb = parent->d_sb;
  42.         } else {
  43.                 INIT_LIST_HEAD(&dentry->d_u.d_child);
  44.         }

  45.         spin_lock(&dcache_lock);
  46.         if (parent)
  47.                 list_add(&dentry->d_u.d_child, &parent->d_subdirs);
  48.         dentry_stat.nr_dentry++;
  49.         spin_unlock(&dcache_lock);

  50.         return dentry;
  51. }
首先调用kmem_cache_alloc分配一个dentry实例。
如果文件名的长度小于36,那么文件名存储到dentry的d_iname字符串数组中,否则,分配空间。

  1. dentry->d_parent = dget(parent);

如果存在父目录的dentry,那么父目录引用计数d_count自加。d_parent,顾名思义,指向父目录的dentry实例。如果是根目录,没有父目录,那么指向自身的dentry实例。

当dentry的d_count字段为0的时候,表示没有程序在使用这个dentry,就会把dentry通过d_lru链入到dentry_unused.新链入的对象插入到链表头。
  1. dentry->d_sb = parent->d_sb;
 d_sb是个指针,指向目录项缓存所属文件系统的超级快。这个指针的值继承它父目录缓存的值。

  1. if (parent)
  2.           list_add(&dentry->d_u.d_child, &parent->d_subdirs);
d_subdirs这个字段是一个链表头,dentry把自己目录下的子文件链接在一起。上面代码的含义是如果dentry有父亲,那么将自己链接在父亲dentry实例的以d_subdirs为头得链表上。


2 d_instantiate函数
  1. void d_instantiate(struct dentry *entry, struct inode * inode)
  2. {
  3.         BUG_ON(!list_empty(&entry->d_alias));
  4.         spin_lock(&dcache_lock);
  5.         if (inode)
  6.                 list_add(&entry->d_alias, &inode->i_dentry);
  7.         entry->d_inode = inode;
  8.         fsnotify_d_instantiate(entry, inode);
  9.         spin_unlock(&dcache_lock);
  10.         security_d_instantiate(entry, inode);
  11. }

  1. entry->d_inode = inode;
每个dentry结构都通过指针d_inode指向一个inode数据结构,当然多个dentry也可以指向同一个inode。这是什么意思呢?指向同一个文件,表示他们本质是同一个文件,虽然有多个dentry。利用硬链接来给文件起小名的时候,会出现多个dentry,指向同一个inode。

硬链接的本质就是小名,比如我大名叫李彬,小名二蛋儿,虽然两个名字,但是其实都是我自己。

            
  1. list_add(&entry->d_alias, &inode->i_dentry)
inode中有个字段i_dentry,这是个链表头,所有指向同一个inode的dentry都可以链到这个链表中。怎么链呢?dentry提供了d_alias字段。这样inode和dentry就建立了联系,织起了关系网。


3 d_add函数
  1. static inline void d_add(struct dentry *entry, struct inode *inode)
  2. {
  3.         d_instantiate(entry, inode);
  4.         d_rehash(entry);
  5. }

  6. void d_rehash(struct dentry * entry)
  7. {
  8.         spin_lock(&dcache_lock);
  9.         spin_lock(&entry->d_lock);
  10.         _d_rehash(entry);
  11.         spin_unlock(&entry->d_lock);
  12.         spin_unlock(&dcache_lock);
  13. }

  14. static void _d_rehash(struct dentry * entry)
  15. {
  16.         __d_rehash(entry, d_hash(entry->d_parent, entry->d_name.hash));
  17. }

  18. static void __d_rehash(struct dentry * entry,struct hlist_head *list)
  19. {

  20.          entry->d_flags &= ~DCACHE_UNHASHED;
  21.          hlist_add_head_rcu(&entry->d_hash, list);
  22. }
 
  1. d_instantiate(entry, inode);
调用d_instantiate(entry, inode),dentry建立和inode的战略伙伴关系。

  1. d_rehash(entry);
内存中有很多的dentry实例,为了方便查找,需要建立哈希表。有个全局变量dentry_hashtable。
先通过计算hash值,来找到dentry_hashtable对应的桶,然后链入桶对应的链表中。dentry中的字段d_hash是链表结构。链入到桶的最前端。新插入的放在最前面。

  1. static void _d_rehash(struct dentry * entry)
  2. {
  3.      __d_rehash(entry, d_hash(entry->d_parent, entry->d_name.hash));
  4. }
我们注意下,计算hash值得时候,除了使用了dentry中的d_name中hash的值,还使用了它的父目录。毛德操老爷子书中讲的比较明白,/home目录下可能存在 Tom。John,Frank,等很多子目录,但是Tom目录下可能有project,John目录下可能也有project,如果不使用父目录的信息,那么,可能这些project都会hash到同一个桶中,这不是我们希望看到的。

  1. static inline struct hlist_head *d_hash(struct dentry *parent,
  2.                                         unsigned long hash)
  3. {
  4.         hash += ((unsigned long) parent ^ GOLDEN_RATIO_PRIME) / L1_CACHE_BYTES;
  5.         hash = hash ^ ((hash ^ GOLDEN_RATIO_PRIME) >> D_HASHBITS);
  6.         return dentry_hashtable + (hash & D_HASHMASK);
  7. }
d_hash将父目录对应的dentry的指针,也投入hash运算,前面提到的Tom和John下,虽然都有project,但是他们不会hash都同一个桶中。


  1. static void __d_rehash(struct dentry * entry,struct hlist_head *list)
  2. {
  3.    entry->d_flags &= ~DCACHE_UNHASHED;
  4.    hlist_add_head_rcu(&entry->d_hash, list);
  5. }

这个里面将d_flags字段的DCACHE_UNHASHED 位清零了。
dentry中存在字段d_flags,DCACHE_UNHASHED 位表示该dentry实例并没有添加到hash表中,既然我们已经计算了hash值,并且将dentry链入了全局hash表相应的桶中,这个标志位自然需要清零了。


参考文献:
1 毛德操 linux内核源代码情景分析
2 深入linux内核架构






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

Heartwork2011-12-05 21:52:52

今天下了linux-3.1.4的代码,发现确实如此:

struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
{
    struct dentry *dentry = __d_alloc(parent->d_sb, name);
    if (!dentry)
        return NULL;

    spin_lock(&parent->d_lock);
    /*
     * don't need child lock because it is not subject
     * to

Heartwork2011-12-05 17:42:55

下面的这部分代码很奇怪:
        spin_lock(&dcache_lock);
        if (parent)
                list_add(&dentry->d_u.d_child, &parent->d_subdirs);
        dentry_stat.nr_dentry++;
        spin_unlock(&dcache_lock);

这段代码,貌似应该加两个锁:
一个用来保护parent的dentry结构,一个用来保护dentry

☆彼岸★花开2011-11-10 03:18:55

好长啊……学习了~