Linux的虚拟文件系统与实际文件系统的关系如下所示:

VFS的原理

各种文件系统之所以有区别,就在于它们的目录文件结构各不相同,随之而来的也就是对文件目录的操作函数也不相同。对于前者可以在保留原系统目录结构的基础上,再构建一个新的统一的目录文件结构,而这个新目录文件中的信息是通过提取原系统目录文件信息进行重新组织来建立的。这样,用户面对的就不再是五花八门的目录文件,而是一个统一的目录文件。

为了让不同文件系统操作函数实现一个统一的操作界面,VFS利用函数指针,因为函数指针实质上就是对函数的一次抽象,如下:

从上图可见,用户在使用函数指针f()调用函数时,真正被调用的函数为该指针指向的函数,而指针f只是一个替身,或者说,它只是实际函数的一个虚拟表示。换句话说,通过同一个f可以调用不同的函数,至于调用的是哪一个函数,则取决于操作系统使哪个函数与指针f相关联。这也就是说,函数指针可以为多个不同的函数提供一个统一界面。

使用上述方法,Linux为虚拟文件系统构建了如下结构的函数指针集,以统一操作界面:

struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);int (*readdir) (struct file *, void *, filldir_t);unsigned int (*poll) (struct file *, struct poll_table_struct *);int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, struct dentry *, int datasync);int (*aio_fsync) (struct kiocb *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);int (*check_flags)(int);int (*flock) (struct file *, int, struct file_lock *);ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);int (*setlease)(struct file *, long, struct file_lock **);
};

用图形表示的file_operations如下所示:

上图表示的函数指针表,就是VFS提供的用户与实际文件系统之间的接口。也就是说,不论使用什么文件系统,用户面对的都是指针表中的函数指针,至于具体的实际文件系统则取决于与函数指针相关联的具体函数。例如,如果VFS与Ext2系统挂接,那么用户对虚拟文件系统的操作就是对Ext2文件系统的操作,如下图所示:

当然,如果实际文件系统不支持虚拟文件系统的某个操作,则该指针应该为NULL。

从上述内容可知,虚拟文件系统既没有文件,也不直接管理文件,它只是用户与实际文件系统之间的接口。因此,它并不需要保存在永久存储介质中,只是在需要时由内核在内存中创建起来的一个文件系统,所以叫做虚拟文件系统。出于系统性能的考虑和设计者的偏好,Linux系统VFS的结构与Ext2基本相同。

对于上述虚拟文件系统,《Linux内核源代码情景分析》中有如下描述:如果把内核比喻成计算机中的“母板”,把VFS比喻成“母板”上的一个“插槽”,那么每一个具体的文件系统就好像是一块“接口卡”。母板面对的是一个固定的插槽,而使用实际系统则是接口卡,插上什么卡就执行什么功能。

VFS的超级块

VFS超级块

所有的实际文件系统,一旦被安装到系统上,就必须由系统在内存为其创建一个虚拟文件系统的超级块。这个超级块实质上也就是系统把实际文件系统超级块的相关信息提取出来(如Ext2的ext2_sb_info),再与虚拟系统所需要的通用信息拼接起来而形成的一个格式统一的超级块,它相当于一个实际文件系统在虚拟文件系统中的身份证。这个虚拟超级块包含如下信息:

  • 设备标识符。这是存储文件系统的物理设备的设备标识符;
  • 节点指针,包括安装i节点指针和覆盖i节点指针。其中,安装i节点指针指向被安装的子文件系统的第一个i节点;覆盖i节点指针则指向被安装文件系统的安装点;
  • 文件数据块的大小;
  • 超级块操作集。这是一组指向超级块操作例程的指针,VFS利用他们可以对超级块和i节点进行访问操作;
  • 所安装的文件系统的类型;
  • 被安装文件系统的一些私有信息。

虚拟文件系统的超级块在文件include/linux/fs.h中定义如下:

