我是靠谱客的博主 明理帅哥,最近开发中收集的这篇文章主要介绍Linux页高速缓存内核处理函数,查找页,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

关于Linux内核页高速缓存的介绍网络上有很多优秀的资源如下:

1、Linux 内核之页高速缓存与页回写

https://www.jianshu.com/p/d33ec2707e7f

2、页高速缓存和页回写

https://blog.csdn.net/yusiguyuan/article/details/12022811

3、《Linux内核设计与实现》读书笔记(十六)页高速缓存和页回写

https://www.cnblogs.com/wang_yb/archive/2013/11/21/3436126.html

4、计算机底层知识拾遗(六)理解页缓存page cache和地址空间address_space

https://blog.csdn.net/ITer_ZC/article/details/44195731

5、Linux内核——第十五章:页高速缓存

https://blog.csdn.net/duxingxia356/article/details/41982771

本文主要讲述内核的查找页相关的处理函数。基于 linux-4.4.4

一、address_space

页高速缓存的核心数据时address_space对象,它是一个嵌入在页所有者的索引节点对象中的数据结构。高速缓存中的许多页可能属于同一个所有者,从而可能被链接到同一个address_space对象。该对象还在所有者的页和对这些页的操作之间建立起链接关系。

每个页描述符都包括把页链接到页高速缓存的两个字段mapping和index。mapping字段指向拥有页的索引节点的address_space对象,index字段表示在所有者的地址空间中以页大小为单位的偏移量,也就是在所有者的磁盘映射中页中数据的位置。在页高速缓存中查找时使用这两个字段。

address_space对象定义在include/linux/fs.h中,其内容如下:

struct address_space {
	struct inode		*host;		/* owner: inode, block_device */
	struct radix_tree_root	page_tree;	/* radix tree of all pages */
	spinlock_t		tree_lock;	/* and lock protecting it */
	atomic_t		i_mmap_writable;/* count VM_SHARED mappings */
	struct rb_root		i_mmap;		/* tree of private and shared mappings */
	struct rw_semaphore	i_mmap_rwsem;	/* protect tree, count, list */
	/* Protected by tree_lock together with the radix tree */
	unsigned long		nrpages;	/* number of total pages */
	unsigned long		nrshadows;	/* number of shadow entries */
	pgoff_t			writeback_index;/* writeback starts here */
	const struct address_space_operations *a_ops;	/* methods */
	unsigned long		flags;		/* error bits/gfp mask */
	spinlock_t		private_lock;	/* for use by the address_space */
	struct list_head	private_list;	/* ditto */
	void			*private_data;	/* ditto */
} __attribute__((aligned(sizeof(long))));

如果页高速缓存中页的所有者是一个文件,address_space对象就嵌入在VFS索引节点对象的i_data字段中。索引节点的i_mapping字段总是指向索引节点的数据页所有者的address_space对象。address_space对象的host字段指向其所有者的索引节点对象。因此,如果页属于一个文件,那么页的所有者就是文件的索引节点,而且相应的address_space对象存放在VFS索引节点对象的i_data字段中。索引节点的i_mapping字段指向同一个索引节点的i_data字段,而address_space对象的host字段也指向这个索引节点。

address space对象的关键字段是a_ops,指向一个类型为address_space_operations的表,定义在include/linux/fs.h中,表中定义了对所有者的页进行处理的各种方法。

address_space_operations内容如下:

struct address_space_operations {
	int (*writepage)(struct page *page, struct writeback_control *wbc);
	int (*readpage)(struct file *, struct page *);

	/* Write back some dirty pages from this mapping. */
	int (*writepages)(struct address_space *, struct writeback_control *);

	/* Set a page dirty.  Return true if this dirtied it */
	int (*set_page_dirty)(struct page *page);

	int (*readpages)(struct file *filp, struct address_space *mapping,
			struct list_head *pages, unsigned nr_pages);

	int (*write_begin)(struct file *, struct address_space *mapping,
				loff_t pos, unsigned len, unsigned flags,
				struct page **pagep, void **fsdata);
	int (*write_end)(struct file *, struct address_space *mapping,
				loff_t pos, unsigned len, unsigned copied,
				struct page *page, void *fsdata);

	/* Unfortunately this kludge is needed for FIBMAP. Don't use it */
	sector_t (*bmap)(struct address_space *, sector_t);
	void (*invalidatepage) (struct page *, unsigned int, unsigned int);
	int (*releasepage) (struct page *, gfp_t);
	void (*freepage)(struct page *);
	ssize_t (*direct_IO)(struct kiocb *, struct iov_iter *iter, loff_t offset);
	/*
	 * migrate the contents of a page to the specified target. If
	 * migrate_mode is MIGRATE_ASYNC, it must not block.
	 */
	int (*migratepage) (struct address_space *,
			struct page *, struct page *, enum migrate_mode);
	int (*launder_page) (struct page *);
	int (*is_partially_uptodate) (struct page *, unsigned long,
					unsigned long);
	void (*is_dirty_writeback) (struct page *, bool *, bool *);
	int (*error_remove_page)(struct address_space *, struct page *);

	/* swapfile support */
	int (*swap_activate)(struct swap_info_struct *sis, struct file *file,
				sector_t *span);
	void (*swap_deactivate)(struct file *file);
};

