admin管理员组

文章数量:1598868

  • 1. 相关概念
    • 页缓存和块缓存概念
      • 页缓存(page cache)
      • 块缓存(buffer cache)
    • 缓存机制的利弊
      • 写缓存
  • 2. 数据同步(刷缓存)
    • flush内核线程
    • 可调参数
    • flush/sync/fsync系列API及命令介绍
      • sync 系统命令
      • sync()
      • fsync()
      • fdatasync()
      • open()之O_SYNC/O_DSYNC选项
      • msync()
      • fflush()
  • 3. 页面回收(page reclaim)
      • 缓存回收策略
      • 双LRU
      • 系统触发页面回收
    • 手动清理缓存
    • 应用
  • 4. 相关工具
    • 查看缓存中的文件
    • free命令
      • free命令各个指标含义
      • /proc/meminfo
  • 5. 参考资料

1. 相关概念

  • 页的概念 : 内核把物理页作为内存管理的基本单位. 通常32位系统页大小为4KB, 64位是8KB
  • 如何查看当前系统页大小:
# getconf PAGESIZE
4096

页缓存和块缓存概念

  • 内核为块设备提供两种通用缓存方案:

页缓存(page cache)

  • 缓存的是内存页面,缓存中的页来自于对正规文件、块设备文件和内存映射文件的读写。也就是说页缓存中包含最近被访问的文件数据块。我们通常所说的系统cache主要就是指页缓存。
  • 在进行一个读操作前(如read),内核会检查数据是否已经在页缓存中,如果在,就可以从内存中快速返回所需的页,而不是从磁盘上读取。

块缓存(buffer cache)

  • 以块(块设备的block)为操作单位。在进行IO操作时,存取的单位是单个块设备的各个块,而不是整个内存页。
    • 早期版本写文件,先经过页缓存,刷盘时再同步到块缓存最后再落盘. 即:如果用dd写裸盘,则不经过页缓存。
    • 从linux内核2.4开始二者统一起来了,缓存用页映射块,块缓存实际上就在页缓存中了。

缓存机制的利弊

  • 好处:
    • 提高性能(且对应用程序透明)
    • 写缓存,可将零碎IO聚齐起来
    • ……
  • 负面影响:
    • 物理内存通常远小于块设备,必须仔细挑选缓存哪些数据
    • 缓存在一定程度上会影响应用程序实际可用的内存容量
    • 如果系统崩溃,缓存的数据可能来不及刷回块设备(硬盘/ssd等),造成数据丢失

写缓存

  • 通常缓存有三种实现策略:
    • 不缓存(很少使用)
    • 写透缓存(write-through cache):写操作会立刻穿透缓存存到磁盘中
    • 回写(writeback):即linux采取的策略,写操作后将对应缓存页面标记为“dirty”,由回写进程周期性刷到磁盘
  • “脏页”这个词有误导性,实际上脏的是磁盘上的数据而不是缓存中的数据。准确讲应该叫“未同步的页”。

2. 数据同步(刷缓存)

  • 数据总是在物理内存中操作,随后在适当的时机点写回(或刷出)到磁盘,以持久化保存修改
  • 几种不同的刷出数据机制:
    • 周期性内核线程将扫描脏页链表,并根据页面变脏的时间,选择一些页会写
    • 如系统中脏页太多,内核将触发进一步机制对脏页与后备存储器进行同步,直到脏页降低到一个可接受的程度
    • 内核各个组件要求数据必须在特定事件发生时同步,例如重新装在文件系统

