2017-03-13

上文针对VFS的基本信息做了介绍,并简单介绍了VFS涉及的几个数据机构,本节结合LInux源码,对各个结构之间的关系进行分析。


一、总体架构图

总体架构图如上图所示,结合进程访问文件的实际情况,根据上图进行细节化的描述。进程通过其结构中的files_struct结构和文件建立联系,看戏files_struct结构

  1. struct files_struct {
  2. /*
  3. * read mostly part
  4. */
  5. atomic_t count;
  6. struct fdtable __rcu *fdt;
  7. struct fdtable fdtab;
  8. /*
  9. * written part on a separate cache line in SMP
  10. */
  11. spinlock_t file_lock ____cacheline_aligned_in_smp;
  12. int next_fd;
  13. unsigned long close_on_exec_init[];
  14. unsigned long open_fds_init[];
  15. struct file __rcu * fd_array[NR_OPEN_DEFAULT];
  16. };

首先是一个原子变量,记录打开文件的个数,注意这里不只是普通文件,还包括设备文件等其他文件。next_fd记录当前下一个可用的文件描述符,用于在下次进程打开文件时快速分配。而close_on_exec_init和open_fds_init是位图。fd_array是初始化状态的文件描述符数组,而fdtab是真正管理文件描述符的结构。看下fdtable结构

  1. struct fdtable {
  2. unsigned int max_fds;
  3. struct file __rcu **fd; /* current fd array */
  4. unsigned long *close_on_exec;
  5. unsigned long *open_fds;
  6. struct rcu_head rcu;
  7. };

max_fds表示最大的打开文件数,可以更改。fd是一个指向文件描述符数组的指针,初始化为files_struct结构中fd_array数组的地址,close_on_exec指向files_struct结构中的位域。open_fds是一个指向位域的指针,管理着当前打开的所有描述符,如果位域中的对应位被置位表示该描述符在使用中。

关于描述符表扩展的情况,最后进行解释。上面的描述符表中,都是指向file结构的指针。进程每打开一个文件,就会有一个file结构与之对应。换句话说,file结构记录的某次进程对文件的某一次操作信息。看下file结构

  1. struct file {
  2. /*
  3. * fu_list becomes invalid after file_free is called and queued via
  4. * fu_rcuhead for RCU freeing
  5. */
  6. union {
  7. struct list_head fu_list;
  8. struct rcu_head fu_rcuhead;
  9. } f_u;
  10. struct path f_path;
  11. #define f_dentry f_path.dentry
  12. struct inode *f_inode; /* cached value */
  13. const struct file_operations *f_op;
  14.  
  15. /*
  16. * Protects f_ep_links, f_flags, f_pos vs i_size in lseek SEEK_CUR.
  17. * Must not be taken from IRQ context.
  18. */
  19. spinlock_t f_lock;
  20. #ifdef CONFIG_SMP
  21. int f_sb_list_cpu;
  22. #endif
  23. atomic_long_t f_count;
  24. unsigned int f_flags;
  25. fmode_t f_mode;
  26. loff_t f_pos;
  27. struct fown_struct f_owner;
  28. const struct cred *f_cred;
  29. struct file_ra_state f_ra;
  30.  
  31. u64 f_version;
  32. #ifdef CONFIG_SECURITY
  33. void *f_security;
  34. #endif
  35. /* needed for tty driver, and maybe others */
  36. void *private_data;
  37.  
  38. #ifdef CONFIG_EPOLL
  39. /* Used by fs/eventpoll.c to link all the hooks to this file */
  40. struct list_head f_ep_links;
  41. struct list_head f_tfile_llink;
  42. #endif /* #ifdef CONFIG_EPOLL */
  43. struct address_space *f_mapping;
  44. #ifdef CONFIG_DEBUG_WRITECOUNT
  45. unsigned long f_mnt_write_state;
  46. #endif
  47. };