这些方法指针指向那些为指定缓存对象实现的页I/O操作。每个后备存储都通过自己的address_space_operation描述自己如何与页高速缓存交互。比如ext4文件系统在文件fs/ext4/inode.c中定义了自己的操作表如下所示:

static const struct address_space_operations ext4_aops = {
	.readpage		= ext4_readpage,
	.readpages		= ext4_readpages,
	.writepage		= ext4_writepage,
	.writepages		= ext4_writepages,
	.write_begin		= ext4_write_begin,
	.write_end		= ext4_write_end,
	.bmap			= ext4_bmap,
	.invalidatepage		= ext4_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,
};

这些方法提供了管理页高速缓存的各种行为,包括最常用的读页到缓存、更新缓存数据等。

二、内核页高速缓存查找页处理函数

函数find_get_page()接收的参数为指向address_space对象的指针和偏移量。find_get_page()函数定义在include/linux/pagemap.h中,是一个内联函数,其内容如下:

static inline struct page *find_get_page(struct address_space *mapping,
					pgoff_t offset)
{
	return pagecache_get_page(mapping, offset, 0, 0);
}

实际上是调用了pagecache_get_page ()函数,进入到这个函数中查看究竟。pagecache_get_page()函数定义在mm/filemap.c中,其内容如下:

struct page *pagecache_get_page(struct address_space *mapping, pgoff_t offset,
	int fgp_flags, gfp_t gfp_mask)
{
	struct page *page;

repeat:
	page = find_get_entry(mapping, offset);
	if (radix_tree_exceptional_entry(page))
		page = NULL;
	if (!page)
		goto no_page;

	if (fgp_flags & FGP_LOCK) {
		if (fgp_flags & FGP_NOWAIT) {
			if (!trylock_page(page)) {
				page_cache_release(page);
				return NULL;
			}
		} else {
			lock_page(page);
		}

		/* Has the page been truncated? */
		if (unlikely(page->mapping != mapping)) {
			unlock_page(page);
			page_cache_release(page);
			goto repeat;
		}
		VM_BUG_ON_PAGE(page->index != offset, page);
	}

	if (page && (fgp_flags & FGP_ACCESSED))
		mark_page_accessed(page);

no_page:
	if (!page && (fgp_flags & FGP_CREAT)) {
		int err;
		if ((fgp_flags & FGP_WRITE) && mapping_cap_account_dirty(mapping))
			gfp_mask |= __GFP_WRITE;
		if (fgp_flags & FGP_NOFS)
			gfp_mask &= ~__GFP_FS;

		page = __page_cache_alloc(gfp_mask);
		if (!page)
			return NULL;

		if (WARN_ON_ONCE(!(fgp_flags & FGP_LOCK)))
			fgp_flags |= FGP_LOCK;

		/* Init accessed so avoid atomic mark_page_accessed later */
		if (fgp_flags & FGP_ACCESSED)
			__SetPageReferenced(page);

		err = add_to_page_cache_lru(page, mapping, offset,
				gfp_mask & GFP_RECLAIM_MASK);
		if (unlikely(err)) {
			page_cache_release(page);
			page = NULL;
			if (err == -EEXIST)
				goto repeat;
		}
	}

	return page;
}

在pagecache_get_page()函数中,首先调用了find_get_entry()函数返回了一个page,进入到find_get_entry()函数中,该函数同样定义在mm/filemap.c文件中,其内容如下:

struct page *find_get_entry(struct address_space *mapping, pgoff_t offset)
{
	void **pagep;
	struct page *page;

	rcu_read_lock();
repeat:
	page = NULL;
	pagep = radix_tree_lookup_slot(&mapping->page_tree, offset);
	if (pagep) {
		page = radix_tree_deref_slot(pagep);
		if (unlikely(!page))
			goto out;
		if (radix_tree_exception(page)) {
			if (radix_tree_deref_retry(page))
				goto repeat;
			/*
			 * A shadow entry of a recently evicted page,
			 * or a swap entry from shmem/tmpfs.  Return
			 * it without attempting to raise page count.
			 */
			goto out;
		}
		if (!page_cache_get_speculative(page))
			goto repeat;

		/*
		 * Has the page moved?
		 * This is part of the lockless pagecache protocol. See
		 * include/linux/pagemap.h for details.
		 */
		if (unlikely(page != *pagep)) {
			page_cache_release(page);
			goto repeat;
		}
	}
out:
	rcu_read_unlock();

	return page;
}

可以看到在这个里面进行了radix tree的相关搜索操作,如果搜索的页并没有在高速缓存中,find_get_entry返回一个NULL。

继续回到pagecache_get_page()函数中,可以看到下面继续执行了如下操作:

如果没有找到page:

……

//分配页

page = __page_cache_alloc(gfp_mask);

……

//将其加入到页面调整缓存

err = add_to_page_cache_lru(page, mapping, offset,

        gfp_mask & GFP_RECLAIM_MASK);

 

最后

以上就是明理帅哥为你收集整理的Linux页高速缓存内核处理函数,查找页的全部内容,希望文章能够帮你解决Linux页高速缓存内核处理函数,查找页所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(52)

评论列表共有 0 条评论

立即
投稿
返回
顶部