Linux 内核文件系统关键数据结构



1、超级块 super block

超级块(super block)代表了整个文件系统本身,代表一个具体的已安装文件系统,通常。超级块是对应文件系统自身的控制块结构,超级块保存了文件系统设定的文件块大小,超级块的操作函数,文件系统内所有的inode也都要链接到超级块的链表头,当然还包括其余的信息,通过超级块对象,我们可以找到这些必要的信息。

超级块是文件系统的心脏,是与文件系统相关的,在超级块中保存了全局文件信息,如硬盘已用空间、数据块可用空间、inode节点信息等,一个文件系统中有哪些资源都记录在其中。

[cpp] view plain copy
 print?
  1. struct super_block {  
  2.     struct list_head    s_list;     /* Keep this first */  
  3.     dev_t           s_dev;      /* search index; _not_ kdev_t */  
  4.     unsigned long       s_blocksize;//指定了文件系统的块大小  
  5.     unsigned char       s_blocksize_bits;  
  6.     unsigned char       s_dirt;  
  7.     unsigned long long  s_maxbytes; /* Max file size *///最大文件的尺寸  
  8.     struct file_system_type *s_type;//指向邋file_system_type结构的指针  
  9.     struct super_operations *s_op;//超级块操作函数集  
  10.     struct dquot_operations *dq_op;  
  11.     struct quotactl_ops *s_qcop;  
  12.     struct export_operations *s_export_op;  
  13.     unsigned long       s_flags;  
  14.     unsigned long       s_magic;//魔术数字  
  15.     struct dentry       *s_root;//文件系统根目录项  
  16.     struct rw_semaphore s_umount;  
  17.     struct mutex        s_lock;//互斥锁  SMP safe  
  18.     int         s_count;  
  19.     int         s_syncing;  
  20.     int         s_need_sync_fs;  
  21.     atomic_t        s_active;  
  22.     void                    *s_security;  
  23.     struct xattr_handler    **s_xattr;  
  24.   
  25.     struct list_head    s_inodes;   /* all inodes *///指向文件系统内的所有inode,通过它可以遍历所有inode对象  
  26.     struct list_head    s_dirty;    /* dirty inodes *///所有dirty的inode对象  
  27.     struct list_head    s_io;       /* parked for writeback */  
  28.     struct hlist_head   s_anon;     /* anonymous dentries for (nfs) exporting */  
  29.     struct list_head    s_files;  
  30.   
  31.     struct block_device *s_bdev;//指向文件系统存在的块设备指针  
  32.     struct list_head    s_instances;  
  33.     struct quota_info   s_dquot;    /* Diskquota specific options */  
  34.   
  35.     int         s_frozen;  
  36.     wait_queue_head_t   s_wait_unfrozen;  
  37.   
  38.     char s_id[32];              /* Informational name */  
  39.   
  40.     void            *s_fs_info; /* Filesystem private info */  
  41.   
  42.     /* 
  43.      * The next field is for VFS *only*. No filesystems have any business 
  44.      * even looking at it. You had been warned. 
  45.      */  
  46.     struct mutex s_vfs_rename_mutex;    /* Kludge */  
  47.   
  48.     /* Granularity of c/m/atime in ns. 
  49.        Cannot be worse than a second */  
  50.     u32        s_time_gran;  
  51. };  

超级块的内容需要读取具体文件系统在硬盘上的超级块结构获得,超级块是具体文件系统超级块的内存抽象。

创建、管理和撤销超级块对象的代码位于文件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的。

[cpp] view plain copy
 print?
  1. struct dentry {  
  2.     atomic_t d_count;  
  3.     unsigned int d_flags;       /* protected by d_lock */  
  4.     spinlock_t d_lock;      /* per dentry lock */  
  5.     struct inode *d_inode;      /* Where the name belongs to - NULL is 
  6.                      * negative *///指向一个inode结构,这个inode和dentry共同描述了一个普通文件或者目录文件  
  7.     /* 
  8.      * The next three fields are touched by __d_lookup.  Place them here 
  9.      * so they all fit in a cache line. 
  10.      */  
  11.     struct hlist_node d_hash;   /* lookup hash list *///dentry cache的hash链表  
  12.     struct dentry *d_parent;    /* parent directory *///指向父dentry  
  13.     struct qstr d_name;//名字  
  14.   
  15.     struct list_head d_lru;     /* LRU list */  
  16.     /* 
  17.      * d_child and d_rcu can share memory 
  18.      */  
  19.     union {  
  20.         struct list_head d_child;   /* child of parent list *///dentry自身的链表头  
  21.         struct rcu_head d_rcu;  
  22.     } d_u;  
  23.     struct list_head d_subdirs; /* our children *///子项(可能是目录,可能是文件)的链表头  
  24.     struct list_head d_alias;   /* inode alias list */  
  25.     unsigned long d_time;       /* used by d_revalidate */  
  26.     struct dentry_operations *d_op;//操作函数集  
  27.     struct super_block *d_sb;   /* The root of the dentry tree */  
  28.     void *d_fsdata;         /* fs-specific data */  
  29. #ifdef CONFIG_PROFILING  
  30.     struct dcookie_struct *d_cookie; /* cookie, if any */  
  31. #endif  
  32.     int d_mounted;//指示dentry是否是一个挂载点,如果是挂载点,该成员不为零  
  33.     unsigned char d_iname[DNAME_INLINE_LEN_MIN];    /* small names */  
  34. };  