flush内核线程

  • 上述刷新机制前2条由flusher线程实现。内核中每个磁盘设备对应一个flush线程,通过块设备号区分,[flush-8:48]
  • 如何查看块设备号,可参考 http://www.lenky.info/archives/2012/02/1141 ,块设备一般都是8开头
  • 历史上(2.6以前)还有bdflush,kupdated和pdflush等.其中pdflush线程数量不固定,而现在flush线程通常和磁盘一一对应,这样有助于在某个磁盘拥塞情况下避免所有IO拥塞,并简化处理逻辑。
  • 关于线程数量和磁盘对应关系,以及周期性创建flush线程这部分逻辑,2.6前后内核版本有一定改动。查阅了几本内核的参考书,这部分介绍都不完全相同。
  • 有些资料中提到,如1秒内没有空闲的flusher线程可用,内核将创建新的线程;如某个线程空闲超过1秒,将被销毁.flush线程数量一般被限制在2-8个

可调参数

  • 可通过/proc/sys/vm/目录下的内核参数控制脏页回写行为:
参数 描述
dirty_background_ratio 占全部内存的百分比,当脏页数量超过该比例时,pdflush机制开始(在后台)回写脏页.此时对write调用没有影响
dirty_background_bytes 同上,以字节描述。与dirty_background_ratio不可同时生效
dirty_writeback_centisecs pdflush线程运行的频率,单位1/100秒。默认500,即pdflush两次调用间隔是5秒。早期版本该参数可能是 dirty_writeback_interval
dirty_ratio 脏页占全部内存的比例,超出该阈值时脏页开始刷出。此时新的IO请求可能会被阻塞直到脏页刷完
dirty_bytes 同上,以字节描述。与dirty_ratio不可同时生效
dirty_expire_centisecs 以百分之一秒为单位,默认3000.即超时30秒的脏页将会被pdflush线程写出,或者说一个脏页在回写之前,保持脏页状态最长可达30秒

- 在写密集的系统中,通常可配置dirty_ratio较高(如60)以增大可用缓存、dirty_background_ratio较低(如20)以遍在后台及时将数据刷入磁盘。同时保持dirty_writeback_centisecs较高(如5秒)
- 内核源码中好像有一个检查,dirty_background_ratio不能大于dirty_ratio的1/2,否则内核会自动调整为后者的1/2.参见内核代码global_dirty_limits() 函数:

void global_dirty_limits(unsigned long *pbackground, unsigned long *pdirty)
{
    ……
    if (background >= dirty)
        background = dirty / 2;
    ……
}
  • 效果示意图:

flush/sync/fsync系列API及命令介绍

sync 系统命令

  • Force changed blocks to disk, update the super block.

sync()

  • 允许进程把所有脏缓冲区刷新到磁盘.网上有说:
    sync函数只是将所有修改过的块缓冲区排入写队列,然后就返回,它并不等待实际写磁盘操作结束。通常称为update的系统守护进程会周期性地(一般每隔30秒)调用sync函数
  • 但从代码看,do_sync()会调用2次sync_inodes()sync_filesystems(),第一次传参数0,表示异步执行;第二次传参数1,表示同步执行.

fsync()

  • 允许进程把属于特定打开文件的所有块刷新到磁盘. 它等待写磁盘操作结束,然后返回(The call blocks until the device reports that the transfer has completed. It also flushes metadata information associated with the file)。fsync可用于数据库这样的应用程序.
  • 但正由于它会将元数据也刷回磁盘,所以相比fdatasync()它多了一次IO操作。对于机械盘来说,这个耗时通常是毫秒(平均10ms左右)级别的!!
  • fync() 不保证其所在目录也被刷到磁盘,因此针对其所在目录显示调用一次 fsync() 是有必要的

fdatasync()

  • 与fsync()类似,但不刷新文件的索引节点块(inode等)
  • 诸如对文件access time等的修改,不会被fsync刷回磁盘; 但对文件大小的改变,由于会影响到后面的读数据,会被刷回磁盘。
  • 通过这种机制减少不必要的磁盘IO,提高性能
  • 网上提到,数据库系统中为了使用fdatasync来记录WAL日志的方法(因为日志都是追加写,文件长度会不断变化,不太适合直接用fdatasync):(https://

本文标签: 史上最全缓存相关知识Linux