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():
参考文献:
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
《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