1406 struct file_system_type { 1407 const char *name; 1408 int fs_flags; 1409 int (*get_sb) (struct file_system_type *, int, 1410 const char *, void *, struct vfsmount *); 1411 void (*kill_sb) (struct super_block *); 1412 struct module *owner; 1413 struct file_system_type * next; 1414 struct list_head fs_supers; 1415 1416 struct lock_class_key s_lock_key; 1417 struct lock_class_key s_umount_key; 1418 1419 struct lock_class_key i_lock_key; 1420 struct lock_class_key i_mutex_key; 1421 struct lock_class_key i_mutex_dir_key; 1422 struct lock_class_key i_alloc_sem_key; 1423 };
系统有一个file_system_type类型的全局变量file_systems,用来保存所有已经注册到系统中的文件系统。在mount文件系统时,会判断系统是否支持挂载的文件系统。
@name:文件系统的名字,这个名字唯一的标识一种文件系统
@next:通过这个结构把所有已经注册的文件系统连接到file_systems
@fs_supers:对于每一个mount的文件系统,系统都会为它创建一个super_block数据结构,该结构保存文件系统本省以及挂载点相关的信息。由于可以同时挂载多个同一文件系统类型的文件系统(比如/ 和/home都挂载了ext3文件系统),因此同一个文件系统类型会对应多个super block,@fs_supers就把这个文件系统类型对应的super block链接起来。
@owner是指向module的指针,仅当文件系统类型是以模块方式注册时,owner才有效。
@get_sb:这个函数非常重要,它VFS能够和底层文件系统交互的起始点,该函数是不能放在super_block结构中的,因为super_block是在get_sb执行之后才能建立的。get_sb从底层文件系统获取super_block的信息,是和底层文件系统相关的。
在mount文件系统时,除了在内存创建vfs_mount数据结构外,还会读取并创建这个文件系统的super block
979 struct super_block { 980 struct list_head s_list; /* Keep this first */ 981 dev_t s_dev; /* search index; _not_ kdev_t */ 982 unsigned long s_blocksize; 983 unsigned char s_blocksize_bits; 984 unsigned char s_dirt; 985 unsigned long long s_maxbytes; /* Max file size */ 986 struct file_system_type *s_type; 987 const struct super_operations *s_op; 988 struct dquot_operations *dq_op; 989 struct quotactl_ops *s_qcop; 990 const struct export_operations *s_export_op; 991 unsigned long s_flags; 992 unsigned long s_magic; 993 struct dentry *s_root; 994 struct rw_semaphore s_umount; 995 struct mutex s_lock; 996 int s_count; 997 int s_syncing; 998 int s_need_sync_fs; 999 atomic_t s_active; 1000 #ifdef CONFIG_SECURITY 1001 void *s_security; 1002 #endif 1003 struct xattr_handler **s_xattr; 1004 1005 struct list_head s_inodes; /* all inodes */ 1006 struct list_head s_dirty; /* dirty inodes */ 1007 struct list_head s_io; /* parked for writeback */ 1008 struct list_head s_more_io; /* parked for more writeback */ 1009 struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */ 1010 struct list_head s_files; 1011 1012 struct block_device *s_bdev; 1013 struct mtd_info *s_mtd; 1014 struct list_head s_instances; 1015 struct quota_info s_dquot; /* Diskquota specific options */ 1016 1017 int s_frozen; 1018 wait_queue_head_t s_wait_unfrozen; 1019 1020 char s_id[32]; /* Informational name */ 1021 1022 void *s_fs_info; /* Filesystem private info */ 1023 1024 /* 1025 * The next field is for VFS *only*. No filesystems have any business 1026 * even looking at it. You had been warned. 1027 */ 1028 struct mutex s_vfs_rename_mutex; /* Kludge */ 1029 1030 /* Granularity of c/m/atime in ns. 1031 Cannot be worse than a second */ 1032 u32 s_time_gran; 1033 1034 /* 1035 * Filesystem subtype. If non-empty the filesystem type field 1036 * in /proc/mounts will be "type.subtype" 1037 */ 1038 char *s_subtype; 1039 };
@s_list 所有的super_block都通过s_list链接在一起
@s_dev @s_bdev标识文件系统所在的块设备,前者使用设备编号,而@s_bdev指向内存中的block_device结构
@s_blocksize @s_blocksize_bits:标识文件系统的块尺寸。这两个信息使用不同的方式表示同一样东西。前者是字节数,后者是字节数取2的对数。s_blocksize可以很容易的计算出s_blocksize_bits,所以实在没看出二者同时存在的意义,唯一能想到的是 super_block数目本来就少,每个结构增加一个4 bytes的成员变量,不会带来什么影响。
@s_dirt 表示需要把super_block的更改写回磁盘super_block
@s_maxbytes 文件系统支持的最大文件尺寸上限,这个是文件系统特定的,不同文件系统有不同的上限
@s_type 文件系统类型,指向file_systems链表中的一个节点。
@s_op 和super_block相关的操作,我们会在super_operations中介绍
@s_flags 文件系统的mount标记,这些标记在mount文件系统时,根据mount标记映射过来的。
@s_magic 每一种文件系统类型都有自己的magic。
@s_root 全局根目录的dentry项,
@s_count super block的引用计数,为0时销毁super block
@s_syncing 表示系统正在把脏inode同步回磁盘:
@s_inodes:该文件系统实例内的所有inode,都通过它们的i_sb_list成员挂接到s_inodes起始的双向链表中
@s_dirty:该文件系统实例内的所有脏inode,都通过它们的i_list成员挂接到s_dirty为表头的双向链表中。在同步内存数据到底层存储介质时,使用该链表更加高效。该链表包含已经修改的inode,因此不需要扫描全部的inode。
@s_io:该链表保存了文件系统实例中,同步代码当前考虑回写的所有inode
@s_more_io:包含那些已经被选中进行同步的inode,他们也在s_io中,但是不能一次处理完毕。
@s_files:该链表是本文件系统实例中,所有已经打开的file对象,通过file->f_list挂接到这个链表上。内核在卸载文件系统时将参考这个链表,如果发现这个链表不为空,那么表示文件系统仍然在使用中,卸载失败。
@s_instances:这个文件系统实例通过s_instances成员,链接到file_system_type中的fs_supers链表上。也就是说,对于一种文件类型file_system_type,它的成员fs_supers是一个链表,所有实例都挂接在这个链表上。
@s_fs_info:是一个指向文件系统实现的私有数据指针,VFS不操作这个数据,而是由底层文件系统操作。
inode包含了文件系统中一个文件的所有信息,inode和文件系统中的文件是一一对应的。借助于inode的这些信息,文件系统可以方便的操作文件
struct inode { struct hlist_node i_hash; struct list_head i_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 long i_version; loff_t i_size; struct timespec i_atime; struct timespec i_mtime; struct timespec i_ctime; unsigned int i_blkbits; 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; struct list_head i_devices; union { struct pipe_inode_info *i_pipe; struct block_device *i_bdev; struct cdev *i_cdev; }; int i_cindex; __u32 i_generation; unsigned long i_state; unsigned long dirtied_when; /* jiffies of first dirtying */ unsigned int i_flags; atomic_t i_writecount; void *i_private; /* fs or device private pointer */ };
首先是四个list_head,每个inode通过这些list_head都会同时挂接在4个链表上
@i_hash:系统中所有的inode都存放在hash_table,以方便对某个inode的快速查找,hash算法是通过超级块和inode number计算的,这两项组合可以唯一确定一个inode。i_hash用作hash表的冲突管理。
@i_list 这个成员可以把inode挂接到几个链表之一(因为这几种状态是互斥的,所以共用了这个成员),具体在哪个链表上,要根据当前inode的状态来判断。
有四个可能的状态:
1. inode在内存中,没有关联到任何文件,因此处于非激活状态。
2. inode在内存中,正在由一个或者多个进程使用,文件数据,元数据和磁盘上的数据,元数据是同步的,也就是说自从上次同步以后,再没更改过文件的数据和元数据。
3. inode在内存中,由一个以上进程使用,但是文件数据,元数据是脏的,需要同步到磁盘上。
@i_sb_list:文件必定属于某个文件系统实例,我们可以把super_block看做这个文件系统实例的代表,该文件系统实例的所有inode都通过i_sb_list挂接到super_block的s_inodes链表上。
@i_dentry:一个inode可以对应多个目录项,这些dentry项通过d_alias链接到i_dentry上。目前我只知道,硬链接会导致一个inode对应多个目录项。
@i_ino:inode 序号,在文件系统实例内是唯一存在的,inode number对底层文件系统很重要,几乎贯穿整个文件系统的管理
@i_count:inode的使用计数,inode对应的文件可以被多个进程同时访问。
@i_nlink:文件的引用计数,表示文件的硬链接数目。对于目录来说,是子目录数目,对于普通文件来说,则是为某一个文件创建的硬链接数目
@i_rdev:表示文件所在磁盘的设备号,通过这个设备号,最终可以获得目标设备block_device
@i_size:文件长度,单位为bytes
@i_blkbits:block size in number of bits,在2.6的早期版本inode结构还有一个成员叫i_blksize,占用了4bytes。二者信息是冗余的,当系统内的inode 数目很大时,该冗余信息可能会占用很打内存,而且根据i_blkbits非常容易就计算出block size,因此在后面版本被去掉了。
@i_blocks:文件的总块数。注意,i_blocks无法从i_size和i_blkbits推导出来,因为i_size表示的文件可能是一个稀疏文件。细节可查看源码中对i_blocks的使用。
@i_bytes:文件在最后一块中的字节数,同样由于文件可能存在洞,我们无法从i_size和i_blkbits推导出i_bytes。
@i_atime i_ctime i_mtime:分别为文件的最后访问时间,文件的创建时间,文件的最后修改时间。
@i_op @i_fop 前者是特定于inode相关的操作,后者则主要是文件内部数据的操作
@i_sb:该文件所属的super block
@i_data @i_mapping:地址空间对象,一个inode必然和一个地址空间对象关联,地址空间对象用来管理和这个文件相关的页面映射,address_space是内核核心抽象概念之一
@i_devices:和设备文件管理相关,一个设备可能对应多个设备文件,使用这个成员,可以把这些设备文件的inode链接起来。
@i_write_count:被write进程使用的使用计数
磁盘文件系统的目录结构保存在磁盘目录项中,而块设备读取速度慢,需要很长时间才能找到与一个文件名对应的inode,linux引入了目录项缓存来利用之前查找的结果
struct dentry { atomic_t d_count; unsigned int d_flags; /* protected by d_lock */ spinlock_t d_lock; /* per dentry lock */ struct inode *d_inode; /* Where the name belongs to - NULL is * negative */ /* * The next three fields are touched by __d_lookup. Place them here * so they all fit in a cache line. */ struct hlist_node d_hash; /* lookup hash list */ struct dentry *d_parent; /* parent directory */ struct qstr d_name; struct list_head d_lru; /* LRU list */ /* * 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 */ unsigned long d_time; /* used by d_revalidate */ struct dentry_operations *d_op; struct super_block *d_sb; /* The root of the dentry tree */ void *d_fsdata; /* fs-specific data */ int d_mounted; unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */ };
@d_count:dentry的引用计数,当创建dentry的子dentry时,会增加父dentry的引用计数;当把inode关联到dentry时,也会增加dentry的引用计数
@d_inode:dentry对应的inode,这个是dentry最中要的成员,因为dentry的主要做用就是通过路径名查找inode
@d_hash:内存中所有的dentry都保存在hash表中d_hash是为了处理hash冲突的
@d_parent:指向这个dentry的父dentry,对于根目录d_parent指向自身
@d_name:指定了文件的名称,qstr是一个包装器,存储了字符串的长度,hash值和字符串本身。字符串不是一个绝对路径,而是当前的分量。如果文件的名称小于DNAME_INLINE_LEN_MIN,那么d_name->name指向d_iname,否则要通过kmalloc进行分配。
@d_lru:表头是dentry_unused,所有引用计数为0的dentry都会放到这个LRU链表中,并且插在链表前面,因此靠后的节点,表示越老。
@d_child:用于将当前dentry链接到父dentry的d_subdirs
@d_subdirs:所有的子dentry都通过他们的d_child链接到父亲的d_subdirs
@d_sb:指向该目录项所在文件系统实例的超级块
虽然file看起来也是表示一个文件,但是要记住,file是进程相关的;而inode是进程无关的。因此一个文件在系统内只有一个inode;而一个文件在系统内可能有多个file结构,分别属于不同的进程。
struct file { struct path f_path; #define f_dentry f_path.dentry #define f_vfsmnt f_path.mnt const struct file_operations *f_op; atomic_t f_count; unsigned int f_flags; mode_t f_mode; loff_t f_pos; struct fown_struct f_owner; unsigned int f_uid, f_gid; struct file_ra_state f_ra; u64 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; #endif /* #ifdef CONFIG_EPOLL */ struct address_space *f_mapping; };
f_count:引用计数,使用file对象的进程数目。比如使用CLONE_FILES创建进程时,这些进程会共享打开的文件,因此会使用相同的file对象。
f_mode:打开文件时,传递的模式参数,保存在f_mode中
f_flags:打开文件时,传递的打开标志,保存在f_flags中
f_pos:是一个很重的值,这个值表示文件的读写位置,这个值是不可以放在inode中的,因为一个inode可能对应多个打开的file结构。
f_dentry:提供了打开文件file和这个文件inode之间的关联
f_vfsmnt:所在文件系统的信息
每一个被装载的系统都对应着一个vfsmount的实例
struct vfsmount { struct list_head mnt_hash; struct vfsmount *mnt_parent; /* fs we are mounted on */ struct dentry *mnt_mountpoint; /* dentry of mountpoint */ struct dentry *mnt_root; /* root of the mounted tree */ struct super_block *mnt_sb; /* pointer to superblock */ struct list_head mnt_mounts; /* list of children, anchored here */ struct list_head mnt_child; /* and going through their mnt_child */ int mnt_flags; /* 4 bytes hole on 64bits arches */ char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ struct list_head mnt_list; struct list_head mnt_expire; /* link in fs-specific expiry list */ struct list_head mnt_share; /* circular list of shared mounts */ struct list_head mnt_slave_list;/* list of slave mounts */ struct list_head mnt_slave; /* slave list entry */ struct vfsmount *mnt_master; /* slave is on master->mnt_slave_list */ struct mnt_namespace *mnt_ns; /* containing namespace */ /* * We put mnt_count & mnt_expiry_mark at the end of struct vfsmount * to let these frequently modified fields in a separate cache line * (so that reads of mnt_flags wont ping-pong on SMP machines) */ atomic_t mnt_count; int mnt_expiry_mark; /* true if marked for expiry */ int mnt_pinned; };
@mnt_mountpoint 是当前文件系统的装载点在其父目录中的dentry结构。
@mnt_root 当前文件系统根目录的dentry
@mnt_parent 指向父文件系统的vfsmount
@mnt_sb 指向与这个vfsmount相关的超级块,对于每一个装载的文件系统,都有且只有一个super block实例
@mnt_mounts 表头节点,是子文件系统链表的表头
@mnt_child vfsmount通过mnt_child挂接到父vfsmount的mnt_mounts链表上。
@mount_count 是一个计数值,每当我们使用这个vfsmount之前,都要通过mntget增加它的引用计数,如果不再使用则调用mntput减少引用计数。