20150514我读《深入理解linux内核》之虚拟文件系统笔记

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 };
VFS的数据结构struct super_block

索引节点对象

 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 };
struct inode

文件对象

 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 };
struct file

目录项对象

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()的强制锁,叫做租借锁。当一个进程试图打开由租借锁保护的文件时,它照样被阻塞。然而,拥有锁的进程接收到一个信号。一旦该进程得到通知,它应首先更新文件,以使文件的内容保持一致,然后释放锁,如果拥有者不在预订的时间间隔内这么做,则租借锁自动删除,且允许阻塞的进程继续执行。

原文地址:https://www.cnblogs.com/lihaiyan/p/4507522.html