前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅谈Linux内核中页缓存和块缓存

浅谈Linux内核中页缓存和块缓存

作者头像
用户4700054
发布2022-08-17 13:11:05
2.7K0
发布2022-08-17 13:11:05
举报

概述

  • 运行在用户态的应用程序需要经常访问磁盘数据,进行读写操作,由于磁盘(HDD)相对较慢,没有任何缓存的情况下,每次应用读写操作时延页非常慢;在内核设计之初,添加了缓存设计,将磁盘数据保存在RAM中,后续的读写操作转换为在RAM中的操作,从而加快应用读写操作的速度。
  • 页高速缓存(Page Cache)的用途是加速访问文件数据,给定inode索引节点和文件的页面的偏移量,快速的在内存中找到文件页的内容。这个Page Cache是存在于VFS和实际文件系统之间。如果应用指定了O_DIRECT方式访问文件,则直接绕开Page Cache直接访问块设备层。Page Cache中缓存的Page大小为4K。Page Cache高速缓存使用的是物理页帧,以页为单位将文件内容缓存,逻辑文件(struct file)中每一个页可以划分为块单位,将每个块映射到磁盘的盘块,因此一个文件的页可以和多个Buffer Cache中块缓存关联,每个块缓存和磁盘的盘块进行关联。
  • 块缓存中缓存的单个块大小是以磁盘扇区大小,默认是512个字节。无论应用程序读取多少个字节,在最终访问磁盘的时候,都必须以扇区大小(512个字节)读取;对应的块缓存中缓存块大小页是扇区的大小。

Page Cache(页缓存)

  • Linux页高速缓存任何基于页的数据,所缓存的Page包括普通文件内容、块设备文件、内存映射文件的读写。页缓存中一个页帧的文件数据锁对应的磁盘块不必是连续的。如果是普通文件内容它们只是逻辑上连续的磁盘盘块,这些磁盘块在磁盘上可以是不连续的。针对块设备文件的页缓存则是磁盘盘块在物理磁盘上是连续的。
  • 页缓存中采用了struct address_space数据结构来管理。它特指一个文件内容所形成的的页缓存空间。struct address_space不仅仅管理某个文件已经读入的页帧内容,同时也管理这些页帧到进程空间的文件映射关系(多个进程打开同一个文件下,多个进程读取不同位置的数据)。如果一个struct address_space和一个文件对应,所有进程访问的页缓存通过一个struct address_space进行管理。如果文件类型是普通文件,struct address_space和文件inode中的i_data进行关联,同时inode中的i_mapping指向i_data这个成员。如果文件类型是块设备文件,struct address_space嵌入到块设备中文件的主索引节点,struct block_device中的db_inode指向块设备这个inode.struct address_space`结构如下:
代码语言:javascript
复制
struct address_space {
	// 对应物理文件的inide
	struct inode		*host;		
	// 缓存的radix的root节点
	struct radix_tree_root	i_pages;	
	atomic_t		i_mmap_writable;
	// i_mmap是红黑树,管理页帧相关的VMA的映射关系
	struct rb_root_cached	i_mmap;		
	struct rw_semaphore	i_mmap_rwsem;	
	// page的个数
	unsigned long		nrpages;	

	unsigned long		nrexceptional;
	pgoff_t			writeback_index;
	// address_space的操作操作函数,每个磁盘文件系统都会有自己的操作函数
	const struct address_space_operations *a_ops;	
	unsigned long		flags;		
	spinlock_t		private_lock;	
	gfp_t			gfp_mask;	
	struct list_head	private_list;	
	void			*private_data;	
	errseq_t		wb_err;
} __attribute__((aligned(sizeof(long)))) __randomize_layout;


// 以ext4磁盘文件系统操作函数
static const struct address_space_operations ext4_da_aops = {
	.readpage		= ext4_readpage,
	.readpages		= ext4_readpages,
	.writepage		= ext4_writepage,
	.writepages		= ext4_writepages,
	.write_begin		= ext4_da_write_begin,
	.write_end		= ext4_da_write_end,
	.set_page_dirty		= ext4_set_page_dirty,
	.bmap			= ext4_bmap,
	.invalidatepage		= ext4_da_invalidatepage,
	.releasepage		= ext4_releasepage,
	.direct_IO		= ext4_direct_IO,
	.migratepage		= buffer_migrate_page,
	.is_partially_uptodate  = block_is_partially_uptodate,
	.error_remove_page	= generic_error_remove_page,
};

Buffer Cache(块缓存)

  • 块缓存和页缓存是相对独立的两种缓存机制,通常也可以结合在一起共同描述页缓存中保存文件的数据,向上以页为单位于页缓存交互,向下以块缓存为单位和通用设备层进行交互。在内核中块缓存是通过struct buffer_head进行管理的。
代码语言:javascript
复制
struct buffer_head {
	// 块缓存的标记
	unsigned long b_state;	
	// 同一个页缓存的块缓存构成的环状链表
	struct buffer_head *b_this_page;
	// 所属的页缓存
	struct page *b_page;	
	// 块缓存个数
	sector_t b_blocknr;		
	size_t b_size;			/* size of mapping */
	// 块缓存的起点
	char *b_data;			
	// 所属的块设备
	struct block_device *b_bdev;
	bh_end_io_t *b_end_io;		/* I/O completion */
 	void *b_private;		/* reserved for b_end_io */
	struct list_head b_assoc_buffers; /* associated with another mapping */
	// 所属的地址空间
	struct address_space *b_assoc_map;	
	atomic_t b_count;		/* users using this buffer_head */
};
  • 内核中按照块访问的场景不多,主要是针对超级块和索引节点等磁盘数据管理操作时候才会用到。例如sb_readsb_getblk根据传入的盘号将盘块读入到块缓存中。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-04-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 存储内核技术交流 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • Page Cache(页缓存)
  • Buffer Cache(块缓存)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档