- 首先看下dentry的定义:
- #define DNAME_INLINE_LEN_MIN 36
- struct dentry {
-
atomic_t d_count;
-
unsigned int d_flags; /* protected by d_lock */
-
spinlock_t d_lock; /* per dentry lock */
-
struct inode *d_inode; /* Where the name belongs to - NULL is
-
* negative */
-
/*
-
* The next three fields are touched by __d_lookup. Place them here
-
* so they all fit in a cache line.
-
*/
-
struct hlist_node d_hash; /* lookup hash list */
-
struct dentry *d_parent; /* parent directory */
-
struct qstr d_name;
-
-
struct list_head d_lru; /* LRU list */
-
/*
-
* d_child and d_rcu can share memory
-
*/
-
union {
-
struct list_head d_child; /* child of parent list */
-
struct rcu_head d_rcu;
-
} d_u;
-
struct list_head d_subdirs; /* our children */
-
struct list_head d_alias; /* inode alias list */
-
unsigned long d_time; /* used by d_revalidate */
-
struct dentry_operations *d_op;
-
struct super_block *d_sb; /* The root of the dentry tree */
-
void *d_fsdata; /* fs-specific data */
-
#ifdef CONFIG_PROFILING
-
struct dcookie_struct *d_cookie; /* cookie, if any */
-
#endif
-
int d_mounted;
-
unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */
-
};
下面结合相关函数解释相应的字段:
1 d_alloc
- struct dentry *d_alloc(struct dentry *parent,const struct qstr *name)
-
{
-
struct dentry *dentry;
-
char *dname;
-
-
dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);
-
if (!dentry)
-
return NULL;
-
-
if (name->len > DNAME_INLINE_LEN-1) {
-
dname = kmalloc(name->len + 1, GFP_KERNEL);
-
if (!dname) {
-
kmem_cache_free(dentry_cache, dentry);
-
return NULL;
-
}
-
} else {
-
dname = dentry->d_iname;
-
}
-
-
dentry->d_name.name = dname;
-
-
dentry->d_name.len = name->len;
-
dentry->d_name.hash = name->hash;
-
memcpy(dname, name->name, name->len);
-
dname[name->len] = 0;
-
-
atomic_set(&dentry->d_count, 1);
-
dentry->d_flags = DCACHE_UNHASHED;
-
spin_lock_init(&dentry->d_lock);
-
dentry->d_inode = NULL;
-
dentry->d_parent = NULL;
-
dentry->d_sb = NULL;
-
dentry->d_op = NULL;
-
dentry->d_fsdata = NULL;
-
dentry->d_mounted = 0;
-
#ifdef CONFIG_PROFILING
-
dentry->d_cookie = NULL;
-
#endif
-
INIT_HLIST_NODE(&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;
-
} else {
-
INIT_LIST_HEAD(&dentry->d_u.d_child);
-
}
-
-
spin_lock(&dcache_lock);
-
if (parent)
-
list_add(&dentry->d_u.d_child, &parent->d_subdirs);
-
dentry_stat.nr_dentry++;
-
spin_unlock(&dcache_lock);
-
-
return dentry;
-
}
首先调用kmem_cache_alloc分配一个dentry实例。
如果文件名的长度小于36,那么文件名存储到dentry的d_iname字符串数组中,否则,分配空间。
- dentry->d_parent = dget(parent);
如果存在父目录的dentry,那么父目录引用计数d_count自加。d_parent,顾名思义,指向父目录的dentry实例。如果是根目录,没有父目录,那么指向自身的dentry实例。
当dentry的d_count字段为0的时候,表示没有程序在使用这个dentry,就会把dentry通过d_lru链入到dentry_unused.新链入的对象插入到链表头。
- dentry->d_sb = parent->d_sb;
d_sb是个指针,指向目录项缓存所属文件系统的超级快。这个指针的值继承它父目录缓存的值。
- if (parent)
-
list_add(&dentry->d_u.d_child, &parent->d_subdirs);
d_subdirs这个字段是一个链表头,dentry把自己目录下的子文件链接在一起。上面代码的含义是如果dentry有父亲,那么将自己链接在父亲dentry实例的以d_subdirs为头得链表上。
2 d_instantiate函数
- void d_instantiate(struct dentry *entry, struct inode * inode)
-
{
-
BUG_ON(!list_empty(&entry->d_alias));
-
spin_lock(&dcache_lock);
-
if (inode)
-
list_add(&entry->d_alias, &inode->i_dentry);
-
entry->d_inode = inode;
-
fsnotify_d_instantiate(entry, inode);
-
spin_unlock(&dcache_lock);
-
security_d_instantiate(entry, inode);
-
}
每个dentry结构都通过指针d_inode指向一个inode数据结构,当然多个dentry也可以指向同一个inode。这是什么意思呢?指向同一个文件,表示他们本质是同一个文件,虽然有多个dentry。利用硬链接来给文件起小名的时候,会出现多个dentry,指向同一个inode。
硬链接的本质就是小名,比如我大名叫李彬,小名二蛋儿,虽然两个名字,但是其实都是我自己。
- list_add(&entry->d_alias, &inode->i_dentry)
inode中有个字段i_dentry,这是个链表头,所有指向同一个inode的dentry都可以链到这个链表中。怎么链呢?dentry提供了d_alias字段。这样inode和dentry就建立了联系,织起了关系网。
3 d_add函数
- static inline void d_add(struct dentry *entry, struct inode *inode)
-
{
-
d_instantiate(entry, inode);
-
d_rehash(entry);
-
}
-
-
void d_rehash(struct dentry * entry)
-
{
-
spin_lock(&dcache_lock);
-
spin_lock(&entry->d_lock);
-
_d_rehash(entry);
-
spin_unlock(&entry->d_lock);
-
spin_unlock(&dcache_lock);
-
}
-
-
static void _d_rehash(struct dentry * entry)
-
{
-
__d_rehash(entry, d_hash(entry->d_parent, entry->d_name.hash));
-
}
-
-
static void __d_rehash(struct dentry * entry,struct hlist_head *list)
-
{
-
-
entry->d_flags &= ~DCACHE_UNHASHED;
-
hlist_add_head_rcu(&entry->d_hash, list);
-
}
- d_instantiate(entry, inode);
调用d_instantiate(entry, inode),dentry建立和inode的战略伙伴关系。
内存中有很多的dentry实例,为了方便查找,需要建立哈希表。有个全局变量dentry_hashtable。
先通过计算hash值,来找到dentry_hashtable对应的桶,然后链入桶对应的链表中。dentry中的字段d_hash是链表结构。链入到桶的最前端。新插入的放在最前面。
- static void _d_rehash(struct dentry * entry)
-
{
- __d_rehash(entry, d_hash(entry->d_parent, entry->d_name.hash));
-
}
我们注意下,计算hash值得时候,除了使用了dentry中的d_name中hash的值,还使用了它的父目录。毛德操老爷子书中讲的比较明白,/home目录下可能存在 Tom。John,Frank,等很多子目录,但是Tom目录下可能有project,John目录下可能也有project,如果不使用父目录的信息,那么,可能这些project都会hash到同一个桶中,这不是我们希望看到的。
- static inline struct hlist_head *d_hash(struct dentry *parent,
-
unsigned long hash)
-
{
-
hash += ((unsigned long) parent ^ GOLDEN_RATIO_PRIME) / L1_CACHE_BYTES;
-
hash = hash ^ ((hash ^ GOLDEN_RATIO_PRIME) >> D_HASHBITS);
-
return dentry_hashtable + (hash & D_HASHMASK);
-
}
d_hash将父目录对应的dentry的指针,也投入hash运算,前面提到的Tom和John下,虽然都有project,但是他们不会hash都同一个桶中。
- static void __d_rehash(struct dentry * entry,struct hlist_head *list)
-
{
- entry->d_flags &= ~DCACHE_UNHASHED;
- hlist_add_head_rcu(&entry->d_hash, list);
-
}
这个里面将d_flags字段的DCACHE_UNHASHED 位清零了。
dentry中存在字段d_flags,DCACHE_UNHASHED 位表示该dentry实例并没有添加到hash表中,既然我们已经计算了hash值,并且将dentry链入了全局hash表相应的桶中,这个标志位自然需要清零了。
参考文献:
1 毛德操 linux内核源代码情景分析
2 深入linux内核架构
阅读(6178) | 评论(3) | 转发(0) |