Linux文件系统2---VFS的四个主要对象

https://www.cnblogs.com/smartjourneys/p/7260911.html

1.引言

本文所述关于文件管理的系列文章主要是对陈莉君老师所讲述的文件系统管理知识讲座的整理。

Linux可以支持不同的文件系统,它源于unix文件系统,也是unix文件系统的一大特色。

Linux文件系统1--概述 中我们了解了文件系统的作用,以及为了使得所有的文件系统能在同一个操作系统上工作,而产生的虚拟文件系统。

本章我们开始分析万能的虚拟文件系统是怎么构成的,虚拟文件文件系统由四个主要的对象构成,分别是:超级块,索引结点,目录项,文件对象。

而所谓的文件系统主要是对文件对象进行管理,那么,其余的三个结构体是用来做什么的呢?

想一下,如果,你要查一份文件,必须要核对文件对象,那么文件对象越大,CPU则会浪费很多时间在核对上。

所以,我们给文件对象学着外国人一样, 起了三层结构的名字,目录项--索引结点--超级块,是不是很有趣。

struct file_system_type {
	const char *name;
	int fs_flags;
	struct super_block *(*read_super) (struct super_block *, void *, int);//ext2_read_super读取超级块,一般是第一个块
	struct module *owner;
	struct file_system_type * next;
	struct list_head fs_supers;
};
每个文件系统都有一个全局file_system_type类型的全局变量,例如:ext2_fs_type代表ext2文件系统
mount的时候会调用read_super

 

  • mount的过程调用,如果启动参数没有指定文件类型的话,将轮询并尝试所有的
sys_mount
 ->do_mount
   ->do_add_mount
     ->do_kern_mount
       ->get_sb_bdev
         ->fs_type->read_super
            ->ext2_read_super
            

 

 

 

2.文件系统的四个主要对象概述

  • 超级块对象

存放系统中已安装文件系统的信息

  • 索引节点对象

存放关于具体文件的一般信息

  • 目录项对象

存放目录项与对应文件进行链接的信息

  • 文件对象

存放打开文件与进程之间进行交互的有关信息

3. 超级块super_block

3.1 struct super_block

复制代码

  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 char        s_blocksize_bits;
  5     unsigned long        s_blocksize; /*以字节为单位的数据块的大小*/
  6     loff_t            s_maxbytes;    /* Max file size */
  7     struct file_system_type    *s_type; /*文件系统类型*/
  8     const struct super_operations    *s_op; /*指向超级块操作的函数集合*/
  9     const struct dquot_operations    *dq_op;
 10     const struct quotactl_ops    *s_qcop;
 11     const struct export_operations *s_export_op;
 12     unsigned long        s_flags;
 13     unsigned long        s_iflags;    /* internal SB_I_* flags */
 14     unsigned long        s_magic;
 15     struct dentry        *s_root;
 16     struct rw_semaphore    s_umount;
 17     int            s_count;
 18     atomic_t        s_active;
 19 #ifdef CONFIG_SECURITY
 20     void                    *s_security;
 21 #endif
 22     const struct xattr_handler **s_xattr;
 23 
 24     struct hlist_bl_head    s_anon;        /* anonymous dentries for (nfs) exporting */
 25     struct list_head    s_mounts;    /* list of mounts; _not_ for fs use */
 26     struct block_device    *s_bdev;
 27     struct backing_dev_info *s_bdi;
 28     struct mtd_info        *s_mtd;
 29     struct hlist_node    s_instances;
 30     unsigned int        s_quota_types;    /* Bitmask of supported quota types */
 31     struct quota_info    s_dquot;    /* Diskquota specific options */
 32 
 33     struct sb_writers    s_writers;
 34 
 35     char s_id[32];                /* Informational name */
 36     u8 s_uuid[16];                /* UUID */
 37 
 38     void             *s_fs_info;    /* Filesystem private info 具体文件系统的私有数据*/
 39     unsigned int        s_max_links;
 40     fmode_t            s_mode;
 41 
 42     /* Granularity of c/m/atime in ns.
 43        Cannot be worse than a second */
 44     u32           s_time_gran;
 45 
 46     /*
 47      * The next field is for VFS *only*. No filesystems have any business
 48      * even looking at it. You had been warned.
 49      */
 50     struct mutex s_vfs_rename_mutex;    /* Kludge */
 51 
 52     /*
 53      * Filesystem subtype.  If non-empty the filesystem type field
 54      * in /proc/mounts will be "type.subtype"
 55      */
 56     char *s_subtype;
 57 
 58     /*
 59      * Saved mount options for lazy filesystems using
 60      * generic_show_options()
 61      */
 62     char __rcu *s_options;
 63     const struct dentry_operations *s_d_op; /* default d_op for dentries */
 64 
 65     /*
 66      * Saved pool identifier for cleancache (-1 means none)
 67      */
 68     int cleancache_poolid;
 69 
 70     struct shrinker s_shrink;    /* per-sb shrinker handle */
 71 
 72     /* Number of inodes with nlink == 0 but still referenced */
 73     atomic_long_t s_remove_count;
 74 
 75     /* Being remounted read-only */
 76     int s_readonly_remount;
 77 
 78     /* AIO completions deferred from interrupt context */
 79     struct workqueue_struct *s_dio_done_wq;
 80     struct hlist_head s_pins;
 81 
 82     /*
 83      * Keep the lru lists last in the structure so they always sit on their
 84      * own individual cachelines.
 85      */
 86     struct list_lru        s_dentry_lru ____cacheline_aligned_in_smp;
 87     struct list_lru        s_inode_lru ____cacheline_aligned_in_smp;
 88     struct rcu_head        rcu;
 89     struct work_struct    destroy_work;
 90 
 91     struct mutex        s_sync_lock;    /* sync serialisation lock */
 92 
 93     /*
 94      * Indicates how deep in a filesystem stack this SB is
 95      */
 96     int s_stack_depth;
 97 
 98     /* s_inode_list_lock protects s_inodes */
 99     spinlock_t        s_inode_list_lock ____cacheline_aligned_in_smp;