构建dentry树,重要的结构自然就是d_subdirs、d_child以及d_parent成员了。

d_subdirs 是子项的链表头,所有的子项都要链接到这个链表,d_child是dentry自身的链表头,需要链接到父dentry的d_subdirs成员。d_parent则是指向父dentry结构的指针。当移动文件的时候,需要把一个dentry结构从旧的父dentry的链表上脱离,然后链接到新的父dentry的d_subdirs成员,这样dentry结构之间就构成了一棵目录树。

3、索引节点inode

inode代表一个文件,inode保存了文件的大小,创建时间,文件的块大小等参数,以及对文件的读写函数、文件的读写缓存等信息。

索引节点对象包含了内核在操作文件或目录时需要的全部信息。仅当文件被访问时,才在内存中创建。

一个真实的文件可以有多个dentry,因为指向文件的路径可以有多个(考虑到文件的链接),但是inode只有一个。

[cpp] view plain copy
 print?
  1. struct inode {  
  2.     struct hlist_node   i_hash;  
  3.     struct list_head    i_list;//用于描述inode当前状态的链表  
  4.     struct list_head    i_sb_list;//用于链接到超级块中的inode链表  
  5.     struct list_head    i_dentry;//用于链接到dentry链表  
  6.     unsigned long       i_ino;//inode号  
  7.     atomic_t        i_count;//引用计数,递增递减均为原子操作  
  8.     umode_t         i_mode;//文件类型  
  9.     unsigned int        i_nlink;  
  10.     uid_t           i_uid;//用户。组等等相关的  
  11.     gid_t           i_gid;  
  12.     dev_t           i_rdev;  
  13.     loff_t          i_size;  
  14.     struct timespec     i_atime;//三个时间戳  
  15.     struct timespec     i_mtime;  
  16.     struct timespec     i_ctime;  
  17.     unsigned int        i_blkbits;//文件块的位数  
  18.     unsigned long       i_blksize;  
  19.     unsigned long       i_version;  
  20.     blkcnt_t        i_blocks;  
  21.     unsigned short          i_bytes;  
  22.     spinlock_t      i_lock; /* i_blocks, i_bytes, maybe i_size */  
  23.     struct mutex        i_mutex;  
  24.     struct rw_semaphore i_alloc_sem;  
  25.     struct inode_operations *i_op;//inode操作函数集  
  26.     const struct file_operations    *i_fop; /* former ->i_op->default_file_ops */  
  27.     struct super_block  *i_sb;//链接到超级块  
  28.     struct file_lock    *i_flock;  
  29.     struct address_space    *i_mapping;//缓存文件的内容,加快文件的读操作  
  30.     struct address_space    i_data;//指向文件数据块的指针  
  31.         ......  
  32. };  
4、文件对象 file

文件对象的作用是描述进程和文件交互的关系,需要注意的是,硬盘上并不存在这样一个文件结构,进程打开一个文件,内核就动态创建一个文件对象,同一个文件,在不同的进程中有不同的文件对象。

文件和文件对象是两个不同的概念,“文件”指的是硬盘上具体存在的文件,而“文件对象”是在内存里存在的文件结构。

[cpp] view plain copy
 print?
  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 dentry       *f_dentry;//文件对应的dentry结构  
  11.     struct vfsmount         *f_vfsmnt;//文件所属于的文件系统的vfsmount对象  
  12.     const struct file_operations    *f_op;//操作函数集  
  13.     atomic_t        f_count;  
  14.     unsigned int        f_flags;  
  15.     mode_t          f_mode;  
  16.     loff_t          f_pos;//进程对文件操作的位置  
  17.     struct fown_struct  f_owner;  
  18.     unsigned int        f_uid, f_gid;  
  19.     struct file_ra_state    f_ra;//文件预读位置  
  20.   
  21.     unsigned long       f_version;  
  22.     void            *f_security;  
  23.   
  24.     /* needed for tty driver, and maybe others */  
  25.     void            *private_data;  
  26.   
  27. #ifdef CONFIG_EPOLL  
  28.     /* Used by fs/eventpoll.c to link all the hooks to this file */  
  29.     struct list_head    f_ep_links;  
  30.     spinlock_t      f_ep_lock;  
  31. #endif /* #ifdef CONFIG_EPOLL */  
  32.     struct address_space    *f_mapping;//文件的读写缓存  
  33. };  
Linux 内核文件系统关键数据结构

VFS是具体文件系统的抽象,VFS是依靠超级块、dentry、inode以及文件这些结构来发挥作用。