目录
本专栏文章将有70篇左右,欢迎+关注,查看后续文章。
8.3 VFS结构
8.3.5 目录项缓存
struct dentry:即目录项。
作用:缓存文件名对应的 inode。加快文件查找。
如果通过文件名查找 inode,很耗时。所以当读取一个目录项(目录或文件)后,会创建 dentry 实例,并缓存查找到路径。
目录项缓存:dentry cache,简称dcache。
不管文件夹,目录还是文件,都可用一个目录项表示。
如:打开文件 /etc/init.d/lte.sh 后,/、etc、init.d、lte.sh 都有一个 struct dentry 实例。
内核只保存常用的目录和文件对应的 struct dentry。
每个dentry对象有四种状态:
空闲:该 dentry 不包含有效信息,没有被 VFS 使用。
未使用:没有被内核使用,即 d_count 为0,但 d_inode 指向对应 inode。
正使用:d_count 大于0,d_inode 指向对应 inode。
负状态:不存在对应 inode,相应磁盘索引节点已删除。
1. struct dentry结构体
struct dentry { //1. 父子链表 2. 关联 inode和文件名d_flags
unsigned int d_flags;
struct hlist_bl_node d_hash;
//用于连接全局哈希表 dentry_hashtable 中冲突项。
//dentry_hashtable:包含系统所有struct dentry。
struct dentry *d_parent;
//父目录的 dentry(若为根目录,则指向自身dentry)
struct qstr d_name;
//文件名称,如 /usr/bin/vim 中的vim, 而 /usr/bin 包含在d_child。
//struct qstr:包含了文件名及其hash值。(比较hash值来简化查找)
struct inode *d_inode;
//d_name 文件对应的 inode,文件名不存在则为 NULL,用于查找不存在的文件。
unsigned char d_iname[16];
// 短文件名,若文件名小于32字节则使用 d_iname,而不是 d_name。
unsigned int d_count; //引用计数。
struct dentry_operations *d_op; // 文件系统需实现。
struct super_block *d_sb; //所属超级块。
unsigned long d_time;
void *d_fsdata; //文件系统私有数据。
union {
struct list_head d_lru;
// 用于连接到超级块的 s_dentry_lru 链表,即最近未使用的目录项。
wait_queue_head_t *d_wait;
};
struct list_head d_child; // 用于连接到父目录 dentry 的 d_subdirs 中。
struct list_head d_subdirs; // 包含的子目录。
union {
struct hlist_node d_alias;
//用于连接到struct inode中的i_dentry中。
//一个inode可有多个dentry。比如硬链接。
struct hlist_bl_node d_in_lookup_hash;
struct rcu_head d_rcu;
} d_u;
}
成员介绍:
d_flags:
1. DCACHE_DISCONNECTED:
该dentry没有连接到super_block的dentry树。
2. DCACHE_UNHASHED:
该dentry没有包含在任何inode hash表。
3. DCACHE_REFERENCED:
该dentry最近使用,不被回收。
同一文件的所有 dentry 都通过 d_alias 域挂入该文件inode中的 i_dentry 队列。
注:进程多次打开同一文件,将生成多个struct file,但只有一个struct dentry。
2. 缓存的组织
dentry 目的:加速VFS处理。
一个 dentry 在内存的两种组织:
1. 全局散列表:
即 dentry_hashtable:包含所有 dentry 对象。
dentry 的 d_hash 用于确定散列冲突位置。
2. LRU链表:
若 dentry 的 d_count 为0则加入 LRU 链表。若太老则从内存删除。
list_add(&dentry->d_lru, &dentry->d_sb->s_dentry_lru);
3. dentry操作
struct dentry_operations {
int (*d_revalidate)(struct dentry *, unsigned int);
//被网络文件系统使用。用于确保一致性,使某些dentry过时。
int (*d_weak_revalidate)(struct dentry *, unsigned int);
int (*d_hash)(const struct dentry *, struct qstr *);
//计算dentry的hash值。将其放入dentry hash表。
int (*d_compare)(const struct dentry *,const struct qstr *);
//比较文件名,不同文件系统可自定义是否区分大小写。
void (*d_prune)(struct dentry *);
void (*d_iput)(struct dentry *, struct inode *);
//将inode引用计数减1,并释放inode。
int (*d_delete)(const struct dentry *);
//当dentry的d_count为0,用该函数删除dentry。默认为NULL。
void (*d_release)(struct dentry *);
//默认为NULL。
char *(*d_dname)(struct dentry *, char *, int);
int (*d_manage)(const struct path *, bool);
struct vfsmount *(*d_automount)(struct path *);
};
4. 标准函数
struct dentry *dget(struct dentry *dentry)
作用:dentry 的 d_count + 1 (引用计数加 1 )。
struct dentry *dput(struct dentry *dentry)
作用:
1. dentry 的 d_count - 1。
2. 若 d_count 为0,则调用 dentry->d_op->d_delete(dentry);
void d_drop(struct dentry *dentry)
作用:
1. 将 dentry 从全局散列表中删除。
2. 若 d_count 为0,可被 dput 函数调用。
void d_instantiate(struct dentry *entry, struct inode *inode);
作用:
1. 关联一个 dentry 和一个 inode。
2. 将 dentry->d_inode 连接到 inode->i_dentry 链表。
void d_add(struct dentry *entry, struct inode *inode)
作用:
1. 调用 d_instantiate。
2. 将 dentry 加入全局散列表。
struct dentry *d_alloc(struct dentry * parent, struct qstr *name)
作用:
1. 分配一个dentry内存,并初始化。
2. 将dentry加入到 parent->d_subdirs 链表中。
struct dentry *d_alloc_anon(struct super_block *sb)
作用:
1. 分配一个 dentry 内存。
2. 将dentry加入两个链表:super_block->s_anon ,inode->i_dentry。
struct dentry *d_lookup(const struct dentry *parent, struct qstr *name)
作用:在指定目录 parent 中,搜索文件名为 name 的 dentry。