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);

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
目录 第一章 Linux底层分段分机制 5 1.1 基于x86的Linux分段机制 5 1.2 基于x86的Linux机制 7 1.2.1 全局目录和表 8 1.2.2 线性地址到物理地址 10 1.2.3 线性地址字段处理 13 1.2.4 表处理 15 1.3 扩展分与联想存储器 20 1.4 Linux内存布局 21 1.5 内核空间和用户空间 23 1.5.1 初始化临时内核表 24 1.5.2 永久内核表的初始化 32 1.5.3 第一次进入用户空间 41 1.5.4 内核映射机制实例 44 1.6 固定映射的线性地址 48 1.7 高端内存内核映射 50 1.8.1 永久内存映射 50 1.8.2 临时内核映射 55 第二章 内核级内存管理系统 58 2.1 Linux面管理 58 2.1.1 NUMA架构 61 2.1.2 内存管理区 62 2.2 伙伴系统算法 65 2.2.1 数据结构 66 2.2.2 块分配 67 2.2.3 块释放 69 2.3 Linux面级内存管理 72 2.3.1 分配一组面 73 2.3.2 释放一组面 80 2.4 每CPU高速缓存 81 2.4.1 数据结构 81 2.4.2 通过每CPU 高速缓存分配面 82 2.4.3 释放面到每CPU 高速缓存 83 2.5 slab分配器 85 2.5.1 数据结构 86 2.5.2 分配/释放slab面 92 2.5.3 增加slab数据结构 93 2.5.4 高速缓存内存布局 94 2.5.5 slab着色 95 2.5.6 分配slab对象 96 2.5.7 释放Slab对象 100 2.5.8 通用对象 102 2.5.9 内存池 103 2.6 非连续内存区 104 2.6.1 高端内存区回顾 105 2.6.2 非连续内存区的描述符 106 2.6.3 分配非连续内存区 109 2.6.4 释放非连续内存区 113 第三章 进程的地址空间 117 3.1 用户态内存分配 117 3.1.1 mm_struct数据结构 118 3.1.2 内核线程的内存描述符 122 3.2 线性区的数据结构 123 3.2.1 线性区数据结构 123 3.2.2 红-黑树算法 126 3.2.3 线性区访问权限 128 3.3 线性区的底层处理 130 3.3.1 查找给定地址的最邻近区 131 3.3.2 查找一个与给定的地址区间相重叠的线性区 135 3.3.3 查找一个空闲的地址区间 135 3.3.4 向内存描述符链表中插入一个线性区 137 3.4 分配线性地址区间 141 3.5 释放线性地址区间 151 3.5.1 do_munmap()函数 151 3.5.2 split_vma()函数 153 3.5.3 unmap_region()函数 155 3.6 创建和删除进程的地址空间 156 3.6.1 创建进程的地址空间 156 3.6.2 删除进程的地址空间 175 3.6.3 内核线程1号的地址空间 176 3.7 堆的管理 178 第四章 磁盘文件内存映射 182 4.1 内存映射的数据结构 182 4.2 内存映射的创建 184 4.3 内存映射的请求调 194 4.4 刷新内存映射的脏 203 4.5 非线性内存映射 210 第五章 面的回收 215 5.1 框回收概念 215 5.1.1 选择目标 216 5.1.2 PFRA设计 217 5.2 反向映射技术 218 5.2.1 匿名的反向映射 220 5.2.2 优先搜索树 226 5.2.3 映射的反向映射 231 5.3 PFRA实现 235 5.3.1 最近最少使用(LRU)链表 236 5.3.2 内存紧缺回收 242 5.3.3 回收磁盘高速缓存 267 5.3.4 周期回收 273 5.3.5 内存不足删除程序 283 第六章 交换机制 289 6.1 交换区数据结构 289 6.1.1 创建交换区 290 6.1.2 交换区描述符 291 6.1.3 换出标识符 293 6.2 激活和禁用交换区 295 6.2.1 sys_swapon()系统调用 296 6.2.2 sys_swapoff()系统调用 304 6.2.3 try_to_unuse()函数 308 6.3 分配和释放槽 313 6.3.1 scan_swap_map()函数 313 6.3.2 get_swap_page()函数 316 6.3.3 swap_free()函数 318 6.4 面的换入换出 320 6.4.1 交换高速缓存 320 6.4.2 换出 323 6.4.3 换入 329 第七章 缺
第1章 硬件基础与软件基础 61.1 硬件基础 61.1.1 CPU 71.1.2 存储器 81.1.3 总线 81.1.4 控制器和外设 81.1.5 地址空间 91.1.6 时钟 91.2 软件基础 91.2.1 计算机语言 91.2.2 什么是操作系统 111.2.3 内核数据结构 13第2章 内存管理 152.1 虚拟内存抽象模型 152.1.1 请求调 172.1.2 交换 172.1.3 共享虚拟内存 182.1.4 物理寻址模式和虚拟寻址模式 182.1.5 访问控制 182.2 高速缓存 192.3 Linux表 202.4 分配和回收 212.4.1 分配 222.4.2 回收 222.5 内存映射 222.6 请求调 232.7 Linux缓存 242.8 换出和淘汰 252.8.1 减少缓冲区和缓存大小 252.8.2 换出System V共享内存 262.8.3 换出和淘汰 272.9 交换缓存 272.10 换入 28第3章 进程 293.1 Linux进程 293.2 标识符 313.3 调度 323.4 文件 343.5 虚拟内存 353.6 创建进程 363.7 时间和定时器 373.8 执行程序 383.8.1 ELF 393.8.2 脚本文件 40第4章 进程间通信机制 414.1 信号机制 414.2 管道 424.3 套接字 444.3.1 System V的进程间通信机制 444.3.2 消息队列 444.3.3 信号量 454.3.4 共享存储区 47第5章 PCI 495.1 PCI的地址空间 495.2 PCI配置头 505.3 PCI的I/O和存储地址空间 515.4 PCI-ISA桥 515.5 PCI-PCI 桥 515.5.1 PCI-PCI桥:PCI I/O和存储地址 空间的窗口 515.5.2 PCI-PCI桥:PCI配置周期和PCI 总线编号 525.6 Linux PCI初始化 535.6.1 Linux内核PCI数据结构 535.6.2 PCI设备驱动程序 535.6.3 PCI的BIOS函数 565.6.4 PCI修正过程 57第6章 中断处理与设备驱动程序 606.1 中断与中断处理 606.1.1 可编程中断控制器 616.1.2 初始化中断处理数据结构 616.1.3 中断处理 626.2 设备驱动程序 636.2.1 测试与中断 646.2.2 直接存储器访问(DMA) 656.2.3 存储器 666.2.4 设备驱动程序与内核的接口 666.2.5 硬盘 696.2.6 网络设备 74第7章 文件系统 777.1 第二个扩展文件系统EXT2 787.1.1 EXT2系统的inode节点 797.1.2 EXT2系统的超级块 807.1.3 EXT2系统的组描述符 807.1.4 EXT2系统的目录 817.1.5 在EXT2文件系统中查找文件 817.1.6 在EXT2文件系统中改变文件 的大小 827.2 虚拟文件系统 837.2.1 VFS文件系统的超级块 847.2.2 VFS文件系统的inode节点 847.2.3 注册文件系统 857.2.4 装配文件系统 857.2.5 在虚拟文件系统中查找文件 877.2.6 卸载文件系统 877.2.7 VFS文件系统的inode缓存 877.2.8 目录缓存 887.3 缓冲区缓存 887.3.1 bdflush内核守护进程 907.3.2 update进程 907.4 /proc文件系统 917.5 特殊设备文件 91第8章 网络 928.1 TCP/IP网络概述 928.2 Linux中的TCP/IP网络层次结构 958.3 BSD套接字接口 968.4 INET的套接字层 978.4.1 创建BSD套接字 988.4.2 为INET BSD Socket绑定地址 998.4.3 建立INET BSD Socket连接 998.4.4 INET BSD Socket侦听 1008.4.5 接受连接请求 1008.5 IP层 1008.5.1 套接字缓冲区 1008.5.2 接收IP报文 1018.5.3 发送IP报文 1028.5.4 数据分片 1028.6 地址解析协议 1038.7 IP路由 104第9章 内核机制与模块 1079.1 内核机制 1079.1.1 Bottom Half控制 1079.1.2 任务队列 1089.1.3 定时器 1099.1.4 等待队列 1109.1.5 自旋锁 1109.1.6 信号量 1109.2 模块 1119.2.1 模块载入 1129.2.2 模块卸载 113第10章 处理器 11510.1 X86 11510.2 ARM 11510.3 Alpha AXP处理器 115第11章 Linux内核源代码 11711.1 怎样得到Linux内核源码 11711.2 内核源码的编排 11711.3 从何处看起 118第12章 Linux数据结构 120附录A 有用的Web和FTP站点 138附录B 词汇表 139

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SweeNeil

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值