struct super_block {struct list_head s_list;     /* 文件超级块链表头指针 */dev_t           s_dev;      /* 文件系统的块设备标识 */unsigned long       s_blocksize;        //数据块的大小unsigned char       s_blocksize_bits;        //块大小值所占的位数unsigned char       s_dirt;        //该值若为1,标识该超级块已被修改unsigned long long  s_maxbytes; /* 文件大小的最大值 */struct file_system_type   *s_type;        //已注册文件系统的链表指针const struct super_operations *s_op;        //指向超级块操作函数集的指针struct dquot_operations    *dq_op;struct quotactl_ops  *s_qcop;const 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;int          s_count;int         s_need_sync_fs;atomic_t     s_active;
#ifdef CONFIG_SECURITYvoid                    *s_security;
#endifstruct xattr_handler  **s_xattr;struct list_head  s_inodes;   /* all inodes */struct list_head    s_dirty;    /* dirty inodes */struct list_head  s_io;       /* parked for writeback */struct list_head  s_more_io;  /* parked for more writeback */struct hlist_head    s_anon;     /* anonymous dentries for (nfs) exporting */struct list_head    s_files;/* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */struct list_head  s_dentry_lru;   /* unused dentry lru */int          s_nr_dentry_unused; /* # of dentry on lru */struct block_device *s_bdev;struct mtd_info     *s_mtd;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; /* 指向实际文件系统超级块内核信息结构的指针 */fmode_t           s_mode;struct mutex s_vfs_rename_mutex; /* Kludge */u32        s_time_gran;char *s_subtype;char *s_options;struct list_head s_async_list;
};

虚拟文件系统的超级块,既包含虚拟文件系统的通用管理信息,也包含具体文件系统的私有信息。其中,结构的指针s_fs_info就指向具体实际文件系统的私有信息,该信息被保存在内存的一个结构中。

例如:当虚拟文件系统在与Ext2文件系统对接时,指针s_fs_info就指向Ext2文件系统的私有信息结构struct ext2_sb_info。即系统在安装Ext2系统时,会在内存中创建一个struct ext2_sb_info结构实例,将磁盘文件系统Ext2超级块struct ext2_super_block的部分私有信息提取到该结构,并将结构与VFS超级块指针s_fs_info关联起来,如下图所示:

结构struct ext2_sb_info的定义如下:

struct ext2_sb_info {unsigned long s_frag_size;      /* 碎片大小 */unsigned long s_frags_per_block;    /* 每块碎片的数目 */unsigned long s_inodes_per_block;    /* 每块节点数 */unsigned long s_frags_per_group;    /* 块组中的碎片数 */unsigned long s_blocks_per_group;    /* 块组中块的数量 */unsigned long s_inodes_per_group;    /* 块组中的节点数目 */unsigned long s_itb_per_group;     /* 块组中节点表所占用的块数 */unsigned long s_gdb_count;        /* 块组描述符表所占用的块数 */unsigned long s_desc_per_block;       /* 块组描述符数 */unsigned long s_groups_count;       /* 块组数 */unsigned long s_overhead_last;  /* 块缓冲区指针 */unsigned long s_blocks_last;    /* 指向缓冲区中超级块的指针 */struct buffer_head * s_sbh;      /* 指向缓冲区中组描述符表的指针 */struct ext2_super_block * s_es;     /* Pointer to the super block in the buffer */struct buffer_head ** s_group_desc;unsigned long  s_mount_opt;unsigned long s_sb_block;uid_t s_resuid;gid_t s_resgid;unsigned short s_mount_state;unsigned short s_pad;int s_addr_per_block_bits;int s_desc_per_block_bits;int s_inode_size;            //节点大小int s_first_ino;            //第一个节点号spinlock_t s_next_gen_lock;u32 s_next_generation;unsigned long s_dir_count;u8 *s_debts;struct percpu_counter s_freeblocks_counter;struct percpu_counter s_freeinodes_counter;struct percpu_counter s_dirs_counter;struct blockgroup_lock *s_blockgroup_lock;spinlock_t s_rsv_window_lock;struct rb_root s_rsv_window_root;struct ext2_reserve_window_node s_rsv_window_head;
};