100     struct list_head    s_inodes;    /* all inodes 所有的inodes*/
101 };

复制代码

  • 超级块用来描述整个文件系统的信息
  • 每个具体的文件系统都有自己的超级块
  • VFS超级块是各种文件系统在安装时建立的,并在卸载时被自动删除,其数据结构是super_block
  • 所有超级块对象都以双向循环链表的形式链接在一起

图 所有超级块链接在一起

 

3.2 struct super_operations

图 超级块对象的操作函数

 

  • 与超级块关联的方法就是超级块操作表,这些操作是由struct super_operations来描述
  • 具体的例子:ext2的super_op
static struct super_operations ext2_sops = {
	read_inode:	ext2_read_inode,
	write_inode:	ext2_write_inode,
	put_inode:	ext2_put_inode,
	delete_inode:	ext2_delete_inode,
	put_super:	ext2_put_super,
	write_super:	ext2_write_super,
	statfs:		ext2_statfs,
	remount_fs:	ext2_remount,
};

 

3.3 打印超级块信息

 

4.索引节点inode

 4.1 struct inode

复制代码

 1 struct inode {
 2     umode_t            i_mode;
 3     unsigned short        i_opflags;
 4     kuid_t            i_uid;
 5     kgid_t            i_gid;
 6     unsigned int        i_flags;
 7 
 8 #ifdef CONFIG_FS_POSIX_ACL
 9     struct posix_acl    *i_acl;
10     struct posix_acl    *i_default_acl;
11 #endif
12 
13     const struct inode_operations    *i_op;
14     struct super_block    *i_sb;
15     struct address_space    *i_mapping;
16 
17 #ifdef CONFIG_SECURITY
18     void            *i_security;
19 #endif
20 
21     /* Stat data, not accessed from path walking */
22     unsigned long        i_ino;
23     /*
24      * Filesystems may only read i_nlink directly.  They shall use the
25      * following functions for modification:
26      *
27      *    (set|clear|inc|drop)_nlink
28      *    inode_(inc|dec)_link_count
29      */
30     union {
31         const unsigned int i_nlink;
32         unsigned int __i_nlink;
33     };
34     dev_t            i_rdev;
35     loff_t            i_size;
36     struct timespec        i_atime;
37     struct timespec        i_mtime;
38     struct timespec        i_ctime;
39     spinlock_t        i_lock;    /* i_blocks, i_bytes, maybe i_size */
40     unsigned short          i_bytes;
41     unsigned int        i_blkbits;
42     blkcnt_t        i_blocks;
43 
44 #ifdef __NEED_I_SIZE_ORDERED
45     seqcount_t        i_size_seqcount;
46 #endif
47 
48     /* Misc */
49     unsigned long        i_state; /*索引节点的状态标志*/
50     struct mutex        i_mutex;
51 
52     unsigned long        dirtied_when;    /* jiffies of first dirtying */
53     unsigned long        dirtied_time_when;
54 
55     struct hlist_node    i_hash; /*指向哈希链表的指针*/
56     struct list_head    i_io_list;    /* backing dev IO list */
57 #ifdef CONFIG_CGROUP_WRITEBACK
58     struct bdi_writeback    *i_wb;        /* the associated cgroup wb */
59 
60     /* foreign inode detection, see wbc_detach_inode() */
61     int            i_wb_frn_winner;
62     u16            i_wb_frn_avg_time;
63     u16            i_wb_frn_history;
64 #endif
65     struct list_head    i_lru;        /* inode LRU list 指向索引节点链表的指针*/
66     struct list_head    i_sb_list; /*指向超级块的指针*/
67     union {
68         struct hlist_head    i_dentry;
69         struct rcu_head        i_rcu;
70     };
71     u64            i_version;
72     atomic_t        i_count;
73     atomic_t        i_dio_count;
74     atomic_t        i_writecount;
75 #ifdef CONFIG_IMA
76     atomic_t        i_readcount; /* struct files open RO */
77 #endif
78     const struct file_operations    *i_fop;    /* former ->i_op->default_file_ops */
79     struct file_lock_context    *i_flctx;
80     struct address_space    i_data;
81     struct list_head    i_devices;
82     union {
83         struct pipe_inode_info    *i_pipe;
84         struct block_device    *i_bdev;
85         struct cdev        *i_cdev;
86         char            *i_link;
87     };
88 
89     __u32            i_generation;
90 
91 #ifdef CONFIG_FSNOTIFY
92     __u32            i_fsnotify_mask; /* all events this inode cares about */
93     struct hlist_head    i_fsnotify_marks;
94 #endif
95 
96     void            *i_private; /* fs or device private pointer */
97 };

