﻿<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>P.Linux Laboratory &#187; Cache</title>
	<atom:link href="http://www.penglixun.com/tag/cache/feed" rel="self" type="application/rss+xml" />
	<link>http://www.penglixun.com</link>
	<description>MySQL DBA &#38; Linux SA</description>
	<lastBuildDate>Sun, 22 Jan 2012 16:34:39 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Linux Cache 机制探究</title>
		<link>http://www.penglixun.com/tech/system/linux_cache_discovery.html</link>
		<comments>http://www.penglixun.com/tech/system/linux_cache_discovery.html#comments</comments>
		<pubDate>Fri, 27 Aug 2010 04:56:31 +0000</pubDate>
		<dc:creator>P.Linux</dc:creator>
				<category><![CDATA[操作系统]]></category>
		<category><![CDATA[Cache]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Radix Tree]]></category>

		<guid isPermaLink="false">http://www.penglixun.com/?p=1160</guid>
		<description><![CDATA[本文内容遵从CC版权协议, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明网址: http://www.penglixun.com/tech/system/linux_cache_discovery.html 经过研究了下Linux相关代码，把对Lin... ]]></description>
			<content:encoded><![CDATA[<p><span style="color: #888888;">本文内容遵从<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank">CC版权协议</a>, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明</br>网址: http://www.penglixun.com/tech/system/linux_cache_discovery.html </p>
<p></span>经过研究了下Linux相关代码，把对Linux Cache实现的方式做一些总结。<br />
相关源码主要在：<br />
./fs/fscache/cache.c    Cache实现的代码<br />
./mm/slab.c                                     SLAB管理器代码<br />
./mm/swap.c                                   缓存替换算法代码<br />
./mm/mmap.c             内存管理器代码<br />
./mm/mempool.c       内存池实现代码</p>
<h2>0. 预备：Linux内存管理基础</h2>
<p>创建进程fork()、程序载入execve()、映射文件mmap()、动态内存分配malloc()/brk()等进程相关操作都需要分配内存给进程。不过这时进程申请和获得的还不是实际内存，而是虚拟内存，准确的说是“内存区域”。Linux除了内核以外，App都不能直接使用内存，因为Linux采用Memory Map的管理方式，App拿到的全部是内核映射自物理内存的一块虚拟内存。malloc分配很少会失败，因为malloc只是通知内存App需要内存，在没有正式使用之前，这段内存其实只在真正开始使用的时候才分配，所以malloc成功了并不代表使用的时候就真的可以拿到这么多内存。据说Google的tcmalloc改进了这一点。</p>
<p>进程对内存区域的分配最终多会归结到do_mmap()函数上来（brk调用被单独以系统调用实现，不用do_mmap()）。内核使用do_mmap()函数创建一个新的线性地址区间，如果创建的地址区间和一个已经存在的地址区间相邻，并且它们具有相同的访问权限的话，那么两个区间将合并为一个。如果不能合并，那么就确实需要创建一个新的VMA了。但无论哪种情况， do_mmap()函数都会将一个地址区间加入到进程的地址空间中，无论是扩展已存在的内存区域还是创建一个新的区域。同样释放一个内存区域使用函数do_ummap()，它会销毁对应的内存区域。</p>
<p>另一个重要的部分是SLAB分配器。在Linux中以页为最小单位分配内存对于内核管理系统物理内存来说是比较方便的，但内核自身最常使用的内存却往往是很小（远远小于一页）的内存块，因为大都是一些描述符。一个整页中可以聚集多个这种这些小块内存，如果一样按页分配，那么会被频繁的创建/销毁，开始是非常大的。</p>
<p>为了满足内核对这种小内存块的需要，Linux系统采用了SLAB分配器。Slab分配器的实现相当复杂，但原理不难，其核心思想就是Memory Pool。内存片段（小块内存）被看作对象，当被使用完后，并不直接释放而是被缓存到Memory Pool里，留做下次使用，这就避免了频繁创建与销毁对象所带来的额外负载。</p>
<p>Slab技术不但避免了内存内部分片带来的不便，而且可以很好利用硬件缓存提高访问速度。但Slab仍然是建立在页面基础之上，Slab将页面分成众多小内存块以供分配，Slab中的对象分配和销毁使用kmem_cache_alloc与kmem_cache_free。</p>
<p>关于SALB分配器有一份资料：<a href="http://lsec.cc.ac.cn/~tengfei/doc/ldd3/ch08s02.html">http://lsec.cc.ac.cn/~tengfei/doc/ldd3/ch08s02.html</a><br />
关于内存管理的两份资料：<a href="http://lsec.cc.ac.cn/~tengfei/doc/ldd3/ch15.html">http://lsec.cc.ac.cn/~tengfei/doc/ldd3/ch15.html</a><br />
<a href="http://memorymyann.javaeye.com/blog/193061">http://memorymyann.javaeye.com/blog/193061</a></p>
<h2>1. Linux Cache的体系</h2>
<p>在 Linux 中，当App需要读取Disk文件中的数据时，Linux先分配一些内存，将数据从Disk读入到这些内存中，然后再将数据传给App。当需要往文件中写数据时，Linux先分配内存接收用户数据，然后再将数据从内存写到Disk上。Linux Cache  管理指的就是对这些由Linux分配，并用来存储文件数据的内存的管理。</p>
<p>下图描述了 Linux 中文件 Cache 管理与内存管理以及文件系统的关系。从图中可以看到，在 Linux 中，具体的文件系统，如  ext2/ext3/ext4 等，负责在文件  Cache和存储设备之间交换数据，位于具体文件系统之上的虚拟文件系统VFS负责在应用程序和文件 Cache 之间通过 read/write  等接口交换数据，而内存管理系统负责文件 Cache 的分配和回收，同时虚拟内存管理系统(VMM)则允许应用程序和文件 Cache 之间通过  memory map的方式交换数据，FS Cache底层通过SLAB管理器来管理内存。</p>
<p style="text-align: left;"><a title="Linux Cache体系 1" href="http://www.yupoo.com/photos/plinux/77106238/"><img class="aligncenter" src="http://pic.yupoo.com/plinux/AqzqMw23/eSwSS.jpg" border="0" alt="Linux Cache体系 1" width="567" height="484" /></a><br />
下图则非常清晰的描述了Cache所在的位置，磁盘与VFS之间的纽带。</p>
<p style="text-align: center;"><a title="Linux Cache体系 2" href="http://www.yupoo.com/photos/plinux/77106302/"><img class="aligncenter" src="http://pic.yupoo.com/plinux/AqzrM4oI/JfuYd.jpg" border="0" alt="Linux Cache体系 2" width="313" height="432" /></a></p>
<h2 style="text-align: left;">2. Linux Cache的结构</h2>
<p style="text-align: left;">在 Linux 中，文件 Cache 分为两层，一是 Page Cache，另一个 Buffer Cache，每一个 Page  Cache 包含若干 Buffer Cache。内存管理系统和 VFS 只与 Page Cache 交互，内存管理系统负责维护每项 Page  Cache 的分配和回收，同时在使用 memory map 方式访问时负责建立映射；VFS 负责 Page Cache  与用户空间的数据交换。而具体文件系统则一般只与 Buffer Cache 交互，它们负责在外围存储设备和 Buffer Cache  之间交换数据。读缓存以Page Cache为单位，每次读取若干个Page Cache，回写磁盘以Buffer Cache为单位，每次回写若干个Buffer Cache。<br />
Page Cache、Buffer Cache、文件以及磁盘之间的关系如下图所示。</p>
<p style="text-align: left;">
<p style="text-align: center;"><a title="Linux Cache实现" href="http://www.yupoo.com/photos/plinux/77106240/"><img class="aligncenter" src="http://pic.yupoo.com/plinux/AqzqMUfN/K05Tc.jpg" border="0" alt="Linux Cache实现" width="389" height="268" /></a></p>
<p style="text-align: left;">Page 结构和 buffer_head  数据结构的关系如下图所示。Page指向一组Buffer的头指针，Buffer的头指针指向磁盘块。在这两个图中，假定了 Page 的大小是 4K，磁盘块的大小是 1K。</p>
<p style="text-align: center;"><a title="Page Cache结构" href="http://www.yupoo.com/photos/plinux/77106241/"><img class="aligncenter" src="http://pic.yupoo.com/plinux/AqzqMRAZ/4YDuW.jpg" border="0" alt="Page Cache结构" width="373" height="258" /></a></p>
<p>在 Linux 内核中，文件的每个数据块最多只能对应一个 Page Cache  项，它通过两个数据结构来管理这些 Cache 项，一个是 Radix Tree，另一个是双向链表。Radix Tree 是一种搜索树，Linux  内核利用这个数据结构来通过文件内偏移快速定位 Cache 项，图 4 是 radix tree的一个示意图，该 radix tree  的分叉为4(22)，树高为4，用来快速定位8位文件内偏移。Linux(2.6.7) 内核中的分叉为 64(26)，树高为 6(64位系统)或者  11(32位系统)，用来快速定位 32 位或者 64 位偏移，Radix tree 中的每一个到叶子节点的路径上的Key所拼接起来的字串都是一个地址，指向文件内相应偏移所对应的Cache项。</p>
<p style="text-align: center;"><a title="Page Cache使用的Radix Tree 1" href="http://www.yupoo.com/photos/plinux/77106242/"><img src="http://pic.yupoo.com/plinux/AqzqMTB3/tnoPA.gif" border="0" alt="Page Cache使用的Radix Tree 1" width="521" height="362" /></a></p>
<p>查看Page Cache的核心数据结构struct address_space就可以看到上述结构（略去了无关结构）：</p>

<div class="wp_codebox"><table><tr id="p11604"><td class="code" id="p1160code4"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">struct</span> address_space  <span style="color: #008000;">&#123;</span>
<span style="color: #0000ff;">struct</span> inode             <span style="color: #000040;">*</span>host<span style="color: #008080;">;</span>              <span style="color: #ff0000; font-style: italic;">/* owner: inode, block_device */</span>
<span style="color: #0000ff;">struct</span> radix_tree_root      page_tree<span style="color: #008080;">;</span>         <span style="color: #ff0000; font-style: italic;">/* radix tree of all pages */</span>
<span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">long</span>           nrpages<span style="color: #008080;">;</span>  <span style="color: #ff0000; font-style: italic;">/* number of total pages */</span>
<span style="color: #0000ff;">struct</span> address_space       <span style="color: #000040;">*</span>assoc_mapping<span style="color: #008080;">;</span>      <span style="color: #ff0000; font-style: italic;">/* ditto */</span>
......
<span style="color: #008000;">&#125;</span> __attribute__<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#40;</span>aligned<span style="color: #008000;">&#40;</span><span style="color: #0000dd;">sizeof</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">long</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></pre></td></tr></table></div>

<p style="text-align: left;">下面是一个Radix Tree实例：</p>
<p style="text-align: center;"><a title="Page Cache使用的Radix Tree 2" href="http://www.yupoo.com/photos/plinux/77106308/"><img class="aligncenter" src="http://pic.yupoo.com/plinux/AqzslYCb/3D95i.jpg" border="0" alt="Page Cache使用的Radix Tree 2" width="442" height="423" /></a></p>
<p>另一个数据结构是双向链表，Linux内核为每一片物理内存区域(zone)  维护active_list和inactive_list两个双向链表，这两个list主要用来实现物理内存的回收。这两个链表上除了文件Cache之 外，还包括其它匿名(Anonymous)内存，如进程堆栈等。</p>
<p style="text-align: center;"><a title="Linux Cache 置换算法" href="http://www.yupoo.com/photos/plinux/77107061/"><img class="aligncenter" src="http://pic.yupoo.com/plinux/AqzJSDWK/cc9PP.png" border="0" alt="Linux Cache 置换算法" width="547" height="243" /></a></p>
<p style="text-align: left;">相关数据结构如下：</p>

<div class="wp_codebox"><table><tr id="p11605"><td class="code" id="p1160code5"><pre class="cpp" style="font-family:monospace;">truct page<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">struct</span> list_head list<span style="color: #008080;">;</span>   <span style="color: #666666;">//通过使用它进入下面的数据结构free_area_struct结构中的双向链队列</span>
    <span style="color: #0000ff;">struct</span> address_space <span style="color: #000040;">*</span> mapping<span style="color: #008080;">;</span>   <span style="color: #666666;">//用于内存交换的数据结构</span>
    <span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">long</span> index<span style="color: #008080;">;</span><span style="color: #666666;">//当页面进入交换文件后</span>
    <span style="color: #0000ff;">struct</span> page <span style="color: #000040;">*</span>next_hash<span style="color: #008080;">;</span> <span style="color: #666666;">//自身的指针，这样就可以链接成一个链表</span>
    atomic t count<span style="color: #008080;">;</span> <span style="color: #666666;">//用于页面交换的计数,若页面为空闲则为0，分配就赋值1，没建立或恢复一次映射就加1，断开映射就减一</span>
    <span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">long</span> flags<span style="color: #008080;">;</span><span style="color: #666666;">//反应页面各种状态，例如活跃，不活跃脏，不活跃干净，空闲</span>
   <span style="color: #0000ff;">struct</span> list_head lru<span style="color: #008080;">;</span>
   <span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">long</span> age<span style="color: #008080;">;</span> <span style="color: #666666;">//表示页面寿命</span>
   wait_queue_head_t wait<span style="color: #008080;">;</span>
   <span style="color: #0000ff;">struct</span> page <span style="color: #000040;">**</span> pprev_hash<span style="color: #008080;">;</span>
   <span style="color: #0000ff;">struct</span> buffer_head <span style="color: #000040;">*</span> buffers<span style="color: #008080;">;</span>
   <span style="color: #0000ff;">void</span> <span style="color: #000040;">*</span> <span style="color: #0000ff;">virtual</span>
   <span style="color: #0000ff;">struct</span> zone_struct <span style="color: #000040;">*</span> zone<span style="color: #008080;">;</span> <span style="color: #666666;">//指向所属的管理区</span>
<span style="color: #008000;">&#125;</span>
<span style="color: #0000ff;">typedef</span> <span style="color: #0000ff;">struct</span> free_area_struct <span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">struct</span> list_head free_list<span style="color: #008080;">;</span>   <span style="color: #666666;">//linux 中通用的双向链队列</span>
    <span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">int</span> <span style="color: #000040;">*</span> map<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span> free_area_t<span style="color: #008080;">;</span>
<span style="color: #0000ff;">typedef</span> <span style="color: #0000ff;">struct</span> zone_struct<span style="color: #008000;">&#123;</span>
    spinlock_t        lock<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">long</span> offset<span style="color: #008080;">;</span>  <span style="color: #666666;">//表示该管理区在mem-map数组中，起始的页号</span>
    <span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">long</span> <span style="color: #0000dd;">free</span> pages<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">long</span> inactive_clean_pages<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">long</span> inactive_dirty_pages<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">unsigned</span> pages_min, pages_low, pages_high<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">struct</span> list_head inactive_clean_list<span style="color: #008080;">;</span>   <span style="color: #666666;">//用于页面交换的队列，基于linux页面交换的机制。这里存贮的是不活动“干净”页面</span>
    free_area_t free_area<span style="color: #008000;">&#91;</span>MAX_ORDER<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span> <span style="color: #666666;">//一组“空闲区间”队列，free_area_t定义在上面，其中空闲下标表示的是页面大小，例如：数组第一个元素0号，表示所有区间大小为2的 0次方的页面链接成的双向队列，1号表示所有2的1次方页面链接链接成的双向队列，2号表示所有2的2次方页面链接成的队列，其中要求是这些页面地址连续</span>
    <span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span> name<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">long</span> size<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">struct</span> pglist_data <span style="color: #000040;">*</span> zone_pgdat<span style="color: #008080;">;</span>   <span style="color: #666666;">//用于指向它所属的存贮节点，及下面的数据结构</span>
    <span style="color: #0000ff;">unsigned</span>  <span style="color: #0000ff;">long</span>  zone_start_paddr<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">unsigned</span>  <span style="color: #0000ff;">long</span>    zone_start_mapnr<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">struct</span> page <span style="color: #000040;">*</span> zone_mem_map<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span> zone_t<span style="color: #008080;">;</span></pre></td></tr></table></div>

<h2 style="text-align: left;">3. Cache预读与换出</h2>
<p style="text-align: left;">Linux  内核中文件预读算法的具体过程是这样的：<br />
对于每个文件的第一个读请求，系统读入所请求的页面并读入紧随其后的少数几个页面(不少于一个页面，通常是三个页 面)，这时的预读称为同步预读。对于第二次读请求，如果所读页面不在Cache中，即不在前次预读的group中，则表明文件访问不是顺序访问，系统继续 采用同步预读；如果所读页面在Cache中，则表明前次预读命中，操作系统把预读group扩大一倍，并让底层文件系统读入group中剩下尚不在  Cache中的文件数据块，这时的预读称为异步预读。无论第二次读请求是否命中，系统都要更新当前预读group的大小。<br />
此外，系统中定义了一个  window，它包括前一次预读的group和本次预读的group。任何接下来的读请求都会处于两种情况之一：<br />
第一种情况是所请求的页面处于预读  window中，这时继续进行异步预读并更新相应的window和group；<br />
第二种情况是所请求的页面处于预读window之外，这时系统就要进行同步 预读并重置相应的window和group。<br />
下图是Linux内核预读机制的一个示意图，其中a是某次读操作之前的情况，b是读操作所请求页面不在  window中的情况，而c是读操作所请求页面在window中的情况。</p>
<p style="text-align: center;"><a title="Cache预读算法" href="http://www.yupoo.com/photos/plinux/77106243/"><img class="aligncenter" src="http://pic.yupoo.com/plinux/AqzqN7jG/EEuVU.gif" border="0" alt="Cache预读算法" width="494" height="298" /></a></p>
<p>Linux内核中文件Cache替换的具体过程是这样的：刚刚分配的Cache项链入到inactive_list头部，并将其状态设置为active，当内存不够需要回收Cache时，系统首先从尾部开始反向扫描  active_list并将状态不是referenced的项链入到inactive_list的头部，然后系统反向扫描inactive_list，如果所扫描的项的处于合适的状态就回收该项，直到回收了足够数目的Cache项。其中Active_list的含义是热访问数据，及多次被访问的，inactive_list是冷访问数据，表示尚未被访问的。如果数据被访问了，Page会被打上一个Refrence标记，如果Page没有被访问过，则打上Unrefrence标记。这些处理在swap.c中可以找到。<br />
下图也描述了这个过程。</p>
<p style="text-align: center;"><a title="Linux Cache 置换算法" href="http://www.yupoo.com/photos/plinux/77107061/"><img class="aligncenter" src="http://pic.yupoo.com/plinux/AqzJSDWK/cc9PP.png" border="0" alt="Linux Cache 置换算法" width="547" height="243" /></a></p>
<p>下面的代码描述了一个Page被访问它的标记为变化：</p>

<div class="wp_codebox"><table><tr id="p11606"><td class="code" id="p1160code6"><pre class="cpp" style="font-family:monospace;"><span style="color: #000040;">*</span>
 <span style="color: #000040;">*</span> Mark a page as having seen activity.
 <span style="color: #000040;">*</span>
 <span style="color: #000040;">*</span> inactive,unreferenced        <span style="color: #000040;">-</span><span style="color: #000040;">&amp;</span>gt<span style="color: #008080;">;</span>      inactive,referenced
 <span style="color: #000040;">*</span> inactive,referenced          <span style="color: #000040;">-</span><span style="color: #000040;">&amp;</span>gt<span style="color: #008080;">;</span>      active,unreferenced
 <span style="color: #000040;">*</span> active,unreferenced          <span style="color: #000040;">-</span><span style="color: #000040;">&amp;</span>gt<span style="color: #008080;">;</span>      active,referenced
 <span style="color: #000040;">*/</span>
<span style="color: #0000ff;">void</span> mark_page_accessed<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">struct</span> page <span style="color: #000040;">*</span>page<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>PageActive<span style="color: #008000;">&#40;</span>page<span style="color: #008000;">&#41;</span> <span style="color: #000040;">&amp;</span>amp<span style="color: #008080;">;</span><span style="color: #000040;">&amp;</span>amp<span style="color: #008080;">;</span> <span style="color: #000040;">!</span>PageUnevictable<span style="color: #008000;">&#40;</span>page<span style="color: #008000;">&#41;</span> <span style="color: #000040;">&amp;</span>amp<span style="color: #008080;">;</span><span style="color: #000040;">&amp;</span>amp<span style="color: #008080;">;</span>
                        PageReferenced<span style="color: #008000;">&#40;</span>page<span style="color: #008000;">&#41;</span> <span style="color: #000040;">&amp;</span>amp<span style="color: #008080;">;</span><span style="color: #000040;">&amp;</span>amp<span style="color: #008080;">;</span> PageLRU<span style="color: #008000;">&#40;</span>page<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
                activate_page<span style="color: #008000;">&#40;</span>page<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
                ClearPageReferenced<span style="color: #008000;">&#40;</span>page<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #008000;">&#125;</span> <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>PageReferenced<span style="color: #008000;">&#40;</span>page<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
                SetPageReferenced<span style="color: #008000;">&#40;</span>page<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>参考文章：<br />
<a href="http://lsec.cc.ac.cn/~tengfei/doc/ldd3/">http://lsec.cc.ac.cn/~tengfei/doc/ldd3/</a><br />
<a href="http://memorymyann.javaeye.com/blog/193061">http://memorymyann.javaeye.com/blog/193061</a><br />
<a href="http://www.cublog.cn/u/20047/showart.php?id=121850">http://www.cublog.cn/u/20047/showart.php?id=121850</a><br />
<a href="http://blog.chinaunix.net/u2/74194/showart_1089736.html">http://blog.chinaunix.net/u2/74194/showart_1089736.html</a><br />
关于内存管理，Linux有一个网页：<a href="http://linux-mm.org/">http://linux-mm.org/</a></p><h2  class="related_post_title">类似的文章</h2><ul class="related_post"><li>2009年12月14日 -- <a href="http://www.penglixun.com/tech/system/buffer_and_cache_diff.html" title="Buffer与Cache">Buffer与Cache</a> (1)</li><li>2009年10月20日 -- <a href="http://www.penglixun.com/tech/system/manual_free_linux_memory.html" title="[转]手工释放Linux内存——/proc/sys/vm/drop_caches ">[转]手工释放Linux内存——/proc/sys/vm/drop_caches </a> (0)</li><li>2012年01月23日 -- <a href="http://www.penglixun.com/tech/database/case_about_innodb_faster_than_oracle.html" title="一个InnoDB性能超过Oracle的调优Case">一个InnoDB性能超过Oracle的调优Case</a> (1)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.penglixun.com/tech/system/linux_cache_discovery.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>数据库算法与数据结构——Cache&amp;Buffer&amp;Lock</title>
		<link>http://www.penglixun.com/tech/database/database_algorithm_and_data_structure_cache_buffer_lock.html</link>
		<comments>http://www.penglixun.com/tech/database/database_algorithm_and_data_structure_cache_buffer_lock.html#comments</comments>
		<pubDate>Tue, 22 Jun 2010 14:40:06 +0000</pubDate>
		<dc:creator>P.Linux</dc:creator>
				<category><![CDATA[数据库]]></category>
		<category><![CDATA[Buffer]]></category>
		<category><![CDATA[Cache]]></category>
		<category><![CDATA[Lock]]></category>
		<category><![CDATA[数据结构]]></category>
		<category><![CDATA[算法]]></category>

		<guid isPermaLink="false">http://www.penglixun.com/?p=1137</guid>
		<description><![CDATA[本文内容遵从CC版权协议, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明网址: http://www.penglixun.com/tech/database/database_algorithm_and_data_structure_cache_buffer_lock.html Database.Ca... ]]></description>
			<content:encoded><![CDATA[<p><span style="color: #888888;">本文内容遵从<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank">CC版权协议</a>, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明</br>网址: http://www.penglixun.com/tech/database/database_algorithm_and_data_structure_cache_buffer_lock.html </p>
<p></span>
<div style="width:425px" id="__ss_4574492"><strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/plinux/databasecachebufferlock" title="Database.Cache&amp;Buffer&amp;Lock">Database.Cache&amp;Buffer&amp;Lock</a></strong><object id="__sse4574492" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=03-cachebufferlock-100622092931-phpapp02&#038;stripped_title=databasecachebufferlock" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed name="__sse4574492" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=03-cachebufferlock-100622092931-phpapp02&#038;stripped_title=databasecachebufferlock" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object>
<div style="padding:5px 0 12px">View more <a href="http://www.slideshare.net/">presentations</a> from <a href="http://www.slideshare.net/plinux">Lixun Peng</a>.</div>
</div><h2  class="related_post_title">类似的文章</h2><ul class="related_post"><li>2009年12月14日 -- <a href="http://www.penglixun.com/tech/system/buffer_and_cache_diff.html" title="Buffer与Cache">Buffer与Cache</a> (1)</li><li>2009年10月29日 -- <a href="http://www.penglixun.com/tech/system/the_diffrents_of_page_cache_and_buffer_cache.html" title="Page Cache和Buffer Cache的区别">Page Cache和Buffer Cache的区别</a> (0)</li><li>2010年10月30日 -- <a href="http://www.penglixun.com/tech/database/static_compile_mysql_with_tcmalloc.html" title="静态编译TCMalloc到MySQL">静态编译TCMalloc到MySQL</a> (6)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.penglixun.com/tech/database/database_algorithm_and_data_structure_cache_buffer_lock.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Buffer与Cache</title>
		<link>http://www.penglixun.com/tech/system/buffer_and_cache_diff.html</link>
		<comments>http://www.penglixun.com/tech/system/buffer_and_cache_diff.html#comments</comments>
		<pubDate>Mon, 14 Dec 2009 09:17:54 +0000</pubDate>
		<dc:creator>P.Linux</dc:creator>
				<category><![CDATA[操作系统]]></category>
		<category><![CDATA[Buffer]]></category>
		<category><![CDATA[Cache]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.penglixun.com/?p=638</guid>
		<description><![CDATA[本文内容遵从CC版权协议, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明网址: http://www.penglixun.com/tech/system/buffer_and_cache_diff.html 今天Twitter上关于Buffer和Cache讨论得... ]]></description>
			<content:encoded><![CDATA[<p><span style="color: #888888;">本文内容遵从<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank">CC版权协议</a>, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明</br>网址: http://www.penglixun.com/tech/system/buffer_and_cache_diff.html </p>
<p></span>今天Twitter上关于Buffer和Cache讨论得蛮火的，被各种说话一搅和，有点乱了，就干脆整理一下。<br />
首先从翻译上，Buffer应该翻译为“缓冲”，Cache应该翻译为“缓存”，两个完全不是一个东西。<br />
在硬件这一层看，Buffer应该为内存，Cache为CPU集成的告诉缓存。<br />
Buffer为了让不同速度的设备能够同步，建立的一个缓冲区域，写进Buffer的数据是为了从中拿出写入其他设备。<br />
Cache是为了提高读取速度，将经常或马上需要的数据预读到缓存中，写进Cache的数据是为了其他设备从中去读取。<br />
从软件这一层来说，Buffer是块设备的缓冲，Cache是文件系统的缓存。以Linux为例，<br />
Buffer(Buffer Cache)以块形式缓冲了块设备的操作，定时或手动的同步到硬盘，它是为了缓冲写操作然后一次性将很多改动写入硬盘，避免频繁写硬盘，提高写入效率。<br />
Cache(Page Cache)以页面形式缓存了文件系统的文件，给需要使用的程序读取，它是为了给读操作提供缓冲，避免频繁读硬盘，提高读取效率。<br />
总而言之，Buffer里面的东西是为了写到别处去，Cache里面的东西是为了给别处读。<br />
我的理解就是这样，欢迎大牛拍砖～</p><h2  class="related_post_title">类似的文章</h2><ul class="related_post"><li>2010年08月27日 -- <a href="http://www.penglixun.com/tech/system/linux_cache_discovery.html" title="Linux Cache 机制探究">Linux Cache 机制探究</a> (1)</li><li>2010年06月22日 -- <a href="http://www.penglixun.com/tech/database/database_algorithm_and_data_structure_cache_buffer_lock.html" title="数据库算法与数据结构——Cache&#038;Buffer&#038;Lock">数据库算法与数据结构——Cache&#038;Buffer&#038;Lock</a> (1)</li><li>2009年10月29日 -- <a href="http://www.penglixun.com/tech/system/the_diffrents_of_page_cache_and_buffer_cache.html" title="Page Cache和Buffer Cache的区别">Page Cache和Buffer Cache的区别</a> (0)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.penglixun.com/tech/system/buffer_and_cache_diff.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Page Cache和Buffer Cache的区别</title>
		<link>http://www.penglixun.com/tech/system/the_diffrents_of_page_cache_and_buffer_cache.html</link>
		<comments>http://www.penglixun.com/tech/system/the_diffrents_of_page_cache_and_buffer_cache.html#comments</comments>
		<pubDate>Thu, 29 Oct 2009 04:45:29 +0000</pubDate>
		<dc:creator>P.Linux</dc:creator>
				<category><![CDATA[操作系统]]></category>
		<category><![CDATA[Buffer]]></category>
		<category><![CDATA[Cache]]></category>
		<category><![CDATA[Page]]></category>

		<guid isPermaLink="false">http://www.penglixun.com/PLX/Blog/?p=506</guid>
		<description><![CDATA[本文内容遵从CC版权协议, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明网址: http://www.penglixun.com/tech/system/the_diffrents_of_page_cache_and_buffer_cache.html 在监控中开始对着... ]]></description>
			<content:encoded><![CDATA[<p><span style="color: #888888;">本文内容遵从<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank">CC版权协议</a>, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明</br>网址: http://www.penglixun.com/tech/system/the_diffrents_of_page_cache_and_buffer_cache.html </p>
<p></span>在监控中开始对着两个Cache有点搞不清楚，后来查了下，弄清楚了它们的区别，都是Cache但完全不是缓存一种东西，很好区分。</p>
<p>磁盘的操作有逻辑级（文件系统）和物理级（磁盘块），这两种Cache就是分别缓存逻辑和物理级数据的。<br />
假设我们通过文件系统操作文件，那么文件将被缓存到Page Cache，如果需要刷新文件的时候，Page Cache将交给Buffer Cache去完成，因为Buffer Cache就是缓存磁盘块的。<br />
也就是说，直接去操作文件，那就是Page Cache区缓存，用dd等命令直接操作磁盘块，就是Buffer Cache缓存的东西。</p><h2  class="related_post_title">类似的文章</h2><ul class="related_post"><li>2010年06月22日 -- <a href="http://www.penglixun.com/tech/database/database_algorithm_and_data_structure_cache_buffer_lock.html" title="数据库算法与数据结构——Cache&#038;Buffer&#038;Lock">数据库算法与数据结构——Cache&#038;Buffer&#038;Lock</a> (1)</li><li>2009年12月14日 -- <a href="http://www.penglixun.com/tech/system/buffer_and_cache_diff.html" title="Buffer与Cache">Buffer与Cache</a> (1)</li><li>2010年08月27日 -- <a href="http://www.penglixun.com/tech/system/linux_cache_discovery.html" title="Linux Cache 机制探究">Linux Cache 机制探究</a> (1)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.penglixun.com/tech/system/the_diffrents_of_page_cache_and_buffer_cache.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[转]手工释放Linux内存——/proc/sys/vm/drop_caches</title>
		<link>http://www.penglixun.com/tech/system/manual_free_linux_memory.html</link>
		<comments>http://www.penglixun.com/tech/system/manual_free_linux_memory.html#comments</comments>
		<pubDate>Tue, 20 Oct 2009 04:56:25 +0000</pubDate>
		<dc:creator>P.Linux</dc:creator>
				<category><![CDATA[操作系统]]></category>
		<category><![CDATA[Cache]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[内存]]></category>

		<guid isPermaLink="false">http://www.penglixun.com/PLX/Blog/?p=433</guid>
		<description><![CDATA[本文内容遵从CC版权协议, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明网址: http://www.penglixun.com/tech/system/manual_free_linux_memory.html 原文地址：http://www.linuxfly.org/post/... ]]></description>
			<content:encoded><![CDATA[<p><span style="color: #888888;">本文内容遵从<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank">CC版权协议</a>, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明</br>网址: http://www.penglixun.com/tech/system/manual_free_linux_memory.html </p>
<p></span>原文地址：http://www.linuxfly.org/post/320/</p>
<p>总有很多朋友对于Linux的内存管理有疑问，之前一篇<a href="http://www.linuxfly.org/post/114/" target="_blank">[转]理解Linux的性能</a>日志似乎也没能清除大家的疑虑。而在新版核心中，似乎对这个问题提供了新的解决方法，特转出来给大家参考一下。最后，还附上我对这方法的意见，欢迎各位一同讨论。</p>
<p>当在Linux下频繁存取文件后，物理内存会很快被用光，当程序结束后，内存不会被正常释放，而是一直作为caching。这个问题，貌似有不少人在问，不过都没有看到有什么很好解决的办法。那么我来谈谈这个问题。<br />
<span id="more-433"></span><br />
<strong><span style="color: #4169e1;">一、通常情况</span></strong><br />
先来说说free命令：</p>
<div>
<div>引用</div>
<div>[root@server ~]# free -m<br />
total used free shared buffers cached<br />
Mem: 249 163 86 0 10 94<br />
-/+ buffers/cache: 58 191<br />
Swap: 511 0 511</div>
</div>
<p>其中：</p>
<div>
<div>引用</div>
<div>total 内存总数<br />
used 已经使用的内存数<br />
free 空闲的内存数<br />
shared 多个进程共享的内存总额<br />
buffers Buffer Cache和cached Page Cache 磁盘缓存的大小<br />
-buffers/cache 的内存数:used &#8211; buffers &#8211; cached<br />
+buffers/cache 的内存数:free + buffers + cached</div>
</div>
<p>可用的<span style="color: #ff0000;">memory=free memory+buffers+cached</span>。</p>
<p>有了这个基础后，可以得知，我现在used为163MB，free为86MB，buffer和cached分别为10MB，94MB。<br />
那么我们来看看,如果我执行复制文件,内存会发生什么变化.</p>
<div>
<div>引用</div>
<div>[root@server ~]# cp -r /etc ~/test/<br />
[root@server ~]# free -m<br />
total used free shared buffers cached<br />
Mem: 249 244 4 0 8 174<br />
-/+ buffers/cache: 62 187<br />
Swap: 511 0 511</div>
</div>
<p>在我命令执行结束后，used为244MB，free为4MB，buffers为8MB，cached为174MB，天呐，都被cached吃掉了。别紧张，这是为了提高文件读取效率的做法。</p>
<p>为 了提高磁盘存取效率，Linux做了一些精心的设计，除了对dentry进行缓存（用于VFS，加速文件路径名到inode的转换），还采取了两种主要 Cache方式：Buffer Cache和Page Cache。前者针对磁盘块的读写，后者针对文件inode的读写。这些Cache有效缩短了 I/O系统调用（比如read，write，getdents）的时间。</p>
<p>那么有人说过段时间，linux会自动释放掉所用的内存。等待一段时间后，我们使用free再来试试，看看是否有释放？</p>
<div>
<div>引用</div>
<div>[root@server test]# free -m<br />
total used free shared buffers cached<br />
Mem: 249 244 5 0 8 174<br />
-/+ buffers/cache: 61 188<br />
Swap: 511 0 511</div>
</div>
<p>似乎没有任何变化。（实际情况下，内存的管理还与Swap有关）</p>
<p>那么我能否手动释放掉这些内存呢？回答是可以的！</p>
<p><strong><span style="color: #4169e1;">二、手动释放缓存</span></strong><br />
/proc 是一个虚拟文件系统，我们可以通过对它的读写操作做为与kernel实体间进行通信的一种手段。也就是说可以通过修改/proc中的文件，来对当前 kernel的行为做出调整。那么我们可以通过调整/proc/sys/vm/drop_caches来释放内存。操作如下：</p>
<div>
<div>引用</div>
<div>[root@server test]# cat /proc/sys/vm/drop_caches<br />
0</div>
</div>
<p>首先，/proc/sys/vm/drop_caches的值，默认为0。</p>
<div>
<div>引用</div>
<div>[root@server test]# sync</div>
</div>
<p>手动执行sync命令（描述：sync 命令运行 sync 子例程。如果必须停止系统，则运行sync 命令以确保文件系统的完整性。sync 命令将所有未写的系统缓冲区写到磁盘中，包含已修改的 i-node、已延迟的块 I/O 和读写映射文件）</p>
<div>
<div>引用</div>
<div>[root@server test]# echo 3 &gt; /proc/sys/vm/drop_caches<br />
[root@server test]# cat /proc/sys/vm/drop_caches<br />
3</div>
</div>
<p>将/proc/sys/vm/drop_caches值设为3</p>
<div>
<div>引用</div>
<div>[root@server test]# free -m<br />
total used free shared buffers cached<br />
Mem: 249 66 182 0 0 11<br />
-/+ buffers/cache: 55 194<br />
Swap: 511 0 511</div>
</div>
<p>再来运行free命令，会发现现在的used为66MB，free为182MB，buffers为0MB，cached为11MB。那么有效的释放了buffer和cache。</p>
<p>◎ 有关/proc/sys/vm/drop_caches的用法在下面进行了说明</p>
<div>
<div>引用</div>
<div>/proc/sys/vm/drop_caches <span style="color: #ff0000;">(since Linux 2.6.16)</span><br />
Writing to this file causes the kernel to drop clean caches,<br />
dentries and inodes from memory, causing that memory to become<br />
free.</div>
<p>To free pagecache, use echo 1 &gt; /proc/sys/vm/drop_caches; to<br />
free dentries and inodes, use echo 2 &gt; /proc/sys/vm/drop_caches;<br />
to free pagecache, dentries and inodes, use echo 3 &gt;<br />
/proc/sys/vm/drop_caches.</p>
<p>Because this is a non-destructive operation and dirty objects<br />
are not freeable, the user should run sync first.</p></div>
<p><strong><span style="color: #4169e1;">三、我的意见</span></strong><br />
上述文章就长期以来很多用户对Linux内存管理方面的疑问，给出了一个比较“直观”的回复，我更觉得有点像是核心开发小组的妥协。<br />
对于是否需要使用这个值，或向用户提及这个值，我是有保留意见的：</p>
<div>
<div>引用</div>
<div>1、从man可以看到，这值从2.6.16以后的核心版本才提供，也就是老版的操作系统，如红旗DC 5.0、RHEL 4.x之前的版本都没有；<br />
2、若对于系统内存是否够用的观察，我还是原意去看swap的使用率和si/so两个值的大小；</div>
</div>
<p>用户常见的疑问是，为什么free这么小，是否关闭应用后内存没有释放？<br />
但实际上，我们都知道这是因为Linux对内存的管理与Windows不同，free小并不是说内存不够用了，应该看的是free的第二行最后一个值：</p>
<div>
<div>引用</div>
<div>-/+ buffers/cache: 58 <span style="color: #ff0000;">191</span></div>
</div>
<p>这才是系统可用的内存大小。<br />
实际项目中告诉我们，如果因为是应用有像内存泄露、溢出的问题，从swap的使用情况是可以比较快速可以判断的，但free上面反而比较难查看。<br />
相反，如果在这个时候，我们告诉用户，修改系统的一个值，“可以”释放内存，free就大了。用户会怎么想？不会觉得操作系统“有问题”吗？<br />
所以说，我觉得既然核心是可以快速清空buffer或cache，也不难做到（这从上面的操作中可以明显看到），但核心并没有这样做（默认值是0），我们就不应该随便去改变它。<br />
一般情况下，应用在系统上稳定运行了，free值也会保持在一个稳定值的，虽然看上去可能比较小。<br />
当发生内存不足、应用获取不到可用内存、OOM错误等问题时，<span style="color: #ff0000;">还是更应该去分析应用方面的原因</span>，如用户量太大导致内存不足、发生应用内存溢出等情况，否则，清空buffer，强制腾出free的大小，<span style="color: #ff0000;">可能只是把问题给暂时屏蔽了</span>。</p>
<p>我 觉得，排除内存不足的情况外，除非是在软件开发阶段，需要临时清掉buffer，以判断应用的内存使用情况；或应用已经不再提供支持，即使应用对内存的时 候确实有问题，而且无法避免的情况下，才考虑定时清空buffer。（可惜，这样的应用通常都是运行在老的操作系统版本上，上面的操作也解决不了）。 O(∩_∩)O哈哈~</p><h2  class="related_post_title">类似的文章</h2><ul class="related_post"><li>2010年08月27日 -- <a href="http://www.penglixun.com/tech/system/linux_cache_discovery.html" title="Linux Cache 机制探究">Linux Cache 机制探究</a> (1)</li><li>2009年12月14日 -- <a href="http://www.penglixun.com/tech/system/buffer_and_cache_diff.html" title="Buffer与Cache">Buffer与Cache</a> (1)</li><li>2012年01月23日 -- <a href="http://www.penglixun.com/tech/database/case_about_innodb_faster_than_oracle.html" title="一个InnoDB性能超过Oracle的调优Case">一个InnoDB性能超过Oracle的调优Case</a> (1)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.penglixun.com/tech/system/manual_free_linux_memory.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

