Linux文件系统的四大对象

本来想看驱动的,但发现要对驱动有底,那还得全方位的了解文件系统。

1 inode 简介

inode 是 UNIX/Linux 操作系统中的一种数据结构,其本质是结构体,它包含了与文件系统中各个文件相关的一些重要信息,例如文件及目录的基本信息,包含时间、档名、使用者及群组等。在 UNIX/Linux中创建文件系统时,同时将会创建大量的 inode 。通常,文件系统磁盘空间中大约百分之一空间分配给了 inode 表。在Linux系统中,内核为每一个新创建的文件分配一个Inode(索引结点),每个文件都有一个惟一的inode号,我们可以将inode简单理解成一个指针,它永远指向本文件的具体存储位置。文件属性保存在索引结点里,在访问文件时,索引结点被复制到内存在,从而实现文件的快速访问。系统是通过索引节点(而不是文件名)来定位每一个文件。

Block 是记录文件内容数据的区域,至于 inode 则是记录'该文件的相关属性,以及文件内容放置在哪一个 Block 之内'的信息,换句说, inode 除了记录文件的属性外,同时还必须要具有指向( pointer )的功能,亦即指向文件内容放置的区块之中,好让操作系统可以正确的去取得文件的内容,底下几个是 inode 记录的信息(当然不止这些):

该文件的拥有者与群组(owner/group); 该文件的存取模式(read/write/excute); 该文件的类型(type);该文件建立或状态改变的时间(ctime)、最近一次的读取时间(atime)、最近修改的时间(mtime);该文件的容量;定义文件特性的旗标(flag),如 SetUID...;该文件真正内容的指向 (pointer); 文件所使用的磁盘块的实际数目;

2 dentry简介

dentry是Linux文件系统中某个索引节点(inode)的链接,每个dentry代表路径中的一个特定部分。这个索引节点可以是文件,也可以是目录。inode(可理解为ext2 inode)对应于物理磁盘上的具体对象,dentry是一个内存实体,没有对应的磁盘数据结构,VFS根据字符串形式的路径名现场创建他,在dentry中,d_inode成员指向对应的inode。也就是说,一个inode可以在运行的时候链接多个dentry,而d_count记录了这个链接的数量。另外dentry对象有三种状态:被使用,未被使用和负状态。

目录项是描述文件的逻辑属性,只存在于内存中,并没有实际对应的磁盘上的描述,更确切的说是存在于内存的目录项缓存,为了提高查找性能而设计,所以它是动态生成的,这不同于inode,inode的数据并不会随进程的消亡而消失。注意不管是文件夹还是最终的文件,都是属于目录项,所有的目录项在一起构成一颗庞大的目录树。例如:open一个文件/home/xxx/yyy.txt,那么/、home、xxx、yyy.txt都是一个目录项,VFS在查找的时候,根据一层一层的目录项找到对应的每个目录项的inode,那么沿着目录项进行操作就可以找到最终的文件。

注意:目录也是一种文件(所以也存在对应的inode)。打开目录,实际上就是打开目录文件。

一个有效的dentry结构必定有一个inode结构,这是因为一个目录项要么代表着一个文件,要么代表着一个目录,而目录实际上也是文件。所以,只要dentry结构是有效的,则其指针d_inode必定指向一个inode结构。可是,反过来则不然,一个inode却可能对应着不止一个dentry结构;也就是说,一个文件可以有不止一个文件名或路径名。这是因为一个已经建立的文件可以被连接(link)到其他文件名。所以在inode结构中有一个队列i_dentry,凡是代表着同一个文件的所有目录项都通过其dentry结构中的d_alias域挂入相应inode结构中的i_dentry队列。

在内核中有一个哈希表dentry_hashtable ,是一个list_head的指针数组。一旦在内存中建立起一个目录节点的dentry 结构,该dentry结构就通过其d_hash域链入哈希表中的某个队列中。

内核中还有一个队列dentry_unused,凡是已经没有用户(count域为0)使用的dentry结构就通过其d_lru域挂入这个队列。

Dentry结构中除了d_alias 、d_hash、d_lru三个队列外,还有d_vfsmnt、d_child及d_subdir三个队列。其中d_vfsmnt仅在该dentry为一个安装点时才使用。另外,当该目录节点有父目录时,则其dentry结构就通过d_child挂入其父节点的d_subdirs队列中,同时又通过指针d_parent指向其父目录的dentry结构,而它自己各个子目录的dentry结构则挂在其d_subdirs域指向的队列中。

从上面的叙述可以看出,一个文件系统中所有目录项结构或组织为一个哈希表,或组织为一颗树,或按照某种需要组织为一个链表,这将为文件访问和文件路径搜索奠定下良好的基础。