复制代码

 

  • 文件系统处理文件所需要的所有信息都保存在称为索引节点的inode结构体中
  • 同一个文件系统中,每个文件的索引节点号都是唯一的
  • 与索引节点关联的方法由struct inode_operations来描述
  • inode有两个设备号:i_dev(常规文件的设备号),i_rdev(某一设备的设备号)
  • LInux文件系统的另外一大特色:设备即文件。驱动中设备号的来源

 4.2  struct inode_operations

图 索引节点的操作函数

5.目录项dentry

5.1 struct dentry

复制代码

84    struct dentry {
85        /* RCU lookup touched fields */
86        unsigned int d_flags;        /* protected by d_lock */
87        seqcount_t d_seq;        /* per dentry seqlock */
88        struct hlist_bl_node d_hash;    /* lookup hash list */
89        struct dentry *d_parent;    /* parent directory */
90        struct qstr d_name;
91        struct inode *d_inode;        /* Where the name belongs to - NULL is
92                         * negative */
93        unsigned char d_iname[DNAME_INLINE_LEN];    /* small names */
94    
95        /* Ref lookup also touches following */
96        struct lockref d_lockref;    /* per-dentry lock and refcount */
97        const struct dentry_operations *d_op;
98        struct super_block *d_sb;    /* The root of the dentry tree */
99        unsigned long d_time;        /* used by d_revalidate */
100        void *d_fsdata;            /* fs-specific data */
101    
102        union {
103            struct list_head d_lru;        /* LRU list */
104            wait_queue_head_t *d_wait;    /* in-lookup ones only */
105        };
106        struct list_head d_child;    /* child of parent list */
107        struct list_head d_subdirs;    /* our children */
108        /*
109         * d_alias and d_rcu can share memory
110         */
111        union {
112            struct hlist_node d_alias;    /* inode alias list */
113            struct hlist_bl_node d_in_lookup_hash;    /* only for in-lookup ones */
114             struct rcu_head d_rcu;
115        } d_u;
116    };

复制代码

 

  • 每个文件除了一个struct inode结构体外,还要一个目录项struct dentry结构
  • dentry代表的逻辑意义上的文件,描述的是文件逻辑上的属性,目录项对象在磁盘上并没有对应的映像
  • inode代表的是物理意义上的文件,记录的是物理上的属性,对于一个具体的文件系统,其inode在磁盘上有对应的映像
  • 一个索引节点可能对应多个目录项对象

5.2  struct dentry_operations

图 目录项的操作函数

6. 文件对象(file)

 

6.1 struct file

复制代码

