最近遇到了一块儿跟磁盘相关的线上故障,借此总结一下以前不太了解的Linux磁盘缓存相关的知识。node
总的来讲磁盘缓存出现的缘由大概有两个:第一是访问磁盘的速度远慢于访问内存的速度,经过在内存中缓存磁盘内容能够提升访问速度;第二是根据程序的局部性原理,数据一旦被访问过,就颇有可能在短期内再次被访问,因此在内存中缓存磁盘内容能够提升程序运行速度。算法
程序局部性原理:程序在执行时呈现出局部性规律,即在一段时间内,整个程序的执行仅限于程序中的某一部分。相应地,执行所访问的存储空间也局限于某个内存区域,具体来讲,局部性一般有两种形式:时间局部性和空间局部性。缓存
时间局部性:被引用过一次的存储器位置在将来会被屡次引用。markdown
空间局部性:若是一个存储器的位置被引用,那么未来他附近的位置也会被引用。函数
Linux系统中为了减小对磁盘的IO操做,会将打开的磁盘内容进行缓存,而缓存的地方则是物理内存,进而将对磁盘的访问转换成对内存的访问,有效提升程序的速度。Linux的缓存方式是利用物理内存缓存磁盘上的内容,称为页缓存(page cache)。spa
页缓存是由内存中的物理页面组成的,其内容对应磁盘上的物理块。页缓存的大小会根据系统的内存空闲大小进行动态调整,它能够经过占用内存以扩张大小,也能够自我收缩以缓解内存使用压力。操作系统
在虚拟内存机制出现之前,操做系统使用块缓存系列,可是在虚拟内存出现之后,操做系统管理IO的粒度更大,所以采用了页缓存机制,页缓存是基于页的、面向文件的缓存机制。线程
Linux系统在读取文件时,会优先从页缓存中读取文件内容,若是页缓存不存在,系统会先从磁盘中读取文件内容更新到页缓存中,而后再从页缓存中读取文件内容并返回。大体过程以下:code
因此说,全部的文件内容的读取,不管最初有没有命中页缓存,最终都是直接来源于页缓存。orm
由于页缓存的存在,当一个进程调用write时,对文件的更新仅仅是被写到了文件的页缓存中,让后将对应的页标记为dirty,整个过程就结束了。Linux内核会在周期性地将脏页写回到磁盘,而后清理掉dirty标识。
因为写操做只会把变动写入页缓存,所以进程并不会所以为阻塞直到磁盘IO发生,若是此时计算机崩溃,写操做的变动可能并无发生在磁盘上。因此对于一些要求比较严格的写操做,好比数据系统,就须要主动调用fsync等操做及时将变动同步到磁盘上。读操做则不一样,read一般会阻塞直到进程读取到数据,而为了减小读操做的这种延迟,Linux系统仍是用了“预读”的技术,即从磁盘中读取数据时,内核将会多读取一些页到页缓存中。
页缓存的回写是由内核中的单独的线程来完成的,回写线程会在如下3种状况下进行回写:
回写线程的实现
名称 | 版本 | 说明 |
---|---|---|
bdflush | 2.6版本之前 | bdflush 内核线程在后台运行,系统中只有一个 bdflush 线程,当内存消耗到特定阀值如下时,bdflush 线程被唤醒。kupdated 周期性的运行,写回脏页。 可是整个系统仅仅只有一个 bdflush 线程,当系统回写任务较重时,bdflush 线程可能会阻塞在某个磁盘的I/O上,致使其余磁盘的I/O回写操做不能及时执行。 |
pdflush | 2.6版本引入 | pdflush 线程数目是动态的,取决于系统的I/O负载。它是面向系统中全部磁盘的全局任务的。 可是因为 pdflush 是面向全部磁盘的,因此有可能出现多个 pdflush 线程所有阻塞在某个拥塞的磁盘上,一样致使其余磁盘的I/O回写不能及时执行。 |
flusher线程 | 2.6.32版本之后引入 | flusher 线程的数目不是惟一的,同时flusher线程不是面向全部磁盘的,而是每一个flusher线程对应一个磁盘 |
Linux中页缓存的替换逻辑是一个修改过的LRU实现,也称为双链策略。和之前不一样,Linux维护的再也不是一个LRU链表,而是维护两个链表:活跃链表和非活跃链表。处于活跃链表上的页面被认为是“热”的且不会被换出,而在非活跃链表上的页面则是能够被换出的。在活跃链表中的页面必须在其被访问时就处于非活跃链表中。两个链表都被伪LRU规则维护:页面从尾部加入,从头部移除,如同队列。两个链表须要维持平衡–若是活跃链表变得过多而超过了非活跃链表,那么活跃链表的头页面将被从新移回到非活跃链表中,一遍能再被回收。双链表策略解决了传统LRU算法中对仅一次访问的窘境。并且也更加简单的实现了伪LRU语义。这种双链表方式也称做LRU/2。更广泛的是n个链表,故称LRU/n。
在此次遇到的线上故障中,根本缘由在于在业务逻辑中使用了临时文件作缓存,一个临时文件建立后若是在短期内删除,这时候对这个文件的操做都是在页缓存内进行,不会实际回写到磁盘。当程序出现问题响应变慢时,临时文件存活时间变长,就可能会使其被回写到磁盘上,致使磁盘压力过大,进而影响整个系统。