VFS超级块的操作函数集

依照Linux的设计思想,Linux把有关文件系统超级块的操作函数集中地列写在一个封装了超级块操作函数指针的结构super_operations中,并用VFS超级块结构中的域s_op指向这个结构:

struct super_operations {struct inode *(*alloc_inode)(struct super_block *sb);void (*destroy_inode)(struct inode *);void (*dirty_inode) (struct inode *);int (*write_inode) (struct inode *, int);void (*drop_inode) (struct inode *);void (*delete_inode) (struct inode *);void (*put_super) (struct super_block *);void (*write_super) (struct super_block *);int (*sync_fs)(struct super_block *sb, int wait);int (*freeze_fs) (struct super_block *);int (*unfreeze_fs) (struct super_block *);int (*statfs) (struct dentry *, struct kstatfs *);int (*remount_fs) (struct super_block *, int *, char *);void (*clear_inode) (struct inode *);void (*umount_begin) (struct super_block *);int (*show_options)(struct seq_file *, struct vfsmount *);int (*show_stats)(struct seq_file *, struct vfsmount *);
#ifdef CONFIG_QUOTAssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
#endifint (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t);
};

这个结构与前面介绍的struct file_operations一样,相当于具体文件系统超级块所使用的安装在内核上的“插槽”。

VFS的dentry结构

结构dentry是实际文件系统的目录项在虚拟文件系统VFS中的对应物,实质上就是前面所讲的目录缓冲区。当系统访问一个具体文件时,会根据这个文件在磁盘上的目录在内存中创建一个dentry结构,它除了要存放文件目录信息之外,还要存放有关文件路径的一些动态信息。

例如:它建立了文件名与节点inode之间的联系,保存了目录项与其父、子目录之间的关系。之所以建立这样的一个文件目录的对应物,是为了同一个目录被再次访问时,其相关信息就可以直接由dentry获得,而不必再次重复访问磁盘。

VFS的dentry结构

结构dentry在文件include/linux/dcache.h中定义如下:

struct dentry {atomic_t d_count;unsigned int d_flags;        /* 记录目录项被引用次数的计数器 */spinlock_t d_lock;      /* 目录项的标志 */int d_mounted;struct inode *d_inode;        /* 与文件名相对应的inode */struct hlist_node d_hash;    /* 目录项形成的散列表 */struct dentry *d_parent; /* 指向父目录项的指针 */struct qstr d_name;        //包含文件名的结构struct list_head d_lru;     /* 已经没有用户使用的目录项的链表 */union {struct list_head d_child;   /* 父目录的子目录项形成的链表 */struct rcu_head d_rcu;} d_u;struct list_head d_subdirs;  /* i节点别名的链表 */struct list_head d_alias; /* inode alias list */unsigned long d_time;     /* used by d_revalidate */const struct dentry_operations *d_op;        //指向dentry操作函数集的指针struct super_block *d_sb;  /* 目录树的超级块,即本目录项所在目录树的根 */void *d_fsdata;            /* 文件系统的特定数据 */unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* 短文件名 */
};

结构中的域d_name是qstr类型的结构。该结构的定义如下:

struct qstr {unsigned int hash;            //文件名unsigned int len;            //文件名长度const unsigned char *name;        //散列值
};

内核通过维护一个散列表dentry_hashtable来管理dentry。系统一旦在内存中建立一个dentry,该dentry结构就会被链入散列表的某个队列中。

