Linux 内核文件系统关键数据结构
1、超级块 super block
超级块(super block)代表了整个文件系统本身,代表一个具体的已安装文件系统,通常。超级块是对应文件系统自身的控制块结构,超级块保存了文件系统设定的文件块大小,超级块的操作函数,文件系统内所有的inode也都要链接到超级块的链表头,当然还包括其余的信息,通过超级块对象,我们可以找到这些必要的信息。
超级块是文件系统的心脏,是与文件系统相关的,在超级块中保存了全局文件信息,如硬盘已用空间、数据块可用空间、inode节点信息等,一个文件系统中有哪些资源都记录在其中。
- struct super_block {
- struct list_head s_list; /* Keep this first */
- dev_t s_dev; /* search index; _not_ kdev_t */
- unsigned long s_blocksize;//指定了文件系统的块大小
- unsigned char s_blocksize_bits;
- unsigned char s_dirt;
- unsigned long long s_maxbytes; /* Max file size *///最大文件的尺寸
- struct file_system_type *s_type;//指向邋file_system_type结构的指针
- struct super_operations *s_op;//超级块操作函数集
- struct dquot_operations *dq_op;
- struct quotactl_ops *s_qcop;
- struct export_operations *s_export_op;
- unsigned long s_flags;
- unsigned long s_magic;//魔术数字
- struct dentry *s_root;//文件系统根目录项
- struct rw_semaphore s_umount;
- struct mutex s_lock;//互斥锁 SMP safe
- int s_count;
- int s_syncing;
- int s_need_sync_fs;
- atomic_t s_active;
- void *s_security;
- struct xattr_handler **s_xattr;
- struct list_head s_inodes; /* all inodes *///指向文件系统内的所有inode,通过它可以遍历所有inode对象
- struct list_head s_dirty; /* dirty inodes *///所有dirty的inode对象
- struct list_head s_io; /* parked for writeback */
- struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */
- struct list_head s_files;
- struct block_device *s_bdev;//指向文件系统存在的块设备指针
- struct list_head s_instances;
- struct quota_info s_dquot; /* Diskquota specific options */
- int s_frozen;
- wait_queue_head_t s_wait_unfrozen;
- char s_id[32]; /* Informational name */
- void *s_fs_info; /* Filesystem private info */
- /*
- * The next field is for VFS *only*. No filesystems have any business
- * even looking at it. You had been warned.
- */
- struct mutex s_vfs_rename_mutex; /* Kludge */
- /* Granularity of c/m/atime in ns.
- Cannot be worse than a second */
- u32 s_time_gran;
- };
超级块的内容需要读取具体文件系统在硬盘上的超级块结构获得,超级块是具体文件系统超级块的内存抽象。
创建、管理和撤销超级块对象的代码位于文件fs/super.c 中。超级块对象通过 alloc_super() 函数创建并初始化。在文件系统安装时,文件系统会调用该函数以便从磁盘读取文件系统超级块,并且将其信息填充到内存的超级块对象中。
从其结构可以看出,超级块包含了与文件系统相关的参数信息:包括inode表容量、文件系统中逻辑块的大小、以逻辑块计。文件系统的大小等。
2、目录项 dentry
首先有必要区分一下目录跟目录项。在Unix语义中,一切皆文件,所以目录也会被当成一个文件来处理,也就不存在目录对象,那么目录项又是什么?目录项代表的是路径中的一个组成部分,他可能包括一个普通文件,换言之,目录可以包含子目录(子文件)、目录是可以层层嵌套的,所以形成了文件路径,而文件路径中的每一部分,就是所谓的目录项。
对文件系统而言,通常,文件和目录是按树状结构来保存的,目录项就是反应文件系统的这种树状关系,在VFS里,目录本身也是一个文件,只是有点特殊。每个文件至少都有一个dentry,这个dentry链接到上级目录的dentry,根目录有一个dentry结构,而根目录里的文件和目录都链接到这个dentry,然后二级目录里的文件和目录,同样通过dentry链接到二级目录,这样层层链接,就形成了一棵dentry树,从树顶可以遍历整个文件系统的所有目录和文件。也就是说,因为这些关系,在Linux环境下你可以从根目录"/"出发,找到整个文件系统中的任何目录和文件,从文件你也可以cd
.. 返回上层目录。
上面的数据结构其实很简单,简单的说来就是,一个节点里面含有指向父目录集和子目录集的指针。
为了加快对dentry的查找(如果VFS层遍历路径名中所有的元素并将它们逐个解析成目录项对象,还要到达最深层目录,将是一个非常费力的工作),内核使用了hash表来缓存dentry,成为dentry cache。前面的进阶篇里有说到。file结构中的f_dentry指针就是指向dentry
cache中的dentry的。
- 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 *///指向一个inode结构,这个inode和dentry共同描述了一个普通文件或者目录文件
- /*
- * 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 *///dentry cache的hash链表
- struct dentry *d_parent; /* parent directory *///指向父dentry
- 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 *///dentry自身的链表头
- 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 */
- #ifdef CONFIG_PROFILING
- struct dcookie_struct *d_cookie; /* cookie, if any */
- #endif
- int d_mounted;//指示dentry是否是一个挂载点,如果是挂载点,该成员不为零
- unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */
- };
d_subdirs 是子项的链表头,所有的子项都要链接到这个链表,d_child是dentry自身的链表头,需要链接到父dentry的d_subdirs成员。d_parent则是指向父dentry结构的指针。当移动文件的时候,需要把一个dentry结构从旧的父dentry的链表上脱离,然后链接到新的父dentry的d_subdirs成员,这样dentry结构之间就构成了一棵目录树。
3、索引节点inode
inode代表一个文件,inode保存了文件的大小,创建时间,文件的块大小等参数,以及对文件的读写函数、文件的读写缓存等信息。
索引节点对象包含了内核在操作文件或目录时需要的全部信息。仅当文件被访问时,才在内存中创建。
一个真实的文件可以有多个dentry,因为指向文件的路径可以有多个(考虑到文件的链接),但是inode只有一个。
- struct inode {
- struct hlist_node i_hash;
- struct list_head i_list;//用于描述inode当前状态的链表
- struct list_head i_sb_list;//用于链接到超级块中的inode链表
- struct list_head i_dentry;//用于链接到dentry链表
- unsigned long i_ino;//inode号
- atomic_t i_count;//引用计数,递增递减均为原子操作
- umode_t i_mode;//文件类型
- unsigned int i_nlink;
- uid_t i_uid;//用户。组等等相关的
- gid_t i_gid;
- dev_t i_rdev;
- loff_t i_size;
- struct timespec i_atime;//三个时间戳
- struct timespec i_mtime;
- struct timespec i_ctime;
- unsigned int i_blkbits;//文件块的位数
- unsigned long i_blksize;
- unsigned long i_version;
- blkcnt_t i_blocks;
- unsigned short i_bytes;
- spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
- struct mutex i_mutex;
- struct rw_semaphore i_alloc_sem;
- struct inode_operations *i_op;//inode操作函数集
- 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 file {
- /*
- * fu_list becomes invalid after file_free is called and queued via
- * fu_rcuhead for RCU freeing
- */
- union {
- struct list_head fu_list;
- struct rcu_head fu_rcuhead;
- } f_u;
- struct dentry *f_dentry;//文件对应的dentry结构
- struct vfsmount *f_vfsmnt;//文件所属于的文件系统的vfsmount对象
- 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;//文件预读位置
- unsigned long f_version;
- void *f_security;
- /* 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;//文件的读写缓存
- };
VFS是具体文件系统的抽象,VFS是依靠超级块、dentry、inode以及文件这些结构来发挥作用。