同一个超级块下打开的所有文件都会通过双链表连接起来。另外,file结构中主要由对应文件的inode缓存,在下次访问不需要通过dentry查找inode了。还有一个path结构,该结构记录当前文件的emulation项和vfsmount结构。其余记录文件的权限模式、读写位置等信息,还有一个重要的函数表,保存操作文件的一些函数的指针。关键是一个进程在这里可以根据dentry查找到inode。究竟是如何查找的呢?看下dentry的结构:

  1. struct dentry {
  2. /* RCU lookup touched fields */
  3. unsigned int d_flags; /* protected by d_lock */
  4. seqcount_t d_seq; /* per dentry seqlock */
  5. struct hlist_bl_node d_hash; /* lookup hash list */
  6. struct dentry *d_parent; /* parent directory */
  7. struct qstr d_name;
  8. struct inode *d_inode; /* Where the name belongs to - NULL is
  9. * negative */
  10. unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */
  11.  
  12. /* Ref lookup also touches following */
  13. unsigned int d_count; /* protected by d_lock */
  14. spinlock_t d_lock; /* per dentry lock */
  15. const struct dentry_operations *d_op;
  16. struct super_block *d_sb; /* The root of the dentry tree */
  17. unsigned long d_time; /* used by d_revalidate */
  18. void *d_fsdata; /* fs-specific data */
  19.  
  20. struct list_head d_lru; /* LRU list */
  21. /*
  22. * d_child and d_rcu can share memory
  23. */
  24. union {
  25. struct list_head d_child; /* child of parent list */
  26. struct rcu_head d_rcu;
  27. } d_u;
  28. struct list_head d_subdirs; /* our children */
  29. struct hlist_node d_alias; /* inode alias list */
  30. };

dentry结构中有指向当前操作文件的inode指针,父目录的dentry,,当然还包括文件名信息。注意这里文件名并没有作为属性保存在inode节点中,而是保存在dentry结构中。因为文件名对于系统来讲主要来查找inode,而通过dentry可以查找到inode,所以这里其实dentry之后就不需要文件名了。通过dentry还可以定位所属 的超级块。该结构中也有个函数表dentry_operations,主要是针对dentry的操作,如增加、删除dentry。一个目录下的所有子目录会形成一个链表,d_subdirs是链表头。而d_child作为一个节点,连接到父目录的子链表中。上节已经提到,系统中所有的dentry通过一个hash表维护起来,以便于查找。表头是全局变量dentry_hashtable.而对于未使用的dentry,内核使用dentry_unused全局链表来组织。因为每个父目录均会有一条自己子目录的链表,所以系统中还存在一个dentry树。

到目前为止已经找到了具体的inode,inode记录文件的真实属性信息,修改时间,是否是脏,以及在内存中的映射信息。看下inode结构

  1. struct inode {
  2. umode_t i_mode;
  3. unsigned short i_opflags;
  4. kuid_t i_uid;
  5. kgid_t i_gid;
  6. unsigned int i_flags;
  7.  
  8. #ifdef CONFIG_FS_POSIX_ACL
  9. struct posix_acl *i_acl;
  10. struct posix_acl *i_default_acl;
  11. #endif
  12.  
  13. const struct inode_operations *i_op;
  14. struct super_block *i_sb;
  15. struct address_space *i_mapping;
  16.  
  17. #ifdef CONFIG_SECURITY
  18. void *i_security;
  19. #endif
  20.  
  21. /* Stat data, not accessed from path walking */
  22. unsigned long i_ino;
  23. /*
  24. * Filesystems may only read i_nlink directly. They shall use the
  25. * following functions for modification:
  26. *
  27. * (set|clear|inc|drop)_nlink
  28. * inode_(inc|dec)_link_count
  29. */
  30. union {
  31. const unsigned int i_nlink;
  32. unsigned int __i_nlink;
  33. };
  34. dev_t i_rdev;
  35. loff_t i_size;
  36. struct timespec i_atime;
  37. struct timespec i_mtime;
  38. struct timespec i_ctime;
  39. spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
  40. unsigned short i_bytes;
  41. unsigned int i_blkbits;
  42. blkcnt_t i_blocks;
  43.  
  44. #ifdef __NEED_I_SIZE_ORDERED
  45. seqcount_t i_size_seqcount;
  46. #endif
  47.  
  48. /* Misc */
  49. unsigned long i_state;
  50. struct mutex i_mutex;
  51.  
  52. unsigned long dirtied_when; /* jiffies of first dirtying */
  53.  
  54. struct hlist_node i_hash;
  55. struct list_head i_wb_list; /* backing dev IO list */
  56. struct list_head i_lru; /* inode LRU list */
  57. struct list_head i_sb_list;
  58. union {
  59. struct hlist_head i_dentry;
  60. struct rcu_head i_rcu;
  61. };
  62. u64 i_version;
  63. atomic_t i_count;
  64. atomic_t i_dio_count;
  65. atomic_t i_writecount;
  66. const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
  67. struct file_lock *i_flock;
  68. struct address_space i_data;
  69. #ifdef CONFIG_QUOTA
  70. struct dquot *i_dquot[MAXQUOTAS];
  71. #endif
  72. struct list_head i_devices;
  73. union {
  74. struct pipe_inode_info *i_pipe;
  75. struct block_device *i_bdev;
  76. struct cdev *i_cdev;
  77. };
  78.  
  79. __u32 i_generation;
  80.  
  81. #ifdef CONFIG_FSNOTIFY
  82. __u32 i_fsnotify_mask; /* all events this inode cares about */
  83. struct hlist_head i_fsnotify_marks;
  84. #endif
  85.  
  86. #ifdef CONFIG_IMA
  87. atomic_t i_readcount; /* struct files open RO */
  88. #endif
  89. void *i_private; /* fs or device private pointer */
  90. };