结构中的域d_count记录了本dentry被访问的次数。当某个dentry的d_count的值为0时,就意味着这个dentry结构已经没有用户使用了,这时这个dentry会被d_lru链入一个由系统维护的另一个队列dentry_unused中。由于内核从不删除曾经建立的dentry,于是,同一个目录被再次访问时,其相关信息就可以直接由dentry获得,而不必重复访问存储文件系统的外存。

另外,当本目录项有父目录节点时,其dentry结构通过域d_child挂入父目录项的子目录d_subdirs队列中,同时使指针d_parent指向父目录的dentry结构。若本目录有子目录,则将它们挂接在域d_subdirs指向的队列中。

dentry的操作函数

为了对dentry结构进行操作,系统定义了一个dentry操作函数集,目录项dentry中的域d_op就指向封装了这些操作函数指针的结构:

struct dentry_operations {int (*d_revalidate)(struct dentry *, struct nameidata *);        //判断目录项是否有效int (*d_hash) (struct dentry *, struct qstr *);            //生成一个散列值int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);        //比较两个文件名int (*d_delete)(struct dentry *);        //删除count为0的dentryvoid (*d_release)(struct dentry *);        //释放一个dentry对象void (*d_iput)(struct dentry *, struct inode *);        //丢弃目录项对应的inodechar *(*d_dname)(struct dentry *, char *, int);
};

由于在结构中的所有域都是操作函数的指针,因此这个结构相当于一个函数跳转表,系统通过这个函数跳转表来调用所要使用的函数。因此,这个结构相当于具体文件系统目录项所使用的安装在内核的“插槽”。

VFS的i节点

VFS的i节点简介

前面谈到,i节点是文件的控制块,它存放文件的基本信息。不同文件系统的i节点结构是不同的,甚至在一些一体化目录结构的文件系统(例如FAT系统)中根本就没有i节点的概念,所以在使用某一个实际文件系统时,内核必须按照虚拟文件系统i节点的格式在内存创建一个VFS的i节点,并根据该实际文件系统的静态数据来填写这个i节点。

VFS的i节点结构在文件include/linux/fs.h中定义如下:

struct inode {struct hlist_node  i_hash;            //指向散列表的指针struct list_head   i_list;            //指向i节点链表的这孩子很struct list_head   i_sb_list;struct list_head  i_dentry;            //指向dentry链表的指针unsigned long       i_ino;            //i节点号atomic_t        i_count;            //记录使用本节点进程数目的计数器unsigned int       i_nlink;            //节点连接的别名的数目uid_t           i_uid;            //文件拥有者的标识号gid_t          i_gid;            //文件拥有者所在组的标识号dev_t           i_rdev;            //文件设备的标识号u64            i_version;loff_t            i_size;
#ifdef __NEED_I_SIZE_ORDEREDseqcount_t      i_size_seqcount;
#endifstruct 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;        //指向i节点操作函数集的指针const struct file_operations   *i_fop;         /* 指向文件操作函数集的指针 */struct super_block    *i_sb;            //指向文件系统超级块的指针struct file_lock    *i_flock;struct address_space   *i_mapping;        //指向下一个域i_data的指针struct address_space    i_data;            //页缓冲区头结构void *generic_ip;            //指向实际文件节点信息结构的指针
#ifdef CONFIG_QUOTAstruct dquot     *i_dquot[MAXQUOTAS];
#endifstruct 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;#ifdef CONFIG_DNOTIFYunsigned long     i_dnotify_mask; /* Directory notify events */struct dnotify_struct  *i_dnotify; /* for directory notifications */
#endif#ifdef CONFIG_INOTIFYstruct list_head inotify_watches; /* watches on this inode */struct mutex        inotify_mutex;  /* protects the watches list */
#endifunsigned long     i_state;unsigned long       dirtied_when;   /* jiffies of first dirtying */unsigned int     i_flags;atomic_t        i_writecount;
#ifdef CONFIG_SECURITYvoid          *i_security;
#endifvoid          *i_private; /* fs or device private pointer */
};

