5
26
2014
0

dentry与inode

首先看dentry数据结构。位于include/linux/dcache.h中 struct dentry

ps:dentry虽然是目录的意思,但是在vfs中,目录和文件都有自己的dentry。(dentry中存了文件名,同一文件存在别名就是这个结构实现的)

struct dentry {
atomic_td_count;目录项对象使用计数器
unsignedintd_flags;目录项标志
structinode*d_inode;与文件名关联的索引节点
structdentry*d_parent;父目录的目录项对象
structlist_headd_hash;散列表表项的指针
structlist_headd_lru;未使用链表的指针
structlist_headd_child;父目录中目录项对象的链表的指针
structlist_headd_subdirs;对目录而言,表示子目录目录项对象的链表
structlist_headd_alias;相关索引节点(别名)的链表
intd_mounted;对于安装点而言,表示被安装文件系统根项
structqstrd_name;文件名
unsignedlongd_time;/*usedbyd_revalidate*/
structdentry_operations*d_op;目录项方法
structsuper_block*d_sb;文件的超级块对象
vunsignedlongd_vfs_flags;
void*d_fsdata;与文件系统相关的数据
unsignedchard_iname[DNAME_INLINE_LEN];存放短文件名
};

再看inode数据结构。include/linux/fs.h

struct inode {/*vfs中数据结构 数据结构中各项的含义见 Linux 内核 465页*/
	struct hlist_node	i_hash;
	struct list_head	i_list;		/* backing dev IO list */
	struct list_head	i_sb_list;
	struct list_head	i_dentry; /*引用该索引节点的目录项对象链表表头*/
	unsigned long		i_ino;/*索引节点号*/
	atomic_t		i_count;
	unsigned int		i_nlink;
	uid_t			i_uid;
	gid_t			i_gid;
	dev_t			i_rdev;
	unsigned int		i_blkbits;
	u64			i_version;
	loff_t			i_size;
#ifdef __NEED_I_SIZE_ORDERED
	seqcount_t		i_size_seqcount;
#endif
	struct timespec		i_atime;
	struct timespec		i_mtime;
	struct timespec		i_ctime;
	blkcnt_t		i_blocks;
	unsigned short          i_bytes;
	umode_t			i_mode;
	spinlock_t		i_lock;	/* i_blocks, i_bytes, maybe i_size */
	struct mutex		i_mutex;
	struct rw_semaphore	i_alloc_sem;
	const struct inode_operations	*i_op;/*与索引结点有关的操作*/
	const struct file_operations	*i_fop;	/* former ->i_op->default_file_ops */
	struct super_block	*i_sb;
	struct file_lock	*i_flock;
	struct address_space	*i_mapping;
	struct address_space	i_data;
#ifdef CONFIG_QUOTA
	struct dquot		*i_dquot[MAXQUOTAS];
#endif
	struct list_head	i_devices;
	union {
		struct pipe_inode_info	*i_pipe;
		struct block_device	*i_bdev;
		struct cdev		*i_cdev;
	};

	__u32			i_generation;

#ifdef CONFIG_FSNOTIFY
	__u32			i_fsnotify_mask; /* all events this inode cares about */
	struct hlist_head	i_fsnotify_mark_entries; /* fsnotify mark entries */
#endif

#ifdef CONFIG_INOTIFY
	struct list_head	inotify_watches; /* watches on this inode */
	struct mutex		inotify_mutex;	/* protects the watches list */
#endif

	unsigned long		i_state;
	unsigned long		dirtied_when;	/* jiffies of first dirtying */

	unsigned int		i_flags;

	atomic_t		i_writecount;
#ifdef CONFIG_SECURITY
	void			*i_security;
#endif
#ifdef CONFIG_FS_POSIX_ACL
	struct posix_acl	*i_acl;
	struct posix_acl	*i_default_acl;
#endif
	void			*i_private; /* fs or device private pointer */
}

"文件"即按一定形式存储在介质上的信息,该信息包含两方面,其一是存储的数据本身,其二是概述的索引。在内存中,每个文件都有一个inode和dentry结构。dentry记录文件名,上级目录,子目录等信息,正是我们看到的树状结构;inode记录着文件在存储介质上的位置和分布,dentry->d_inode指向对应的inode结构。inode代表物理意义上的文件,通过inode可以得到一个数组,这个数组记录文件内容的位置,若数组为(4,5,9),则对应数据位于硬盘的4,5,9块。其索引节点号为inode->ino,根据ino就可以计算出对应硬盘中inode的具体位置。


dentry所描述的是逻辑意义上的文件,所描述的是文件逻辑上的属性,所以dentry在磁盘上没有对应的影像;而inode结构代表的是物理意义上的文件,故磁盘上也有inode结构

inode结构中有一个队列i_dentry,凡是代表同一个文件的所有目录项都通过d_alias域挂入响应的inode结构中的i_dentry队列。


进程打开一个文件,就会有一个file结构与之对应,同一个进程可以多次打开同一个文件而得到多个不同的file结构,file结构描述了被打开文件的属性,读写偏移指针等