struct file {
836        union {
837            struct llist_node    fu_llist;//文件对象链表
838            struct rcu_head     fu_rcuhead; //释放之后的RCU链表
839        } f_u;
840        struct path        f_path;
841        struct inode        *f_inode;    /* cached value */
842        const struct file_operations    *f_op;
843    
844        /*
845         * Protects f_ep_links, f_flags.
846         * Must not be taken from IRQ context.
847         */
848        spinlock_t        f_lock;
849        atomic_long_t        f_count;   //文件对象的使用计数
850        unsigned int         f_flags;  //当打开文件时所使用的标志
851        fmode_t            f_mode;     //文件的访问模式
852        struct mutex        f_pos_lock;
853        loff_t            f_pos;       //文件当前的位移量
854        struct fown_struct    f_owner;  //拥有者通过信号量进行异步I/O传输
855        const struct cred    *f_cred;
856        struct file_ra_state    f_ra;
857    
858        u64            f_version;
859    #ifdef CONFIG_SECURITY
860        void            *f_security;  //安全模块
861    #endif
862        /* needed for tty driver, and maybe others */
863        void            *private_data;  //tty 设备驱动的钩子
864    
865    #ifdef CONFIG_EPOLL
866        /* Used by fs/eventpoll.c to link all the hooks to this file */
867        struct list_head    f_ep_links;  //事件池锁
868        struct list_head    f_tfile_llink;
869    #endif /* #ifdef CONFIG_EPOLL */
870        struct address_space    *f_mapping;  //页缓存映射
871    } __attribute__((aligned(4)));    /* lest something weird decides that 2 is OK */

复制代码

  • 进程通过文件描述符来访问文件
  • LInux用一个file文件对象来保存打开文件的位置,这个对象称为打开的文件描述符
  • file结构主要保存了文件位置,还把指向文件索引节点的指针也放在其中
  • file结构形成一个双链表,称为系统打开文件表

 6.2 struct file_operations

6.3  struct files_struct

 

  • 文件描述符用来描述打开的文件
  • 每个进程用一个files_struct结构来记录文件描述符的使用情况
  • 这个files_stuct结构称为用户打开文件表,它是进程的私有数据

 

图 用户打开文件表

6.4 struct fs_struct

复制代码

8    struct fs_struct {
9        int users;
10        spinlock_t lock;
11        seqcount_t seq;
12        int umask;  //用于为新创建的文件设置初始文件许可权限
13        int in_exec;
14        struct path root, pwd;
15    };

复制代码

  • 描述进程与文件系统的关系

 7. 主要数据结构之间的关系

图 主要数据结构之间的关系

  • 超级块是对一个文件系统的描述
  • 索引节点是对一个文件物理属性的描述
  • 目录项是对一个文件逻辑属性的描述
  • 一个进程所处的位置由fs_struct描述
  • 一个进程(或用户)打开的文件由files_struct描述
  • 整个系统所打开的文件由 file结构来描述

 

 8. sys_open

sys_open
 ->filp_open
   ->open_namei 
   ->dentry_open
   ->inode->i_fop
     ->ext2_dir_operations.open(null)/ext2_file_operations.open(generic_file_open)/def_blk_fops.open(blkdev_open)
     
   

 

  • inode->i_fop 对于目录没有open操作
  • inode->i_fop 对于普通文件的open操作只是检查了文件
  • inode->i_fop 对于块设备文件的open操作调用blkdev_open

 

 

 9. sys_read

aaa

saaaa

bbbb

 

Linux-块设备驱动之框架详细分析

(ll_rw_block/sumbit_bh/buff_head/do_hd_request)

对于2.4__make_request会plug到tq_task ,然后有工作任务(2.6work_queue)调度

 

任务队列(task queues)/工作队列(work queues)

       

 tq_task -> request_queue -> request_queue  -> request_queue
               |                |                 |
               V                V                 V
              req              req               req
               |                |                 | 
               V                V                 V
               bh ->bh          bh->bh            bh->bh
               
               

 

2.4 run_task_queue:   调用generic_unplug_device,从request_queue中取下一个req的第一个bh去请求硬盘读,因为linus电梯,那么就会一个req中合并有多个bh请求,那么就会再次请求硬盘。

 

读完成后,会唤醒等待这个req上的请求进程来bh中读取数据

 

2.6的内核代码使用kblockd_workqueue工作队列来完成处理request_queue

plug/unplug机制----

