linux 4.4版本中的页高速缓存以及buffer cache
linux 4.4版本中的页高速缓存以及buffer cache
最近要接触相关领域,学习一下共勉!!本文档使用linux为linux-4.4.4版本
磁盘高速缓存:是一种软件机制,它允许系统把通常存放在磁盘上的一些数据保留在RAM中,以便对那些数据的进一步访问不用再访问磁盘而能尽快得到满足。
一种对完整的数据页进行操作的磁盘高速缓存。
几乎所有的文件读和写操作都依赖于页高速缓存。只有在O_DIRECT标志被置位而进程打开文件的情况下才会出现例外:此时,IO数据绕过了页高速缓存,而使用了进程用户态地址空间的缓冲区;少数数据库应用软件为了能采用自己的磁盘高速缓存算法而使用O_DIRECT标志。
使用页高速缓存目的:
快速定位含有给定所有者相关数据的特定页。为了尽可能充分发挥页高速缓存的优势,对它采用高速的搜索操作。
记录在读或写页中的数据时应当如何处理高速缓存中的每个页。例如:从普通文件、块设备文件或交换区读一个数据页必须用不同的实现方式。因此内核必须根据页的所有者选择适当的操作。
address_space 对象
页高速缓存的核心数据结构是address_space对象-----是一个嵌入在页所有者的索引节点对象中的数据结构。
每个页描述符都包括把页链接到页高速缓存的两个字段mapping和index。mapping字段指向拥有页的索引节点的address_space对象,index字段表示在所有者的地址空间中以页大小为单位的偏移量,也就是在所有者的磁盘映像中页中数据位置。在页高速缓存中查找页时使用这两个字段。
address_space 对象的字段
struct address_space {
struct inode*host;/* owner: inode, block_device 指向拥有该对象的索引节点的指针*/
struct radix_tree_rootpage_tree;/* radix tree of all pages 表示拥有者页的基数radix tree 的根*/
spinlock_ttree_lock;/* and lock protecting it 保护基树的自旋锁 */
atomic_ti_mmap_writable;/* count VM_SHARED mappings 地址空间中共享内存映射的个数*/
struct rb_rooti_mmap;/* tree of private and shared mappings radix优先搜索树的根 */
struct rw_semaphorei_mmap_rwsem;/* protect tree, count, list */
/* Protected by tree_lock together with the radix tree */
unsigned longnrpages;/* number of total pages */
unsigned longnrshadows;/* number of shadow entries */
pgoff_twriteback_index;/* writeback starts here */
const struct address_space_operations *a_ops;/* methods */
unsigned longflags;/* error bits/gfp mask */
spinlock_tprivate_lock;/* for use by the address_space */
struct list_headprivate_list;/* ditto */
void*private_data;/* ditto */
}
address_space对象就嵌入在VFS索引节点(inode)对象的i_data字段中。
索引节点(inode)的i_mapping字段总是指向索引节点的数据页所有者的address_space对象。
address_space对象的host字段指向其所有者的索引节点对象。
address_space 对象的方法
writepage 写操作,从页写到所有者的磁盘映像
readpage 读操作,从所有者的磁盘映像读到页
sync_page 如果对所有者页进行的操作已准备好,则立刻开始I/O数据的传输
writepages 把指定数量的所有者脏页写回磁盘。
set_page_dirty 把所有者的页设置为脏页
readpages 从磁盘中读所有者页的链表
prepare_write 为写操作做准备(由磁盘文件系统使用)
commit_write 完成写操作(由磁盘文件系统使用)
bmap 从文件块索引中获取逻辑块号
invalidatepage 是所有者的页无效(截断文件时使用)
releasepage 由日志文件系统使用以准备释放页
direct_IO 所有者页的直接I/O传输 (绕过了页高速缓存 page cache)
基树
address_space 对象的page_tree字段是基树(radix tree)的根。
内核可以通过此索引结构树快速判断所需要的页是否在页高速缓存中。当查找所需要的页时,内核把页索引转换为基树的路径,并快速找到页描述符所在位置。如果找到,内核可以从基树获得页描述符,并且确定所找到的页是否是脏页,以及其数据是否正在进行。
基树每个节点可以有多到64个指针指向其他节点或页描述符。底层结点指向页描述符的指针,而上层的节点存放指向其他节点的指针。每个节点由radix_tree_node数据结构表示,它包含三个字段: slots是包含64个指针的数组,count是记录节点中非空指针数量的计数器,tags是二维的标志数组。树根由radix_tree_root数据结构表示,他有三个字段:height表示树的当前深度(不包含叶子节点的层数),gfp_mask指定新节点请求内存时所用的标志,rnode指向与树中第一层节点相应的数据结构radix_tree_node(如果有的话).
上图 (a)中,高度为1,如果索引中没有索引大于63的,那么树的深度即为1。
(b)中,高度为2,索引131 在,127 + 4 中!!!!
资料显示在32位体系结构的基树中,最大深度为 6 。
如果基树的最大索引小于应该增加的页的索引,那么内核相应地增加树的深度;基树的中间节点依赖于页索引的值。
页高速缓存的处理函数
查找页
find_get_page()
find_get_pages()与find_get_page()类似,实现查找一组具有相邻索引的页。不过它依赖于radix_tree_gang_lookup()查找。
find_get_page()
|
radix_tree_lookup
如果找到所需要的页,就增加该页的计数器。否则返回NULL
增加页
add_to_page_cache(); 把新的页描述符插入到页高速缓存中。
删除页
remove_from_page_cache()
更新页
read_cache_page() 确保高速缓存中包含最新版本的指定页。
基树的标记
页高速缓存允许内核快速获得含有块设备中指定数据的页。
允许内核从高速缓存中快速获得给定状态的页。
基树中的每个中间节点都包含一个针对每个孩子节点(或叶子节点)的脏标记PG_dirty。目的:当内核遍历基树搜索脏页时,就可以跳过脏标记为0的中间节点的所有子树:中间结点的脏标记为0说明其子树中的所有页描述符都不是脏的。 同样的想法应用到PG_writeback,该标志表表示页正在被写回磁盘。设置这两个标记时调用函数radix_tree_tag_set().清除标志时调用函数radix_tree_tag_clear()。
从基树删除页描述符时,必须更新从根结点到叶子结点的路径中结点的相应标记。radix_tree_delete()函数来完成。但是radix_tree_insert()函数不更新标记(上面的两个),如果需要调用radix_tree_tag_set()。
函数find_get_pages_tag() 和find_get_pages()类似,前者返回的是只是那些用tag参数标记的页。
把块放在页高速缓存中
在linux内核旧版本中,主要有两种不同的磁盘高速缓存:页高速缓存和缓冲区高速缓存,前者用来存放磁盘文件内容时生成的磁盘数据页,后者把VFS访问的块的内容保留在内存中。当时2.4.10后缓冲区高速缓存不再存在。也就是我们说的buffer cache不再存在,而是统一都存放进了page cache中。-----把它们存放在叫做“缓冲区页”的专门页中。
缓冲区 页在形式就是与称作“缓冲区首部”的附加描述符相关的数据页,主要目的是快速确定页中的一个块在磁盘中的地址。
缓冲区首部的字段:
unsigned long b_state 缓冲区状态标志
struct buffer_head * b_this_page 指向缓冲区页的链表中的下一个元素的指针
struct page * b_ page 指向拥有该块的缓冲区页的表舒服指针。
atomic_t b_count 块使用计数器
u32 b_size 块大小
sector_t b_blocknr 与块设备相关的块号(逻辑块号)
char * b_data 快在缓冲区页内的位置
struct block_device * b_bdev 指向块设备描述符的指针
bh_end_io_t * b_end_io I/O完成方法
void * b_private 指向I/O完成方法数据的指针
struct list_head b_assoc_buffers 为与某个索引节点相关的间接块的链表提供的指针。
两个字段编码表示块的磁盘地址:b_bdev字段表示包含块的块设备,通常是磁盘或分区;而b_blocknr字段存放逻辑块号,即块在磁盘或分区中的编号。
b_data表示块缓冲区子缓冲区页中的位置,如果再内存,存放的是块缓冲区相对页的起始位置的偏移量,否则存放块缓冲区的线性地址。
b_state 存放标志。
分配块缓冲区页
当内核发现指定块的缓冲区所在的页不在页高速缓存中时,就分配一个块设备缓冲区页。特别是情况如下:
包含数据块的页不在块设备的基树中。
包含数据块的页在块设备的基树中,但这个页不是缓冲区页。
包含数据块的缓冲区页在块设备的基树中,但页中块的大小与所请求的块大小不相同。
end
暂且入门分析到这里,等到有深刻理解时再更新!
linux 4.4版本中的页高速缓存以及buffer cache相关教程