与VFS的超级块一样,VFS的i节点也是既包含虚拟文件系统的通用管理系统,又包含具体文件系统i节点的私有信息。其中,结构的指针generic_ip就指向了具体文件系统i节点的私有信息,该信息也被保存在内存的一个结构中。

例如:当虚拟文件系统与Ext2文件系统对接时,指针generic_ip就指向Ext2文件系统的i节点信息结构ext2_inode_info。即系统在安装Ext2系统时,会在内存中创建一个ext2_inode_info结构实例,并将磁盘文件系统Ext2的i节点的部分私有信息提取到该结构。

结构struct ext2_inode_info的代码如下:

struct ext2_inode_info {__le32   i_data[15];            //数据库块指针数组__u32  i_flags;            //文件打开方式__u32   i_faddr;__u8    i_frag_no;            //第一个碎片号__u8  i_frag_size;            //碎片大小__u16 i_state;__u32   i_file_acl;            //文件访问控制链表__u32  i_dir_acl;__u32 i_dtime;            //文件删除时间__u32   i_block_group;            //文件所在块组号struct ext2_block_alloc_info *i_block_alloc_info;__u32   i_dir_start_lookup;
#ifdef CONFIG_EXT2_FS_XATTRstruct rw_semaphore xattr_sem;
#endif
#ifdef CONFIG_EXT2_FS_POSIX_ACLstruct posix_acl *i_acl;struct posix_acl *i_default_acl;
#endifrwlock_t i_meta_lock;struct mutex truncate_mutex;struct inode vfs_inode;struct list_head i_orphan;    /* unlinked but open inodes */
};

当虚拟文件与Ext2文件系统相关联时,VFS的索引节点如下图所示:

VFS的i节点操作函数

对于索引节点的操作函数封装在结构inode_operations中。该结构的定义如下:

struct inode_operations {int (*create) (struct inode *,struct dentry *,int, struct nameidata *);        //创建一个新i节点struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);        //查找一个i节点的dentryint (*link) (struct dentry *,struct inode *,struct dentry *);        //创建一个新的硬链接int (*unlink) (struct inode *,struct dentry *);        //删除一个硬链接int (*symlink) (struct inode *,struct dentry *,const char *);int (*mkdir) (struct inode *,struct dentry *,int);int (*rmdir) (struct inode *,struct dentry *);int (*mknod) (struct inode *,struct dentry *,int,dev_t);int (*rename) (struct inode *, struct dentry *,struct inode *, struct dentry *);int (*readlink) (struct dentry *, char __user *,int);void * (*follow_link) (struct dentry *, struct nameidata *);void (*put_link) (struct dentry *, struct nameidata *, void *);void (*truncate) (struct inode *);int (*permission) (struct inode *, int);int (*setattr) (struct dentry *, struct iattr *);int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);ssize_t (*listxattr) (struct dentry *, char *, size_t);int (*removexattr) (struct dentry *, const char *);void (*truncate_range)(struct inode *, loff_t, loff_t);long (*fallocate)(struct inode *inode, int mode, loff_t offset,loff_t len);int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,u64 len);
};

inode的结构的指针i_op指向这个函数集。

需要注意的是,inode结构还是要一个指向文件操作函数集struct file_operations的指针i_fop。

文件缓冲区

与内存相比,处理器读取磁盘数据的速度相当低,只相当于读取内存速度的千分之一,所以为了提高系统效率,Linux要在内存为最近访问的磁盘数据简历文件缓冲区。

所谓文件缓冲区,就是一块内核内存区域。由于该区域是进程与磁盘之间起缓冲作用的中介,因此缓冲区在与磁盘交换信息时必须以块为访问单位。即从磁盘的角度看,这个缓冲区是磁盘数据块缓冲区,而从进程的角度看,则是页缓冲区。如下图所示:

因此,内核中设置了两套用来管理缓冲区的数据结构。

磁盘数据块缓冲区

