20150514我读《深入理解linux内核》之虚拟文件系统笔记
2015-05-14 Lover雪儿
虚拟文件系统所隐含的思想就是把很多不同种类的文件系统的共同信息放入内核,其中有一个字段或者函数来支持Linux所支持的所有实际文件系统所提供的任何操作。对所调用的每个读、写或者其他函数,内核都能把他们替换成支持本地Linux文件系统、NTFS文件系统,或者文件所在的任何其他文件系统的实际函数。
虚拟文件系统可以称为虚拟文件系统转换,是一个内核软件层,用来处理与Unix标准文件系统相关的所有系统调用。其健壮性表现在能为各种文件系统提供一个通用的接口。
VFS支持的文件系统可以划分为:磁盘文件系统、网络文件系统、特殊文件系统。
注:当一个文件系统被安装在某一个目录上面,在父目录文件系统中的目录内容不再是可访问的,因为任何路径都将引用已安装的文件系统,但是,当被安装的文件系统卸载时,原目录的内容又可再现。这种令人惊讶的Unix文件系统特点可以由系统管理员用来隐藏文件,他们只需要把一个文件系统安装在要隐藏的目录中即可。
内核负责把一组合适的指针分配给每个打开文件相关的file变量,然后负责调用针对每个具体文件系统的函数(f_op字段指向)。
共用文件模型有下列对象类型组成:
①超级快对象:存放已安装文件系统的有关信息。
②索引节点对象:存放关于具体文件的一般信息。
③文件对象:存放打开文件与进程之间进行交互的有关信息。
④目录项对象:存放目录项与对应文件进行链接的有关系信息。
磁盘高速缓存属于软件机制,它允许内核将原本存在磁盘上的某些信息保存在RAM中,以便对这些数据的进一步访问能够快速进行,而不必慢速访问磁盘本身
VFS的数据结构
每个VFS对象都存放在一个适当的数据结构中,其中包括对象的属性和指向对象方法表的指针,内核可以动态的修改对象的方法,因为可以为对象建立专用的行为。
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; /* 文件系统类型 */ 9 const struct super_operations *s_op; /* 超级块方法 */ 10 struct dquot_operations *dq_op; /* 磁盘限额处理方法 */ 11 struct quotactl_ops *s_qcop; /* 磁盘限额管理方法 */ 12 const 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; /* 超级块信号量 */ 18 int s_count; /* 引用计数器 */ 19 int s_syncing; /* 表示对超级块的索引节点进行同步的标志 */ 20 int s_need_sync_fs; /* 对超级块的已安装文件系统进行同步的标志 */ 21 #ifdef CONFIG_SECURITY 22 void *s_security; /* 指向超级块安全数据结构体的指针 */ 23 #endif 24 struct xattr_handler **s_xattr; /* 指向超级块拓展属性结构的指针 */ 25 26 struct list_head s_inodes; /* 所有索引节点的链表all inodes */ 27 struct list_head s_dirty; /* 改进型索引节点的链表dirty inodes */ 28 struct list_head s_io; /* 等待被写入磁盘的索引节点的链表parked for writeback */ 29 struct list_head s_more_io; /* parked for more writeback */ 30 struct hlist_head s_anon; /* 用于处理远程网络文件系统的匿名anonymous dentries for (nfs) exporting */ 31 struct list_head s_files; /* 文件对象的链表 */ 32 /* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */ 33 struct list_head s_dentry_lru; /* unused dentry lru */ 34 int s_nr_dentry_unused; /* # of dentry on lru */ 35 36 struct block_device *s_bdev; /* 指向块设备驱动程序描述符的指针 */ 37 struct mtd_info *s_mtd; /* */ 38 struct list_head s_instances; /*用于给定文件系统类型的超级块对象链表的指针*/ 39 40 int s_frozen; /* 冻结文件系统时使用的标志 */ 41 wait_queue_head_t s_wait_unfrozen; /* 进程挂起的等待队列,直到文件系统被解冻 */ 42 43 char s_id[32]; /* 包含超级块的块设备名称Informational name */ 44 45 void *s_fs_info; /* 指向特定文件系统的超级块信息的指针Filesystem private info */ 46 47 /* 48 * The next field is for VFS *only*. No filesystems have any business 49 * even looking at it. You had been warned. 50 */ 51 struct mutex s_vfs_rename_mutex; /* Kludge */ 52 53 /* Granularity of c/m/atime in ns. 54 Cannot be worse than a second */ 55 u32 s_time_gran; /* */ 56 57 /* 58 * Filesystem subtype. If non-empty the filesystem type field 59 * in /proc/mounts will be "type.subtype" 60 */ 61 char *s_subtype; /* */ 62 63 /* 64 * Saved mount options for lazy filesystems using 65 * generic_show_options() 66 */ 67 char *s_options; /* */ 68 };
索引节点对象
1 struct inode { 2 struct hlist_node i_hash; /* 用于散列链表的指针 */ 3 struct list_head i_list; /* 用于描述索引节点当前状态的链表指针 */ 4 struct list_head i_sb_list; /* 用于超级块的索引节点链表的指针 */ 5 struct list_head i_dentry; /* 引用索引节点的目录项对象链表的头 */ 6 unsigned long i_ino; /* 索引节点号 */ 7 unsigned int i_nlink; /* 硬链接数目 */ 8 uid_t i_uid; /* 所有者标识符 */ 9 gid_t i_gid; /* 所属组标志符 */ 10 dev_t i_rdev; /* 实设备标识符 */ 11 u64 i_version; /* 版本号 */ 12 loff_t i_size; /* 文件的字节数 */ 13 #ifdef __NEED_I_SIZE_ORDERED 14 seqcount_t i_size_seqcount; /* */ 15 #endif 16 struct timespec i_atime; /* 上次访问文件的时间 */ 17 struct timespec i_mtime; /* 上次写文件的时间 */ 18 struct timespec i_ctime; /* 上次修改索引节点的时间 */ 19 unsigned int i_blkbits; /* 块的位数 */ 20 unsigned short i_bytes; /* 文件中最后一个块的字节数 */ 21 umode_t i_mode; /* */ 22 spinlock_t i_lock; /*保护索引节点一些字符的自旋锁i_blocks, i_bytes, maybe i_size */ 23 struct mutex i_mutex; /* */ 24 struct rw_semaphore i_alloc_sem; /* 在直接I/O文件操作中避免出现竞争条件的读写信号量 */ 25 const struct inode_operations *i_op; /* 索引节点的操作 */ 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 #ifdef CONFIG_QUOTA 30 struct dquot *i_dquot[MAXQUOTAS]; /* 索引节点磁盘限额 */ 31 #endif 32 struct list_head i_devices; /* 用于具体的字符或者块设备索引节点链表的指针 */ 33 int i_cindex; /* 拥有一组此设备号的设备文件的索引号 */ 34 35 __u32 i_generation; /* 索引节点版本号 */ 36 37 #ifdef CONFIG_DNOTIFY 38 unsigned long i_dnotify_mask; /* 目录通知事件的位掩码Directory notify events */ 39 struct dnotify_struct *i_dnotify; /* 用于目录通知for directory notifications */ 40 #endif 41 42 #ifdef CONFIG_INOTIFY 43 struct list_head inotify_watches; /* watches on this inode */ 44 struct mutex inotify_mutex; /* protects the watches list */ 45 #endif 46 47 unsigned long i_state; /* 索引节点的状态标志 */ 48 unsigned long dirtied_when; /* 索引节点的弄脏事件(数据不同步)jiffies of first dirtying */ 49 50 unsigned int i_flags; /* 文件系统的安装标志 */ 51 52 #ifdef CONFIG_SECURITY 53 void *i_security; /* 指向索引节点安全结构的指针 */ 54 #endif 55 void *i_private; /* fs or device private pointer */ 56 };
文件对象
1 struct file { 2 struct path f_path; 3 #define f_dentry f_path.dentry 4 #define f_vfsmnt f_path.mnt 5 const struct file_operations *f_op; 6 unsigned int f_flags; 7 loff_t f_pos; 8 unsigned int f_uid, f_gid; 9 10 u64 f_version; 11 #ifdef CONFIG_SECURITY 12 void *f_security; 13 #endif 14 /* needed for tty driver, and maybe others */ 15 void *private_data; 16 17 #ifdef CONFIG_EPOLL 18 /* Used by fs/eventpoll.c to link all the hooks to this file */ 19 struct list_head f_ep_links; 20 spinlock_t f_ep_lock; 21 #endif /* #ifdef CONFIG_EPOLL */ 22 #ifdef CONFIG_DEBUG_WRITECOUNT 23 unsigned long f_mnt_write_state; 24 #endif 25 };
目录项对象
VFS把每一个目录看做若干个子目录和文件组成的一个普通文件。
每个目录项对象可以处以以下四种状态之一:
空闲状态:不包换有效信息,且还没有被VFS使用。对应的内存区由slab分配器进行处理
未使用状态:还没有被内核使用,引用计数为0,但d_inode字段任然指向关联的索引节点。
正在使用状态:正在背内核使用,引用计数为正数,d_inode字段任然指向关联的索引节点
负状态:与目录相关的索引节点不复存在,相应的磁盘索引节点已经被删除,或者因为目录项对象是通过解析一个不存在文件的路径名创建的。d_inode为NULL
目录项高速缓存
为了最大限度的提高处理这些目录项对象的效率,Linux使用目录项告诉缓存,它是由两种数据类型结构组成:
①一个处于正使用,未使用或者负状态的目录项对象的集合
②一个散列表,从中能够快速获取与给定的文件名和目录名对应的目录项对象。同样,如果访问的对象不再目录项高速缓存中,则散列函数返回一个空值。
目录项高速缓冲的作用还相当于索引节点高速缓冲的控制器。在内核内存中,并不丢弃与未用目录项相关的索引节点,这是由于目录项高速缓冲仍在使用它们。因此这些索引节点对象保存在RAM中,并能够借助相应的目录项快速引用它们。
与进程相关的文件
每个进程都有他自己的当前工作目录和它自己的根目录。
fs_struct结构中定义了进程目录相关的信息。
files_struct结构体中存放了进程当前打开的文件信息。
文件系统类型
特殊文件系统
当网络和磁盘文件系统能够使用户处理存放在内核之外的信息时,特殊文件系统 可以为系统程序员和管理员提供一种容易的方式来操作内核的数据结构并实现操作系统的特殊特征。
特殊文件系统不限于物理块设备。然而,内核个每个安装的特殊文件系统分配一个虚拟的块设备,让其主设备号为0而次设备具有任意值(每个特殊文件系统有不同的值)
文件系统类型注册
VFS必须对代码目前已在内核中的所有文件系统的类型进行追踪,这就是通过文件系统类型注册来实现。
文件系统处理
Linux也使用系统的根文件系统,它有内核在引导阶段直接安装,并拥有系统初始化脚本以及最基本的系统程序。
其他文件系统要么由初始化脚本安装,要么由用户直接安装在已安装文件系统的目录上。安装文件系统的这个目录称为安装点。已安装文件系统属于安装点目录的一个子文件系统。已安装文件系统的根目录隐藏了父文件系统的安装点目录的原来的内容,而且父文件系统的整个子树位于安装点下。
命名空间
在传统的unix系统中,只有一个已安装文件系统树:从系统的根文件系统开始,每个进程通过指定合适的路径名可以访问已安装文件系统中的任何文件。
每个进程可拥有自己已安装文件系统树---叫进程的命名空间
通常大多数进程共享同一个命名空间,即位于系统的根文件系统且被init进程使用的已安装文件系统树。
当进程安装或者卸载一个文件系统时,仅修改它的命名空间。
文件系统安装
大多数的类unix内核中,每个文件系统只能安装一次,同样在某些类unix操作系统中,同一个文件系统被安装多次是可能的。
安装的文件系统形成一个层次,一个文件系统的安装点可能成为第二个文件系统的目录,而第二个文件系统又安装在第三个文件系统之上。
把多个安装点堆叠在一个单独的安装点上也是有可能的。尽管已经使用先前安装先的文件和目录的进程可以继续使用,但在同一安装点上的新安装隐藏前一个安装的文件系统。当最顶层的安装被删除时,下一层的安装再一次变为可见的。
安装普通文件系统
安装根文件系统
安装根文件系统是系统初始化的关键部分。这是一个相当负载的过程,因为linux允许根文件系统存放在很多不同的地方,比如硬盘分区、软盘、通过NFS共享的远程文件系统,甚至保存在ramdisk中(RAM中的虚拟块设备)
安装根文件系统分为两个阶段。
1.内核安装特殊rootfs文件系统,该文件系统仅仅提供一个作为初始安装点的空目录、
2.内核在空目录上安装实际根文件系统。
路径名查找
当进程必须识别一个文件时,就把它的 文件路径名传递给某一个VFS系统调用。
执行这一任务的标准过程就是分析路径名,并把它拆分为一个文件名序列,除了最后一个文件名以外,所有的文件名都必须是目录。
目录项高速缓存极大的加速了这一过程,因为它把最近常使用的目录项对象保留在内存中。
linux中符号链接嵌套层数不超过5.
文件加锁
当一个文件可以被多个进程访问时,就会出现同步问题。
对文件的同一位置的同时访问会产生不可预料的结果,为了避免这个问题,系统提供了一种允许进程对一个文件区进行加锁的机制。
Linux文件加锁
Linux支持所有的文件加锁方式:劝告锁和强制锁,以及fnctl(),flock(),lockf()系统调用。不过,lockf()系统调用仅仅是一个标准的库函数。
在linux中还引入了另一种基于fcntl()的强制锁,叫做租借锁。当一个进程试图打开由租借锁保护的文件时,它照样被阻塞。然而,拥有锁的进程接收到一个信号。一旦该进程得到通知,它应首先更新文件,以使文件的内容保持一致,然后释放锁,如果拥有者不在预订的时间间隔内这么做,则租借锁自动删除,且允许阻塞的进程继续执行。
* 博客园: http://www.cnblogs.com/lihaiyan/
* 邮箱:1063385677@qq.com
* QQ: 1063385677
* Copyright ©2014 Lover雪儿
********************************************
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。