两个不同的file可以对应同一个dentry结构,进程多次打开一个文件,对应只有一个dentry结构,dentry存的是目录项和对应文件的inode的信息。

在介质中,每个文件对应一个inode结点,每个文件可有多个文件名,即可以通过不同的文件名访问同一个文件,多个文件名对应一个文件的关系就是数据结构中dentry和inode多对一的关系。inode中不存储文件名字,只有节点号,通过节点号(ino),可以找到数据在介质中的具体位置,即通过内存inode结构中的ino可以定位到磁盘上的inode结构;而dentry则保存文件名和对应的节点号(inode号),这样就可以实现不同文件名访问一个inode。不同的dentry是通过ln指令实现的。


dentry树描绘了文件系统目录结构,但整个目录结构不能长驻内存,因为非常大。内存装不下。

初始状态下,系统只有代表根目录的dentry和所指向的inode(在根文件系统中挂载生成)。此时要打开一个文件,文件路径对应的结点不存在,根目录的dentry无法找到需要的子节点。这时需要通过inode->i_op中lookup方法找到inode的子节点,找到后,再创建一个dentry与之关联。

由这个过程可以看出。现有inode再有dentry。

当生成的dentry无人使用时被释放,d_count字段记录了dentry的引用计数,引用为0时,dentry被释放。这里的释放不是直接销毁,而是将dentry放入一个“最近最少使用”队列。当队列过大,内存紧缺时,dentry才被释放。这个LRU队列就像是一个缓存池, 加速了对重复的路径的访问. 而当dentry被真正释放时, 它所对应的inode将被减引用. 如果引用为0, inode也被释放.

故需找一个文件路径是有三种情况:

1.dentry引用大于0,直接在dentry树种找

2.dentry不在树种,在lru队列中找,LRU队列中的dentry被散列到一个散列表中,便于查找,若找到,则重新添加到dentry树种。

3.若2也未找到,则区找inode,找到后,再创建对应的dentry

这里补充每个目录项对象可以出于以下四种状态之一:

1:空闲状态  即该目录项不包含有效的信息,且没被VFS使用。

2.未使用状态  该对象的d_count计数为0,但d_inode指向关联的索引节点

3.正在使用状态  该对象的d_count计数不为0,但d_inode指向关联的索引节点

4.负状态   与目录项关联的索引结点不存在了


mount过程:

linux首先找到磁盘分区的super block,然后通过解析磁盘的inode table与file data。构建出自己的dentry列表和inode列表。VFS是按照Ext的方法进行构建的,两者非常相似。如inode结点。ext_inode结点中的一些成员变量其实是没有用的,如引用计数等,保留目的是为了和vfs-inode保持一致,这样在用ext_inode构建vfs_inode时,就不需要一个个赋值,只需要一次拷贝。

故非ext格式的文件系统,mount的过程会慢一些


根目录有一个dentry结构,而根目录里的文件盒目录都链接到这个跟dentry;同样的道理,二级目录里的文件和目录链接到二级目录,这样一层层,就形成了一颗dentry树。从树顶可以遍历整个文件系统。

为了加快对dentry的查找,内核使用hash表来缓存dentry,称为dentry cache,dentry一般现在dentry cache中查找。

 

dentry结构里有d_subdirs成员和d_child成员。d_subdirs是子项的链表头,所有的子项都要链接在这个链表上,d_child是自身链表头,需要连接到父dentry的d_subdirs成员。

d_parent是指针,指向父dentry结构。

d_hash是连接到dentry cache的hash链表。

d_name保存目录或文件的名字。打开一个文件的时候,根据这个成员和用户输入的名字来搜寻目标。

d_mounted用来指示dentry是否是一个挂载点。如果是,则成员不为零。


dentry的hash定位,通过d_hash()函数将父目录dentry的地址和所要查找的文件名的hash值结合起来,重新计算一个hash值,并根据其定位到dentry_hashtable哈希表中(即定位某个表头,缩小查找范围),该表是dentry缓存一部分,接下来扫描该链表,从中找到目标dentry,若没有找到,则通过real_lookup()函数从磁盘中查找。


目录项对象存放在dentry_cache的slab高速缓存中。故目录项的创建和删除是通过kmem_cache_alloc()和kmem_cache_free()实现的。目录项高速缓存由两种类型数据结构组成:

1.处于正在使用、未使用或负状态的目录项对象的集合。

2.一个散列表。用于hash快速查找给定文件名和目录名对应的目录项对象。散列表是由dentry_hashtable数组实现。数组中每个元素是一个指向链表的指针。

 

参考:

http://book.2cto.com/201312/38229.html

http://blog.csdn.net/vanbreaker/article/details/8208173
http://blog.chinaunix.net/uid-20184656-id-3151111.html

http://blog.csdn.net/fudan_abc/article/details/1775313

Category: 文件系统 | Tags: | Read Count: 2975

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com