Linux 内核文件 Cache 机制
Linux内核文件Cache
机制
http://www.ilinuxkernel.com
本文链接: http://ilinuxkernel.com/?p=1700
1
Linux 内核文件 Cache 机制
目 录Table of Contents
1 概述 ........................................................................................................................... 5
2 Cache重要数据结构和函数 ........................................................................................ 7
2.1
address_space .................................................................................................... 7
address_space_operations................................................................................ 10
2.2
find_get_page() ............................................................................................. 11
2.3
2.4
add_to_page_cache() ................................................................................... 12
2.5
remove_from_page_cache() ......................................................................... 13
3 Linux内核预读机制 .................................................................................................. 14
3.1
file_ra_state数据结构 ......................................................................................... 15
do_generic_file_read() .................................................................................. 16
3.2
page_cache_sync_readahead() ................................................................... 17
3.3
3.4
page_cache_async_readahead() ................................................................. 18
ondemand_readahead() ............................................................................... 19
3.5
ra_submit() .................................................................................................... 22
3.6
3.7
__do_page_cache_readahead() ................................................................... 22
3.8
read_pages() ................................................................................................ 24
3.9 mpage_readpages() ..................................................................................... 25
4 文件读写I/O流程与Cache机制 ................................................................................. 26
4.1 读文件过程 ......................................................................................................... 26
4.1.1 数据不在页面Cache中 ................................................................................. 27
4.1.2 数据在页面Cache中 ..................................................................................... 28
4.2 写文件过程 ......................................................................................................... 29
2
Linux 内核文件 Cache 机制
图目录 List of Figures
图1 内核中块设备操作流程 ......................................................................................................... 6
3
Linux 内核文件 Cache 机制
表目录 List of Tables
表1 address_space对象成员变量含义 ......................................................................................... 8
表2 file_ra_state数据结构成员变量含义 ..................................................................................... 16
4
Linux 内核文件 Cache 机制
1 概述
在我们使用Linux时,使用free命令观察系统内存使用情况,如下面空间内存为
66053100k。可能很多人都遇到过一个问题,发现随着时间的推移,内存的free越来越小,
而cached越来越大;于是就以为是不是自己的程序存在内存泄漏,或者是硬件、操作系统
出了问题?显然,从这里看不出用户程序是否有内存泄漏,也不是内核有Bug或硬件有问题。
原因是内核的文件Cache机制。实际上文件Cache的实现是页面Cache,本文后续都以页面
Cache来描述。
[root@localhost ~]# free
total used free shared buffers cached
Mem: 66053100 1727572 64325528 0 242492 409440
-/+ buffers/cache: 1075640 64977460
Swap: 2097144 0 2097144
当应用程序需要读取文件中的数据时,操作系统先分配一些内存,将数据从存储设备读
入到这些内存中,然后再将数据分发给应用程序;当需要往文件中写数据时,操作系统先分
配内存接收用户数据,然后再将数据从内存写到磁盘上。文件Cache管理指的就是对这些由
操作系统内核分配,并用来存储文件数据的内存管理。
在大部分情况下,内核在读写磁盘时都先通过页面Cache。若页面不在Cache中,新页
加入到页面Cache中,并用从磁盘上读来的数据来填充页面。如果内存有足够的内存空间,
该页可以在页面Cache长时间驻留,其他进程再访问该部分数据时,不需要访问磁盘。这就
是free命令显示内核free值越来越小,cached值越来越大的原因。
同样,在把一页数据写到块设备之前,内核首先检查对应的页是否已经在页面Cache
中;如果不在,就在页面Cache增加一个新页面,并用要写到磁盘的数据来填充。数据的I/O
传输并不会立即开始执行,而是会延迟几秒左右;这样进程就有机会进一步修改写到磁盘的
数据。
内核的代码和数据结构不必从磁盘读,也不必写入磁盘。因此页面Cache可能是下面的
类型:
含有普通文件数据的页;
5
含有目录的页;
Linux 内核文件 Cache 机制
含有直接从块设备文件(跳过文件系统层)读出的数据页;
含有用户态进程数据的页,但页中的数据已被交换到磁盘;
属于特殊文件系统的页,如进程间通信中的特殊文件系统shm。
图1是块设备I/O操作流程图,从图中我们可以看出具体文件系统(如ext3/ext4、xfs等),
负责在文件Cache和存储设备之间交换数据,位于具体文件系统之上的虚拟文件系统VFS负
责在应用程序和文件 Cache 之间通过read()/write()等接口交换数据。
Virtual Filesystem (VFS) Layer
Buffer Cache (Page
Direct IO
Disk
Filesystem
Disk
Filesystem
Generic Block Layer
I/O Scheduler Layer
Request Queue
Request Queue
Block Device Driver
Block Device Driver
Kernel Space
Storage Media
HDD
… …
CD
图1 内核中块设备操作流程
页面Cache中的每页所包含的数据是属于某个文件,这个文件(准确地说是文件的
inode)就是该页的拥有者。事实上,所有的read()和write()都依赖于页面Cache;唯
6
Linux 内核文件 Cache 机制
一的例外是当进程打开文件时,使用了O_DIRECT标志,在这种情况下,页面Cache被跳过,
且使用了进程用户态地址空间的缓冲区。有些数据库应用程序使用O_DIRECT标志,这样
他们可以使用自己的磁盘缓冲算法。
内核页面Cache的实现主要为了满足下面两种需要:
快速定位含有给定所有者相关数据的特定页。为了尽可能发挥页面Cache的优势,
查找过程必须是快速的。
记录在读或写页中的数据时,应该如何处理页面Cache中的每个页。例如,从普通
文件、块设备文件或交换区读一个数据页,必须用不同的方式;这样内核必须根据
页面拥有者来选择正确的操作。
显然,页面Cache中的数据单位是整页数据。当然一个页面中的数据在磁盘上不必是相
邻的,这样页面就不能用设备号和块号来识别。取而代之的是,Cache中的页面识别是通过
拥有者和拥有者数据中的索引,通常是inode和相应文件内的偏移量。
文件Cache是文件数据在内存中的副本,因此文件Cache管理与内存管理系统和文件系
统都相关:一方面文件 Cache 作为物理内存的一部分,需要参与物理内存的分配回收过程,
另一方面文件Cache中的数据来源于存储设备上的文件,需要通过文件系统与存储设备进行
读写交互。从操作系统的角度考虑,文件Cache可以看做是内存管理系统与文件系统之间的
联系纽带。因此,文件 Cache管理是操作系统的一个重要组成部分,它的性能直接影响着
文件系统和内存管理系统的性能。
2 Cache重要数据结构和函数
2.1
address_space
页面Cache的核心数据结构是address_space,定义在include/linux/fs.h中。
address_space结构体嵌入在拥有该页面的inode对象中。在Cache中,可能有多个页面同
属于一个inode,这样他们就可能指向同一个address_space对象。同时,通过该对象将拥
有者的页面和在这些页面上的操作方法联系起来。address_space对象中成员变量含义如表
1所示。
00664: struct aaddddrreessss__ssppaaccee {
7
Linux 内核文件 Cache 机制
*host;
/* owner: inode, block_device */
/* radix tree of all pages */
tree_lock;
/* and lock protecting it */
i_mmap_lock; /* protect tree, count, list */
i_mmap_writable;/* count VM_SHARED mappings */
/* tree of private and shared mappings */
i_mmap_nonlinear;/*list VM_NONLINEAR mappings */
00665:
00666:
00667:
00668:
00669:
00670:
00671:
00672:
00673:
00674:
00675:
00676:
00677:
00678:
00679:
00680:
00681:
00682: } attribute
struct inode
struct radix_tree_root page_tree;
spinlock_t
unsigned int
struct prio_tree_root i_mmap;
struct list_head
spinlock_t
unsigned int
/* Protected by tree_lock together with the radix tree */
unsigned long
pgoff_t
const struct address_space_operations *a_ops; /* methods */
unsigned long
struct backing_dev_info *backing_dev_info; /* device readahead, etc */
spinlock_t
struct list_head private_list; /* ditto */
struct address_space *assoc_mapping; /* ditto */
private_lock;/* for use by the address_space */
writeback_index;/* writeback starts here */
truncate_count; /* Cover race condition with truncate */
nrpages; /* number of total pages */
flags;
/* error bits/gfp mask */
((aligned(sizeof(long))));
每个页面描述符(struct page)包含两个成员变量mapping和index,它们是用来链接
页面到页面Cache。mapping指向inode(拥有该页面)的address_space对象;index是拥
有者“地址空间”(这种情况下可以地址空间理解为磁盘上的文件)内的偏移量,单位大小
是页。当在页面Cache中查找某个页时,就是使用成员变量mapping和index。
表1 address_space对象成员变量含义
类型
struct inode *
struct
radix_tree_root
spinlock_t
unsigned int
struct
prio_tree_root
struct list_head
spinlock_t
unsigned int
unsigned long
unsigned long
struct
address_space_operations *
unsigned long
struct backing_dev_info *
字段
host
page_tree
含义
指向拥有该对象的节点
表示拥有者的页基树(radix tree)的根
保护基树的自旋锁
tree_lock
i_mmap_writable 地址空间中共享内存映射的个数
i_mmap
radix优先搜索树的根
i_mmap_nonlinear 地址空间中非线性内存区的链表
i_mmap_lock
保护radix优先搜索树的自旋锁
TRuncate_count 截断文件时使用的顺序计数器
nrpages
writeback_index 最后一次回写操作所作用的页索引
a_ops
对所有者页进行操作的方法
所有者的页总数
flags
backing_dev_info 指向拥有者的数据所在块设备的
错误位和内存分配标志
spinlock_t
struct list head
private_lock
private_list
backing_dev_info
通常是管理private_list链表时使用的自旋锁
通常是与索引节点相关的间接快的脏缓冲区
链表
struct address_space *
assoc_mapping 通常指向间接块所在块设备的
address_space对象
注意,在某些情况下,页面Cache可能包含磁盘上数据多份拷贝。因为一个普通文件的
8