在Linux中,每一个磁盘数据块缓冲区用buffer_head结构来描述。buffer_head结构如下:

struct buffer_head {unsigned long b_state;       /* 缓冲区状态位图 */struct buffer_head *b_this_page;/* 指向同一页面的缓冲块,形成环形链表 */struct page *b_page;     /* 指向页缓冲指针 */sector_t b_blocknr;        /* 逻辑块号 */size_t b_size;            /* 块大小 */char *b_data;          /* 指向数据块缓冲区的指针 */struct block_device *b_bdev;        //指向块设备结构的指针bh_end_io_t *b_end_io;     /* I/O completion */void *b_private;        /* reserved for b_end_io */struct list_head b_assoc_buffers; /* associated with another mapping */struct address_space *b_assoc_map;    /* mapping this buffer isassociated with */atomic_t b_count;        /* 块引用计数 */
};

其中,域b_data是指向内存中磁盘块缓冲区的指针;域b_size表示缓冲区的大小。由于进程以页面为单位来访问缓冲区,所以结构中用域b_this_page把同一页面缓冲块组成了一个环形链表。

另外,系统根据磁盘数据块缓冲区的使用状态将它们分别组成了多个队列。

内存中的一个磁盘块缓冲区的队列示意图如下所示:

磁盘数据块缓冲区是一种全局资源,可以被所有的文件系统共享使用。

页缓冲区

为了方便进程对文件的使用,并在需要时能把以块为单位的磁盘块缓冲区(块的大小一般为512字节)映射到以页为单位(页的大小一般4k)的用户空间,VFS还建立了页缓冲区。由于进程是通过VFS的i节点来访问文件的,因此,文件的页缓冲区也就被设置在inode结构中。

inode结构中有一个指向自身的i_data域的指针i_mapping,这个i_data域是一个address_space的结构。而每一个页面的所有信息由struct page来描述,它有一个名称为mapping的域,这是一个指针,它指向一个struct address_space类型结构。

缓冲区的数据结构struct address_space的主要内容如下:

struct address_space {struct inode       *host;          /* 属主的索引节点 */struct radix_tree_root page_tree;          /* 全部页面的radix数 */spinlock_t     tree_lock;          /* and lock protecting it */unsigned int        i_mmap_writable;        /* count VM_SHARED mappings */struct prio_tree_root i_mmap;         /* tree of private and shared mappings */struct list_head   i_mmap_nonlinear;        /*list VM_NONLINEAR mappings */spinlock_t      i_mmap_lock;        /* protect tree, count, list */unsigned int     truncate_count;     /* Cover race condition with truncate */unsigned long       nrpages;        /* 占用的物理边框总数 */pgoff_t          writeback_index;        /* writeback starts here */const struct address_space_operations *a_ops;        /* 页缓冲区操作函数集指针 */unsigned long      flags;          /* error bits/gfp mask */struct backing_dev_info *backing_dev_info;     /* 预读信息 */spinlock_t        private_lock;       /* for use by the address_space */struct list_head  private_list;       /* 页缓冲区链表 */struct address_space    *assoc_mapping;     /* 相关缓存 */
} __attribute__((aligned(sizeof(long))));

页缓冲区与磁盘块缓冲区之间的关系如下图所示:

依照Linux的一贯风格,Linux将缓冲区操作函数封装在如下的address_space_operations结构中:

struct address_space_operations {int (*writepage)(struct page *page, struct writeback_control *wbc);int (*readpage)(struct file *, struct page *);void (*sync_page)(struct page *);/* Write back some dirty pages from this mapping. */int (*writepages)(struct address_space *, struct writeback_control *);/* Set a page dirty.  Return true if this dirtied it */int (*set_page_dirty)(struct page *page);int (*readpages)(struct file *filp, struct address_space *mapping,struct list_head *pages, unsigned nr_pages);int (*write_begin)(struct file *, struct address_space *mapping,loff_t pos, unsigned len, unsigned flags,struct page **pagep, void **fsdata);int (*write_end)(struct file *, struct address_space *mapping,loff_t pos, unsigned len, unsigned copied,struct page *page, void *fsdata);/* Unfortunately this kludge is needed for FIBMAP. Don't use it */sector_t (*bmap)(struct address_space *, sector_t);void (*invalidatepage) (struct page *, unsigned long);int (*releasepage) (struct page *, gfp_t);ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,loff_t offset, unsigned long nr_segs);int (*get_xip_mem)(struct address_space *, pgoff_t, int,void **, unsigned long *);/* migrate the contents of a page to the specified target */int (*migratepage) (struct address_space *,struct page *, struct page *);int (*launder_page) (struct page *);int (*is_partially_uptodate) (struct page *, read_descriptor_t *,unsigned long);
};

通常,i_fop(inode的指向文件操作函数集的指针)并不直接与块设备联系,而是间接通过a_ops(address_space的页缓冲区操作函数集)读写文件。文件的页缓冲就是i_fop与a_ops之间的缓冲,它是块设备缓冲之上的更高一级缓冲,直接用于具体文件的读写。

Linux的Proc文件系统

从原理上来讲,计算机中凡是能够提供信息和接受信息的设备或者主体,都可以被抽象成文件。计算机应用程序经常需要了解Linux内核和正在运行进程的当前状态和信息,并且希望能对这些信息做简单的修改。为此,Linux创建了一个专门提供上述信息的文件系统——Proc文件系统,该文件系统具有固定的安装点/proc,所以有时也把它叫做/proc文件系统。

Proc是一种特殊的文件系统。说它特殊,是因为它的文件并不像普通文件那样存放在外存储器中,它只是存在于内存中的一种文件。同样,在外存储器中也不存在这个文件系统的目录和节点,它们也只存在于内存。当系统关闭之后,Proc文件系统包括其中所有的文件也就不复存在了。

实质上,Proc文件就是一个特殊的内存区,只不过系统把这个内存区看成文件,并且按照文件方式进行管理。这个文件中存放的都是实时从操作系统的各个部分收集来的动态数据,进程通过访问这个文件不但可以方便地获得内核当前的运行状况和内部数据信息,并且还可以对其进行修改和配置。下面是使用Proc文件系统的优点:

  • 可以利用Proc文件系统的文件,开发一些专门用于获取内核数据的应用程序;
  • 完成用户空间和内核空间的通信,能得到内核运行时的数据,安全且方便;
  • 利用Proc文件可使进程直接对内核的参数进行配置的特点,可以在不重新编译内核的情况下优化系统配置。

参考文章:深入理解linux系统下proc文件系统内容。