struct dentry { unsigned int d_flags; seqcount_t d_seq; struct hlist_bl_node d_hash; struct dentry *d_parent; struct qstr d_name; struct inode *d_inode; unsigned char d_iname[DNAME_INLINE_LEN]; /* Ref lookup also touches following */ unsigned int d_count; 引用计数 spinlock_t d_lock; const struct dentry_operations *d_op; struct super_block *d_sb; unsigned long d_time; void *d_fsdata; struct list_head d_lru; /* * 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 */ }; d_count:引用计数 d_flags:目录项缓存标识,可取DCACHE_UNUSED、DCACHE_REFERENCED等 d_inode:与该目录项关联的inode d_parent:父目录的目录项 d_hash:内核使用dentry_hashtable对dentry进行管理,dentry_hashtable是由list_head组成的链表,一个dentry创建之后,就通过 d_hash链接进入对应的hash值的链表中。 d_lru:最近未使用的目录项的链表 d_child:目录项通过这个加入到父目录的d_subdirs中 d_subdirs:本目录的所有孩子目录链表头 d_alias:一个有效的dentry必然与一个inode关联,但是一个inode可以对应多个dentry,因为一个文件可以被链接到其他文件,所以,这个dentry就是通过这个字段链接到属于自己的inode结构中的i_dentry链表中的。(inode中讲过) d_mounted:安装在该目录的文件系统的数量!注意一个文件目录下可以有不同的文件系统! d_name:目录项名称 d_time:重新变为有效的时间!注意只要操作成功这个dentry就是有效的,否则无效。 d_op:目录项操作 d_sb:这个目录项所属的文件系统的超级块 d_vfs_flags:一些标志 d_fsdata:文件系统私有数据 d_iname:存放短的文件名

3 file简介(文件记录表)

文件对象表示进程已打开的文件,该对象file(不是物理文件)由相应的open()系统调用创建,有close()系统调用销毁,因为多个进程可以同时打开和操作一个文件,所以同一个文件也可能存在多个对应的文件对象。文件对象仅仅在进程观点上代表已打开文件,它反过来指向目录项对象(反过来指向索引节点),其实只有目录项对象才表示已打开的实际文件,虽然一个文件对应的文件对象不是是唯一的,但对应的索引节点和目录项对象无疑是唯一的,另外类似于目录项对象,文件对象实际上没有对应的磁盘数据。

