首先看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
2022年9月16日 21:42
All Andhra Pradesh State Class 10th (SSC) Students can download AP SSC Science model paper 2023 with Answer solutions in Chapter by Chapter for Physical Science, Chemistry, Biological Science and Environmental science for AP SSC Science Model Paper 2023 examination test for Unit Test-1, Unit Test-2, AP SSC General Science Model Paper Unit Test-3, Unit Test-4, and Quarterly, Half Yearly, Pre-Final with Annual final examination tests 2023. The Andhra Pradesh State Board of Secondary Education has published the General Science model paper with study material with practice paper with mock test question bank as AP 10th Biology, PS, Chemistry Model Paper 2023 for all examination tests conducted by BSEAP.