【Linux】Linux的虚拟文件系统相关推荐

  1. Linux(一) VFS虚拟文件系统

    一.先了解一下什么是挂载 Linux有自己的一套文件系统,例如Ext2.Ext3,但是外部其他文件系统时,由于各个文件系统都各自有一套的文件管理体系,是无法通过Linux本身访问文件的方式直接访问的, ...

  2. 深入linux内核架构--虚拟文件系统VFS

    [推荐阅读] Linux内核源码分析--内核启动之zImage自解压过程 你应该知道的Linux内核基础及内核编译 深入理解LINUX内核堆栈 [零声教育]vico老师教你怎么学习Linux内核 值得 ...

  3. linux内核之虚拟文件系统

    一.虚拟文件系统概述 虚拟文件系统VFS(也成虚拟文件交换)作为内核子系统,为用户空间程序提供了文件和文件系统相关的统一接口.通过VFS,应用程序可以使用相同接口完成不同介质上不同文件系统的数据读写操 ...

  4. linux文件体系结构和虚拟文件系统

    linux中的虚拟文件系统(Virtual File System, VFS)是一种采用面向对象编程策略(尽管书写操作系统的C语言本身不支持面向对象编程,但是思想还是可以借鉴的),是对该操作系统所支持 ...

  5. 十九.Linux开发之根文件系统移植——根文件系统的原理

    有道云笔记地址: 详情看这里链接,记录太多,就不一一排版了. http://note.youdao.com/noteshare?id=f9c7c1b589233d7b6ed661c3749f1ce8& ...

  6. Linux虚拟文件系统简介

    本文将对Linux下的VFS做一个简单介绍,主要包括VFS里面的一些概念,以及文件系统是如何与VFS交互的. 本文所涉及的代码摘自Linux-4.4.0-59 什么是VFS VFS的全称为virtua ...

  7. Linux内核设计与实现笔记(一) 虚拟文件系统、块I/O层

    虚拟文件系统 系统内所有文件系统不但依赖VFS共存,而且也依靠VFS系统协同工作. VFS使得用户可以直接使用open/read/write等系统调用,而无需考虑具体文件系统和实际物理介质. VFS中 ...

  8. Linux虚拟文件系统解析

    概述 虚拟文件系统(Virtual Filesystem Switch,简称VFS)所有的数据结构都是在运行以后建立的,并在卸载时删除,在磁盘中并没有存储这些数据结构.虚拟文件系统只有和实际文件系统( ...

  9. linux虚拟文件系统浅析

    linux虚拟文件系统浅析 虚拟文件系统(VFS) 在我看来, "虚拟"二字主要有两层含义: 1, 在同一个目录结构中, 可以挂载着若干种不同的文件系统. VFS隐藏了它们的实现细 ...

  10. Linux 操作系统原理 — 文件系统 — 虚拟文件系统

    目录 文章目录 目录 为什么需要文件系统? Linux 的虚拟文件系统 创建 Linux 文件系统 为什么需要文件系统? 程序 = 数据结构 + 算法,所有的应用程序都需要存储和检索信息,进程运行时它 ...

最新文章

  1. java(13)内部类
  2. 【python练习】支付宝自动偷取能量
  3. java 集合反射_关于granite源码包CollectionUtil集合工具类获取集合反射类型、实例化各种集合类型HashSet/ArrayList等...
  4. Python学习之urlib模块和urllib2模块学习
  5. mysql导出(导入)数据库window平台
  6. 数据科学入门与实战:玩转pandas之四
  7. Python--Flask初学Demo(11.10)
  8. PMP课程笔记:第12章 项目采购管理
  9. Linux文件加密方式gzip和bzip
  10. 使压缩文件隐藏在图片格式中的方法(c语言版)--图片合成器
  11. 谁说变态才叫函数,带你了解不一样的表格函数
  12. NppFTP-Disconnected
  13. 《这个历史挺靠谱·上·袁腾飞讲中国史》读后感
  14. iOS 设置个人头像
  15. 利用MSCNN实现人群密度监测
  16. MySQL数据库入门超级详细教程
  17. AMD平台使用Android Studio原生安卓模拟器
  18. Code of conduct[行为准则]
  19. 简单的LaTeX数学表达式教程
  20. 788_AUTOSAR_RS_SystemTemplate3_通信2_时序以及SAE J1939等

热门文章

  1. MySQL之基础管理
  2. 《芳华》文工团的花儿特辑 冯小刚圆女兵情结
  3. 唐太宗李世民的14儿子与21女儿[zz]
  4. 技术员 Ghost Win 7 Sp1(x86/x64)特别版 2018
  5. 3D模型欣赏:科幻场景走廊 融合多重科幻元素,材质处理细致
  6. 降龙十八掌之 springboot整合shiro(含MD5加密)
  7. mSATA 群联PS3111+长江YMTC颗粒开卡实录,PS3111开卡软件下载
  8. qsssssssssss
  9. L11注意力机制和Seq2seq模型
  10. 在 SQL 中计算分页元数据,无需额外的往返