一个文件对象是由一个文件结构体表示的,文件结构体代表一个打开的文件,系统中的每个打开的文件在内核空间都有一个关联的 struct file。它由内核在打开文件时创建,并传递给在文件上进行操作的任何函数。在文件的所有实例都关闭后,内核释放这个数据结构。

 struct file {  union { struct list_head fu_list; //文件对象链表指针linux/include/linux/list.h struct rcu_head fu_rcuhead; //RCU(Read-Copy Update)是Linux 2.6内核中新的锁机制 } f_u; struct path f_path; //包含dentry和mnt两个成员,用于确定文件路径 #define f_dentry f_path.dentry //f_path的成员之一,当前文件的dentry结构  #define f_vfsmnt f_path.mnt //表示当前文件所在文件系统的挂载根目录  const struct file_operations *f_op; //与该文件相关联的操作函数  atomic_t f_count; //文件的引用计数(有多少进程打开该文件)  unsigned int f_flags; //对应于open时指定的 flag mode_t f_mode; //读写模式:open的mod_t mode参数  off_t f_pos; //该文件在当前进程中的文件偏移量  struct fown_struct f_owner; //该结构的作用是通过信号进行I/O时间通知的数据。  unsigned int f_uid, f_gid; //文件所有者id,所有者组id  struct file_ra_state f_ra; //在linux/include/linux/fs.h中定义,文件预读相关  unsigned long f_version; //记录文件的版本号,每次使用后都自动递增。  #ifdef  CONFIG_SECURITY void *f_security; //用来描述安全措施或者是记录与安全有关的信息。  #endif /* needed for tty driver, and maybe others */  void *private_data; //可以用字段指向已分配的数据  #ifdef CONFIG_EPOLL /* Used by fs/eventpoll.c to link all the hooks to this file */  struct list_head f_ep_links; 文件的事件轮询等待者链表的头,  spinlock_t f_ep_lock;  f_ep_lock是保护f_ep_links链表的自旋锁。  #endif /* #ifdef CONFIG_EPOLL */  struct address_space *f_mapping; 文件地址空间的指针};

4 files_struct简介

struct files_struct { atomic_t count; /* 共享该表的进程数 */ rwlock_t file_lock; /* 保护该结构体的锁*/ int max_fds; /*当前文件对象的最大数*/ int max_fdset; /*当前文件描述符的最大数*/ int next_fd; /*已分配的文件描述符加1*/ struct file ** fd; /* 指向文件对象指针数组的指针 */ fd_set *close_on_exec; /*指向执行exec()时需要关闭的文件描述符*/ fd_set *open_fds; /*指向打开文件描述符的指针*/ fd_set close_on_exec_init; /* 执行exec()时关闭的初始文件*/ fd_set open_fds_init; /*文件描述符的初值集合*/ struct file * fd_array[32]; /* 文件对象指针的初始化数组*/};

fd域指向文件对象的指针数组。该数组的长度存放在max_fds域中。通常,fd域指向files_struct结构的fd_array域,该域包括32个文件对象指针。如果进程打开的文件数目多于32,内核就分配一个新的、更大的文件指针数组,并将其地址存放在fd域中;内核同时也更新max_fds域的值。

对于在fd数组中有入口地址的每个文件来说,数组的索引就是文件描述符(file descriptor)。通常,数组的第一个元素(索引为0)是进程的标准输入文件,数组的第二个元素(索引为1)是进程的标准输出文件,数组的第三个元素(索引为2)是进程的标准错误文件。请注意,借助于dup()、dup2()和

fcntl ) 系统调用,两个文件描述符就可以指向同一个打开的文件,也就是说,数组的两个元素可能指向同一个文件对象。当用户使用shell结构(如2>&;1)将标准错误文件重定向到标准输出文件上时,用户总能看到这一点。

open_fds域包含open_fds_init域的地址,open_fds_init域表示当前已打开文件的文件描述符的位图。max_fdset域存放位图中的位数。由于数据结构fd_set有1024位,通常不需要扩大位图的大小。不过,如果确实必须的话,内核仍能动态增加位图的大小,这非常类似文件对象的数组的情形。

当开始使用一个文件对象时调用内核提供的fget()函数。这个函数接收文件描述符fd作为参数,返回在current->files->fd[fd]中的地址,即对应文件对象的地址,如果没有任何文件与fd对应,则返回NULL。在第一种情况下,fget()使文件对象引用计数器f_count的值增1。

用户打开文件表files_structs是由进程描述符task_struct中的file域指向,所有与进程相关的信息如打开的文件及文件描述符都包含其中,又从前面可以看出,files_struct通过**file保持对文件对象file的访问,于此相似文件对象file的结构体内成员中包含目录项dentry,目录项dentry将文件名与inode相连,最终通过inode中的指针可以访问存储实际的数据的地方Data Area.

so他们管理层次关系关系如下:进程->task_struct->files_struct->file->dentry->inode->Data Area

(0)

相关推荐

  • 操作系统之文件管理,万字长文让你彻底弄懂

    磁盘的结构 磁盘原理 磁盘,磁道,扇区 读取数据 柱面号,盘面号,扇区号定位一个块 磁盘分类 磁盘小结 磁盘初始化 磁盘初始化 引导块 磁盘调度算法 一次磁盘读/写操作需要的时间 内存管理请求分页五种 ...

  • 查看linux文件系统

    表白:黑白圣堂血天使,天剑鬼刀阿修罗.  讲解对象:/查看linux文件系统 作者:融水公子 rsgz 阿里云教程 阿里云教程 http://www.rsgz.top/post/179.html 可以 ...

  • Linux文件系统详解(文件系统层次、分类、存储结构、inode)

    参考:http://blog.chinaunix.net/uid-8698570-id-1763151.html           http://www.iteye.com/topic/816268 ...

  • Linux 文件系统基本介绍

    想想有多个搁板的冰箱,可用于存放各种物品. 这些货架帮助您按形状.大小.类型等组织杂货项目.相同的概念适用于文件系统,它是一种以人类可用的形式存储和组织任意数据集合的方法的体现. Linux 支持的不 ...

  • 磁盘、分区及Linux文件系统 [Disk, Partition, Linux File Syste...

    磁盘、分区及Linux文件系统 [Disk, Partition, Linux File Syste...

  • 利用fsck命令修复linux文件系统

    一台rhel linux6.5的虚机重启后无法远程,进入控制台查看,系统界面如下: 分析:可以看到提示,inode有部分崩溃,产生了"孤儿文件".建议手动使用fsck检查.这时系统 ...

  • Linux 文件系统 -- 简述几种文件类型

    Linux 中一切皆为文件,文件类型也有多种,使用 ls -l 命令可以查看文件的属性,所显示结果的第一列的第一个字符用来表明该文件的文件类型,如下: 1.普通文件 使用 ls -l 命令后,第一列第 ...

  • ip命令网络对象常见操作命令有哪些?linux学习

    ip命令是iproute软件包中的一个强大的网络配置工具,用于显示或管理Linux系统的路由.网络设备.策略路由和隧道.作为ip命令参数选项之一的操作对象,即对指定的网络对象完成的具体操作.通常,每一 ...

  • 挂载文件系统mount命令参数选项有哪些?Linux命令

    mount命令可以将指定的文件系统挂载到指定目录(挂载点),在Linux系统下必须先挂载所有的设备,然后才能被访问,挂载其实就是为要访问的设置开个门(开门才能访问).挂载文件系统对于linux运维工程 ...

  • Linux系统移植的四大步骤

    最近在学习系统移植的相关知识,在学习和调试过程中,发现了很多问题,也解决了很多问题,但总是对于我们的开发结果有一种莫名其妙的感觉,纠其原因,主要对于我们的开发环境没有一个深刻的认识,有时候几个简单的命 ...