inode是一个比较庞大的结构,开头记录了文件的权限信息,如用户、用户组等。该结构中有个函数表inode_operations,记录针对inode的一些操作。inode中还有指向当前文件所属文件系统的超级块结构。当然一个至关重要的就是address_space 类型的i_mapping指针了。其指向一个address_space 结构,记录当前文件在内存中的映射情况。这点等会在分析。除此之外,记录文件的一些时间信息。前文说过,inode在内存中有三种类型:位于内存中但未使用的;位于内存中正在使用的;位于内存中已经发生变化即需要写会到磁盘的,前两种都是全局链表,第三种特定于超级块结构。除此之外,inode还在一个hash表中出现,表头是inode_hashtable,支持根据inode编号和超级块快速访问inode。

到此进程已经找到了具体的inode节点,后来又是如何把文件映射到内存中呢?一个核心结构就是address_space,先看下该结构

  1. struct address_space {
  2. struct inode *host; /* owner: inode, block_device */
  3. struct radix_tree_root page_tree; /* radix tree of all pages */
  4. spinlock_t tree_lock; /* and lock protecting it */
  5. unsigned int i_mmap_writable;/* count VM_SHARED mappings */
  6. struct rb_root i_mmap; /* tree of private and shared mappings */
  7. struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */
  8. struct mutex i_mmap_mutex; /* protect tree, count, list */
  9. /* Protected by tree_lock together with the radix tree */
  10. unsigned long nrpages; /* number of total pages */
  11. pgoff_t writeback_index;/* writeback starts here */
  12. const struct address_space_operations *a_ops; /* methods */
  13. unsigned long flags; /* error bits/gfp mask */
  14. struct backing_dev_info *backing_dev_info; /* device readahead, etc */
  15. spinlock_t private_lock; /* for use by the address_space */
  16. struct list_head private_list; /* ditto */
  17. void *private_data; /* ditto */
  18. } __attribute__((aligned(sizeof(long))));

该结构特定于inode节点存在,多个进程可以共享同一个文件,所以在特定于进程的file结构中,有一个指向该inode address_space的指针f_mapping。具体的访问位置记录在file结构中。address_space仅仅负责对文件的映射,该结构管理了对应文件映射的所有内存区域vm_area_struct实例。上面的i_map作为一个红黑树根,关联所有的vm_area_struct,而i_mmap_nonliner是一个双向链表,关联所有非线性映射的vm_area_struct实例。该结构中还记录了所属inode节点的指针host,区域包含的虚拟页面的数量nrpages,当然还有一组操作函数,用于和设备交互,如读取一个页或者写入一个页,设置页面为脏等。关于进程虚拟内存的管理,参考另一篇文章:

Linux下的文件系统2的更多相关文章

  1. 全面了解Linux下Proc文件系统

    全面了解Linux下Proc文件系统   Proc是一个虚拟文件系统,在Linux系统中它被挂载于/proc目录之上.Proc有多个功能 ,这其中包括用户可以通过它访问内核信息或用于排错,这其中一个非 ...

  2. Linux 下EXT2文件系统 —— 如何将蚂蚁和大象优雅的装进冰箱里

    这一阵子真是偷懒,无时无刻不和自己身体中的懒癌做斗争.最终我还是被打败了,星期天两天几乎都是荒废过去的,在空闲的时候实际上我内心也是有点焦虑的,不知道去怎么度过这时间.学习吧又不想学习,看电视娱乐吧也 ...

  3. linux下的文件系统

    转http://www.cnblogs.com/yyyyy5101/articles/1901842.html 谈谈个人对于文件系统的认识,其实这也体现了计算机操作系统的抽象:你不用管计算机中的文件如 ...

  4. Linux下删除文件系统空间不释放的问题

    删除了Linux下的一个文件,但是系统空间并没有被释放. 如下:/home/hadmin/data/hadoop 使用了1.3T的空间,但是实际只使用了600多G 原因是我删除了一个600多G的文件, ...

  5. <解说linux下proc文件系统>

    proc文件系统的作用是访问系统内核信息 proc不是一个真实的文件系统,它不占系统的外存空间,只是以文件的形式为用户访问linux内核数据提供接口,因为系统内核总是动态的变化,所以我们所捕捉到的也只 ...

  6. linux下查看文件系统类型

    1. df -hT命令   -h, --human-readable  print sizes in human readable format (e.g., 1K 234M 2G) -T, --pr ...

  7. Linux下查看文件系统磁盘使用

    [root@localhost ~]# df -h 可以查看所有文件系统的磁盘使用情况 du --max-depth=1 -h 可以查看当前目录下各子目录的磁盘使用情况 参考:http://www.2 ...

  8. Linux下网络文件系统NFS服务搭建易错点总结

    一.环境准备: 1 [root@czh ~]# cat /etc/redhat-release 2 CentOS release 6.7 (Final) 3 [root@czh ~]# uname - ...

  9. Linux下识别分区文件系统类型

    Linux下挂载文件系统有时候需要填写文件系统.但有的设备拿到手还不知道文件系统,这种情况,可以用 parted命令 # parted /dev/vda GNU Parted 3.2 Using /d ...

随机推荐

  1. Tomcat热部署的三种方式

    原文地址:https://blog.csdn.net/nlwangxin/article/details/49734659热部署是指在你修改项目BUG的时候对JSP或JAVA类进行了修改在不重启WEB ...

  2. Linux Performance tool

    https://www.tecmint.com/command-line-tools-to-monitor-linux-performance/ https://www.tecmint.com/lin ...

  3. ProtoBuf3 C++使用篇

    protobuf 是用于结构化数据串行化的灵活.高效.自动化的解决方案.又如 XML,不过它更小.更快.也更简单.你只需要按照你想要的数据存储格式编写一个.proto,然后使用生成器生成的代码来读写这 ...

  4. AICODER全栈实习报名

    三期班开始报名 三期班定于6月17日开班,实习费用如下: 三个月模式实习费为12000元. 一个月模式实习费为4500元. AICODER提供后付费模式,报名参加AICODER的线下实习3个月模式的朋 ...

  5. Delphi目录监控、目录监听

    资料地址: 1.https://www.cnblogs.com/studypanp/p/4890970.html 单元代码: (************************************ ...

  6. 现代php编程

    自动加载__autolaod和spl_autoload_register() 自动加载就是指如果找不到某个类如何处理的方式,具体可参见此文,可以说spl_autoload_register是更加高级, ...

  7. 如何知道局域网内哪些ip被占用----工具法Free IP Scanner

    在局域网中,尤其是在工作室和公司中需要修改IP地址才能上网,通常我们在设置完ip地址后会提示[该ip地址已被占用],又得回头去修改ip地址.本篇经验就介绍一款很好用的免费软件——Free IP Sca ...

  8. 当 return 遇到 try

    . . . . . 今天有同事和我探讨在群里看到的一道有趣的题目,在探讨的过程中让我搞清楚了一些曾经模糊的概念,特此记录下来. 题目给出如下代码,问运行后打印的结果是什么. public static ...

  9. GeForce GTX 1080 ti安装记录

    安装GeForce GTX 1080ti 安装GeForce GTX 1080ti,8+8pin需要全接,接4pin就开机显示器上会提示电源线没接完,将显示器线接在显卡上. 设置Win 10 pro ...

  10. mysql安装过程mysql本地环境变量的配置

    配置环境变量 前面步骤完成后安装好MySQL,为MySQL配置环境变量.MySQL默认安装在C:\Program Files下. 1)新建MYSQL_HOME变量,并配置:C:\Program Fil ...