一、基本原理
Linux块设备层使用了plug/unplug(蓄流/泄流)的机制来提升IO吞吐量。基本原理为:当IO请求提交时,不知直接提交给底层驱动,而是先将其放入一个队列中(相当于水池),待一定时机或周期后再将该队列中的请求统一下发。将请求放入队列的过程即plug(蓄流)过程,统一下发请求的过程即为unplug(泄流)过程。每个请求在队列中等待的时间不会太长,通常在ms级别。
如此设计,可以增加IO合并和排序的机会,便于提升磁盘访问效率。

二、plug
1、基本流程
从mapping层提交到块设备层的io请求为bio,bio会在块设备进行合并,并生成新的request,并经过IO调度(排序和合并)之后下发到底层。下发request时,通过请求队列的make_request_fn接口,其中实质为将请求放入per task的plug队列,当队列满或在进行调度时(schedule函数中)会根据当前进程的状态将该队列中的请求flush到派发队列中,并触发unplug(具体流程后面介绍)。

per task的plug队列:新内核版本中实现的机制。IO请求提交时先链入此队列,当该队列满时(>BLK_MAX_REQUEST_COUNT),会flush到相应设备的请求队列中(request_queue)。
优点:per task维护plug队列,可以避免频繁对设备的请求队列操作导致的锁竞争,能提升效率。

2、plug基本代码流程如下:
submit_bio->
    generic_make_request->
        make_request->
            blk_queue_bio->
                list_add_tail(&req->queuelist, &plug->list);//将请求加入plug队列

3. plug 的时候并request_queue没有挂到到工作队列中,而是等待一段时间才挂入工作队列,才能够下发到设备

4. 工作队列就是从request_queue中取出一个req(可能一个req有多个bio),然后下发到硬盘,其实就是unplug


三、unplug
unplug分同步unplug和异步unplug两种方式。
同步unplug即当即通过调用blk_run_queue对下发请求队列中的情况。
异步unplug,通过唤醒kblockd工作队列来对请求队列中的请求进行下发。

1、kblockd工作队列的初始化:
1) 分配工作队列
主要代码流程:
blk_dev_init ->
    alloc_workqueue //分配工作队列
2) 初始化工作队列
blk_alloc_queue_node():

 

 

 

参考文献:

 Linux Kernel 3.10内核源码分析--块设备层request plug/unplug机制https://blog.csdn.net/omnispace/article/details/51038723

ext2读取并产生间接块和数据块https://blog.csdn.net/zouxiaoting/article/details/8943091

 Ext2的超级块对象https://blog.csdn.net/yunsongice/article/details/6171160

我所认识的EXT2(一)https://blog.csdn.net/wh8_2011/article/details/52209454

Linux文件系统详解https://www.cnblogs.com/bellkosmos/p/detail_of_linux_file_system.html

linux文件系统写过程简析https://www.cnblogs.com/linghuchong0605/p/4515542.html

address_space 结构https://blog.csdn.net/ryder001/article/details/7912408

Linux 内存中Page cache和buffer cache 的区别https://blog.csdn.net/haiross/article/details/39478959

Linux 文件系统(IBM DW)https://www.ibm.com/developerworks/cn/linux/theme/filesystem.html

块设备内核参数max_segments和max_sectors_kb解析http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=22954220&id=4813537

《Linux内核设计与实现》读书笔记(十四)- 块I/O层https://www.cnblogs.com/wang_yb/p/3299092.html

Buffer cache和page cache的区别https://www.linuxidc.com/Linux/2013-01/78140.htm

Linux中Buffer cache性能问题一探究竟https://www.linuxidc.com/Linux/2013-01/77573.htm

Linux驱动开发之块设备初入门https://www.linuxidc.com/Linux/2016-12/138153.htm

Linux块设备驱动(一)————块设备的结构及磁盘的结构https://blog.csdn.net/yangguoyu8023/article/details/70473823

linux内核之块设备二---真正派发请求request https://blog.csdn.net/revoer001/article/details/52003733

学写块设备驱动(三)----踢开IO调度器,自己处理bio(上)https://blog.csdn.net/magic_coder/article/details/7187226

Linux块设备驱动(四)————块设备的数据结构与相关操作及I/O调度器https://blog.csdn.net/yangguoyu8023/article/details/70478397

Linux块设备驱动(五)————通用块层https://blog.csdn.net/yangguoyu8023/article/details/70500229

 

 

 

 

 

 

阅读更多

没有更多推荐了,返回首页