原文链接http://www.cnblogs.com/xuczhang/archive/2010/06/11/1756095.html
Disk Cache是一种将磁盘上的一些数据保留着RAM中的软件机制,这使得对这部分数据的访问可以得到更快的性能。
Disk Cache在Linux中有三种类型:1.Dentry cache 2. Page cache 3. Buffer cache
Dentry cache是目录项的cache,这里不对此展开。
Page Cache
Page cache是以Page为单位的cache,这里记录了一个物理内存中.
address_space
address_space是页高速缓存中最为重要的数据结构,它把页和页相应的host联系在了一起。
address_space包含5种类型的页:
1)普通文件和磁盘文件系统的目录
2)内存映射文件的数据
3)直接从块设备文件读取的数据(跳过文件系统层)
4)已经交换到磁盘上的用户态进程数据
5)IPC(进程间通信)的共享线性区
也就是说页高速缓存有上面5种类型。
比如当address_space指向磁盘文件的页时,它的host就指向该磁盘文件的inode。该磁盘文件对应的所有page就分别记录在clean_pages,dirty_pages和locked_pages这三个链表中。另外一个非常重要的成员就是a_ops,它定义了一系列属于当前页类型的操作函数。它会根据不同的页类型提供不同的操作函数。
页散列表
当进程读大文件时,那address_space对应的三个链表就会很长,那要找到相应的高速缓存页就需要遍历这几个链表,相当费时。linux的解决方法是建立一个页散列表page_hash_table,它的hash值是address_space的指针和偏移量。
page_hash_table的大小取决于RAM的容量。
当要查找一个文件的某个页是否在page cache中时,就可以通过inode->i_mapping得到address_space,然后在hash中查找是否已经有相应的高速缓存存在。
page cache处理函数
find_get_page(),add_to_page_cache(),find_or_create_page(),remove_inode_page()
缓冲区高速缓存
缓冲区是对块设备进行缓存的内存区域,不管读与写,对块设备的请求都要通过缓冲区进行。由下图可以看出读写块设备是通过发出请求来实现的。一次请求的块在设备上必须是连续的,不过对缓冲区来说就不必是连续的。ll_rw_block()函数就是用来产生块设备请求的。
不过其实这个函数就是创建一个请求描述符,然后把它插入到相应块设备的请求队列中去。然后通过各自块设备的设备驱动程序来完成实际的读写操作。读就是把设备的内容读到缓冲区,写就是把缓冲区的内容写到设备中。当然在这个过程中会通过一些策略进程来优化请求等等操作。所有的这些缓冲区都会放在一个hash表中,便于在使用时可以找到相应的缓冲区,以避免I/O操作。
知道了什么是缓冲区,缓冲区首部(buffer head)就是记录缓冲区信息的头,它是由slab分配的。
缓冲区页
缓冲区并不作为一个单独的内存单元来分配,而是存放在名为缓冲区页(buffer pages)中。一个严格的约束是缓冲区页中的所有缓冲区必须指向基本块设备的相邻块。
我们用两种方式来看待缓冲区页:
1)缓冲区页是一些缓冲区的容器,可以通过缓冲区高速缓存对这些缓冲区单独地访问。
2)另一方面,每一个缓冲区页包含块设备文件的4KB(一页的大小)的内容,因此缓冲区页就包含在页高速缓存中。换言之,缓冲区高速缓存所存放的RAM部分总是页高速缓存的一个子集。这样就极大地减少了缓冲区高速缓存和页高速缓存的同步问题。
下图是缓冲区首部和页之间的关系:
分配缓冲区页
当内核发现缓冲区高速冲没有包含给定块的数据时,就分配一个新的缓冲区页。内核调用grow_buffers()函数
getblk()
当内核需要读写物理设备上某个块的时候,内核必须检查所请求缓冲区的缓冲区首部是否已经包括在缓冲区高速缓存中。getblk函数会去找缓冲区的hash表,如果没有就调用grow_buffers()函数来申请一个新的缓冲区(包括缓冲区首部)。
把脏缓冲区写入磁盘
bdflush内核线程
kupdate内核线程