ILD

dentry lifetime
作者:Yuan Jianpeng 邮箱:yuanjp89@163.com
发布时间:2026-3-23 站点:Inside Linux Development

dentry有两个哈希表,一个是主哈希表,一个是in lookup哈希表。


Alloc

分配一个alloc,接口有:

struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
struct dentry *d_alloc_name(struct dentry *parent, const char *name)

这两个接口一个传参是qstr,qstr要求计算过hash,一个是纯字符串,

这两个接口只是单纯分配dentry,不去查找hash表,也不加入哈希表。因此这两个接口不用任何锁。


1 分配一个dentry,并且初始化,

d_lockref.count初始化为1

d_flags和d_inode初始化为NULL。

d_parent初始化为parent

d_sb初始化为parent->d_sb。


2 挂在parent下面

hlist_add_head(&dentry->d_sib, &parent->d_children);

这里要上parent的d_lock。


New

第二个alloc接口是非常重要的,namei里面的lookup等业务是调这个接口

struct dentry *d_alloc_parallel(struct dentry *parent,
                                const struct qstr *name,
                                wait_queue_head_t *wq)


这个接口支持并行操作,也就是说只需要上parent的inode的共享锁即可。

inode_lock_shared(parent->d_inode);

当然上写锁也是可以的。


1 它首先调用d_alloc()分配一个dentry。

这个dentry会做备用,后面如果找到了,会把这个dentry使用dput(dentry)释放掉。


2 然后使用RCU方式去主hash表查找,如果找到了,说明一个并行的操作已经完成了,并且进了主hash表。

所以释放分配的dentry,直接返回找到的dentry。


3 如果没找到,就去in lookup哈希表查找,如果找到,

则表示有一个并行的操作正在进行(可能正在执行文件系统的lookup(),还没有完成)

此时调用,d_wait_lookup()等待wait queue,完成。

完成后,释放分配的dentry,返回找到的这个dentry。


4 最后都没找到,那么就返回分配的这个dentry。

a. 设置d_flags包含DCACHE_PAR_LOOKUP

b. 保存wq到d_wait

c. 添加到in lookup 哈希表。


d_in_lookup()

可以判断一个dentry,是不是在in lookup哈希表中,其判断DCACHE_PAR_LOOKUP标志。


Add

将一个dentry添加到哈希表

void d_add(struct dentry *entry, struct inode *inode)

inode可以为NULL,这样会产生一个negative dentry


1 如果dentry,在in lookup哈希表,则将其移除,移除前调用start_dir_add()给父目录的inode上i_dir_seq锁。

start_dir_add()

先禁止抢占,然后循环判断i_dir_seq是否为偶数,奇数表示正在修改,奇数继续循环,期间调用cpu_relax,

如果是偶数,则加+1,调用cmpxchg实现原子操作。

调用__d_lookup_unhash(),将其从in lookup哈希表中移除。


2 关联dentry和inode

设置dentry->inode,以及flags。


3 加入主hash表

__d_rehash(dentry);


4 如果之前在in lookup哈希表,做结束处理:

end_dir_add(dir, n, d_wait);

增加i_dir_seq,唤醒等待队列。



Instantiate

关联一个dentry和inode,和Add的不同,就是不处理哈希表,原来不再hash表中,则不会添加。

void d_instantiate(struct dentry *entry, struct inode * inode)


Splice alias

和add类似,不过对于目录,它有特殊的处理。

struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)


1 找到inode是否有之前的dentry关联。

struct dentry *new = __d_find_any_alias(inode);


2 如果有,那么调用:

__d_unalias(dentry, new)

进行移动操作


fs的lookup()接口,最终要调用此函数,来关联dentry和inode。


Move

移动dentry到另外一个dentry

void d_move(struct dentry *dentry, struct dentry *target)

整个操作在rename_lock全局锁中保护


dentry和target都从hash表中移除。

然后dentry的parent等于target的parent,名字也拷贝过来,

然后重新哈希dentry。


target如果在in lookup哈希表中,走

start_dir_add()/__d_lookup_unhash()/end_dir_add()流程。


target从哈希表中摘除了,其它字段没变。

通常由调用者释放target及其inode。


Drop

将dentry从哈希表中移除,其它啥都不变。

void __d_drop(struct dentry *dentry);
void d_drop(struct dentry *dentry);


Rehash

将dentry重新添加到hash队列

void d_rehash(struct dentry *);


Delete

从哈希表移除,或者将其变成negative。

void d_delete(struct dentry * dentry)


如果有多个引用计数,则将其从哈希表中拿掉,但是inode/parent这些字段不动。

如果只有最后一个引用计数,则

dentry->d_flags &= ~DCACHE_CANT_MOUNT;

设置为不可挂载,同时调用

dentry_unlink_inode(dentry);


Put

释放一个引用计数

void dput(struct dentry *dentry)







Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.